Skip to content

Commit 483e298

Browse files
huguesbrandall77
authored andcommitted
cmd/compile: fix reassignment check
CL 65071 enabled inlining for local closures with no captures. To determine safety of inlining a call sites, we check whether the variable holding the closure has any assignments after its original definition. Unfortunately, that check did not catch OAS2MAPR and OAS2DOTTYPE, leading to incorrect inlining when a variable holding a closure was subsequently reassigned through a type conversion or a 2-valued map access. There was another more subtle issue wherein reassignment check would always return a false positive for closure calls inside other closures. This was caused by the Name.Curfn field of local variables pointing to the OCLOSURE node instead of the corresponding ODCLFUNC, which resulted in reassigned walking an empty Nbody and thus never seeing any reassignments. This CL fixes these oversights and adds many more tests for closure inlining which ensure not only that inlining triggers but also the correctness of the resulting code. Updates #15561 Change-Id: I74bdae849c4ecfa328546d6d62b512e8d54d04ce Reviewed-on: https://go-review.googlesource.com/75770 Reviewed-by: Hugues Bruant <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent d593f85 commit 483e298

File tree

3 files changed

+194
-2
lines changed

3 files changed

+194
-2
lines changed

src/cmd/compile/internal/gc/inl.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -661,8 +661,17 @@ func reassigned(n *Node) (bool, *Node) {
661661
if n.Name.Curfn == nil {
662662
return true, nil
663663
}
664+
f := n.Name.Curfn
665+
// There just might be a good reason for this although this can be pretty surprising:
666+
// local variables inside a closure have Curfn pointing to the OCLOSURE node instead
667+
// of the corresponding ODCLFUNC.
668+
// We need to walk the function body to check for reassignments so we follow the
669+
// linkage to the ODCLFUNC node as that is where body is held.
670+
if f.Op == OCLOSURE {
671+
f = f.Func.Closure
672+
}
664673
v := reassignVisitor{name: n}
665-
a := v.visitList(n.Name.Curfn.Nbody)
674+
a := v.visitList(f.Nbody)
666675
return a != nil, a
667676
}
668677

@@ -680,7 +689,7 @@ func (v *reassignVisitor) visit(n *Node) *Node {
680689
return n
681690
}
682691
return nil
683-
case OAS2, OAS2FUNC:
692+
case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE:
684693
for _, p := range n.List.Slice() {
685694
if p == v.name && n != v.name.Name.Defn {
686695
return n

test/closure3.dir/main.go

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Check correctness of various closure corner cases that
6+
// that are expected to be inlined
7+
8+
package main
9+
10+
var ok bool
11+
var sink int
12+
13+
func main() {
14+
{
15+
if x := func() int { // ERROR "can inline main.func1"
16+
return 1
17+
}(); x != 1 { // ERROR "inlining call to main.func1"
18+
panic("x != 1")
19+
}
20+
if x := func() int { // ERROR "can inline main.func2" "func literal does not escape"
21+
return 1
22+
}; x() != 1 { // ERROR "inlining call to main.func2"
23+
panic("x() != 1")
24+
}
25+
}
26+
27+
{
28+
if y := func(x int) int { // ERROR "can inline main.func3"
29+
return x + 2
30+
}(40); y != 42 { // ERROR "inlining call to main.func3"
31+
panic("y != 42")
32+
}
33+
if y := func(x int) int { // ERROR "can inline main.func4" "func literal does not escape"
34+
return x + 2
35+
}; y(40) != 42 { // ERROR "inlining call to main.func4"
36+
panic("y(40) != 42")
37+
}
38+
}
39+
40+
{
41+
y := func(x int) int { // ERROR "can inline main.func5" "func literal does not escape"
42+
return x + 2
43+
}
44+
y = func(x int) int { // ERROR "can inline main.func6" "func literal does not escape"
45+
return x + 1
46+
}
47+
if y(40) != 41 {
48+
panic("y(40) != 41")
49+
}
50+
}
51+
52+
{
53+
func() { // ERROR "func literal does not escape"
54+
y := func(x int) int { // ERROR "can inline main.func7.1" "func literal does not escape"
55+
return x + 2
56+
}
57+
y = func(x int) int { // ERROR "can inline main.func7.2" "func literal does not escape"
58+
return x + 1
59+
}
60+
if y(40) != 41 {
61+
panic("y(40) != 41")
62+
}
63+
}()
64+
}
65+
66+
{
67+
y := func(x int) int { // ERROR "can inline main.func8" "func literal does not escape"
68+
return x + 2
69+
}
70+
y, sink = func(x int) int { // ERROR "can inline main.func9" "func literal does not escape"
71+
return x + 1
72+
}, 42
73+
if y(40) != 41 {
74+
panic("y(40) != 41")
75+
}
76+
}
77+
78+
{
79+
func() { // ERROR "func literal does not escape"
80+
y := func(x int) int { // ERROR "can inline main.func10.1" "func literal does not escape"
81+
return x + 2
82+
}
83+
y, sink = func(x int) int { // ERROR "can inline main.func10.2" "func literal does not escape"
84+
return x + 1
85+
}, 42
86+
if y(40) != 41 {
87+
panic("y(40) != 41")
88+
}
89+
}()
90+
}
91+
92+
{
93+
y := func(x int) int { // ERROR "can inline main.func11" "func literal does not escape"
94+
return x + 2
95+
}
96+
y, sink = func() (func(int)int, int) { // ERROR "func literal does not escape"
97+
return func(x int) int { // ERROR "can inline main.func12" "func literal escapes"
98+
return x + 1
99+
}, 42
100+
}()
101+
if y(40) != 41 {
102+
panic("y(40) != 41")
103+
}
104+
}
105+
106+
{
107+
func() { // ERROR "func literal does not escape"
108+
y := func(x int) int { // ERROR "can inline main.func13.1" "func literal does not escape"
109+
return x + 2
110+
}
111+
y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape"
112+
return func(x int) int { // ERROR "can inline main.func13.2" "func literal escapes"
113+
return x + 1
114+
}, 42
115+
}()
116+
if y(40) != 41 {
117+
panic("y(40) != 41")
118+
}
119+
}()
120+
}
121+
122+
{
123+
y := func(x int) int { // ERROR "can inline main.func14" "func literal does not escape"
124+
return x + 2
125+
}
126+
y, ok = map[int]func(int)int { // ERROR "does not escape"
127+
0: func (x int) int { return x + 1 }, // ERROR "can inline main.func15" "func literal escapes"
128+
}[0]
129+
if y(40) != 41 {
130+
panic("y(40) != 41")
131+
}
132+
}
133+
134+
{
135+
func() { // ERROR "func literal does not escape"
136+
y := func(x int) int { // ERROR "can inline main.func16.1" "func literal does not escape"
137+
return x + 2
138+
}
139+
y, ok = map[int]func(int) int{// ERROR "does not escape"
140+
0: func(x int) int { return x + 1 }, // ERROR "can inline main.func16.2" "func literal escapes"
141+
}[0]
142+
if y(40) != 41 {
143+
panic("y(40) != 41")
144+
}
145+
}()
146+
}
147+
148+
{
149+
y := func(x int) int { // ERROR "can inline main.func17" "func literal does not escape"
150+
return x + 2
151+
}
152+
y, ok = interface{}(func (x int) int { // ERROR "can inline main.func18" "does not escape"
153+
return x + 1
154+
}).(func(int)int)
155+
if y(40) != 41 {
156+
panic("y(40) != 41")
157+
}
158+
}
159+
160+
{
161+
func() { // ERROR "func literal does not escape"
162+
y := func(x int) int { // ERROR "can inline main.func19.1" "func literal does not escape"
163+
return x + 2
164+
}
165+
y, ok = interface{}(func(x int) int { // ERROR "can inline main.func19.2" "does not escape"
166+
return x + 1
167+
}).(func(int) int)
168+
if y(40) != 41 {
169+
panic("y(40) != 41")
170+
}
171+
}()
172+
}
173+
}

test/closure3.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// errorcheckandrundir -0 -m
2+
3+
// Copyright 2017 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Check correctness of various closure corner cases that
8+
// that are expected to be inlined
9+
10+
package ignored

0 commit comments

Comments
 (0)