Skip to content

Commit b64f549

Browse files
committed
cmd/compile: ignore OXXX nodes in closure captured vars list
Added a debug flag "-d closure" to explain compilation of closures (should this be done some other way? Should we rewrite the "-m" flag to "-d escapes"?) Used this to discover that cause was an OXXX node in the captured vars list, and in turn noticed that OXXX nodes are explicitly ignored in all other processing of captured variables. Couldn't figure out a reproducer, did verify that this OXXX was not caused by an unnamed return value (which is one use of these). Verified lack of heap allocation by examining -S output. Assembly: (runtime/mgc.go:1371) PCDATA $0, $2 (runtime/mgc.go:1371) CALL "".notewakeup(SB) (runtime/mgc.go:1377) LEAQ "".gcBgMarkWorker.func1·f(SB), AX (runtime/mgc.go:1404) MOVQ AX, (SP) (runtime/mgc.go:1404) MOVQ "".autotmp_2242+88(SP), CX (runtime/mgc.go:1404) MOVQ CX, 8(SP) (runtime/mgc.go:1404) LEAQ go.string."GC worker (idle)"(SB), AX (runtime/mgc.go:1404) MOVQ AX, 16(SP) (runtime/mgc.go:1404) MOVQ $16, 24(SP) (runtime/mgc.go:1404) MOVB $20, 32(SP) (runtime/mgc.go:1404) MOVQ $0, 40(SP) (runtime/mgc.go:1404) PCDATA $0, $2 (runtime/mgc.go:1404) CALL "".gopark(SB) Added a check for compiling_runtime to ensure that this is caught in the future. Added a test to test the check. Verified that 1.5.3 did NOT reject the test case when compiled with -+ flag, so this is not a recently added bug. Cause of bug is two-part -- there was no leaking closure detection ever, and instead it relied on capture-of-variables to trigger compiling_runtime test, but closures improved in 1.5.3 so that mere capture of a value did not also capture the variable, which thus allowed closures to escape, as well as this case where the escape was spurious. In fixedbugs/issue14999.go, compare messages for f and g; 1.5.3 would reject g, but not f. 1.4 rejects both because 1.4 heap-allocates parameter x for both. Fixes #14999. Change-Id: I40bcdd27056810628e96763a44f2acddd503aee1 Reviewed-on: https://go-review.googlesource.com/21322 Run-TryBot: David Chase <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 1cb3044 commit b64f549

File tree

4 files changed

+63
-6
lines changed

4 files changed

+63
-6
lines changed

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,42 @@ func transformclosure(xfunc *Node) {
397397
lineno = lno
398398
}
399399

400+
// hasemptycvars returns true iff closure func_ has an
401+
// empty list of captured vars. OXXX nodes don't count.
402+
func hasemptycvars(func_ *Node) bool {
403+
for _, v := range func_.Func.Cvars.Slice() {
404+
if v.Op == OXXX {
405+
continue
406+
}
407+
return false
408+
}
409+
return true
410+
}
411+
412+
// closuredebugruntimecheck applies boilerplate checks for debug flags
413+
// and compiling runtime
414+
func closuredebugruntimecheck(r *Node) {
415+
if Debug_closure > 0 {
416+
if r.Esc == EscHeap {
417+
Warnl(r.Lineno, "heap closure, captured vars = %v", r.Func.Cvars)
418+
} else {
419+
Warnl(r.Lineno, "stack closure, captured vars = %v", r.Func.Cvars)
420+
}
421+
}
422+
if compiling_runtime > 0 && r.Esc == EscHeap {
423+
yyerrorl(r.Lineno, "heap-allocated closure, not allowed in runtime.")
424+
}
425+
}
426+
400427
func walkclosure(func_ *Node, init *Nodes) *Node {
401428
// If no closure vars, don't bother wrapping.
402-
if len(func_.Func.Cvars.Slice()) == 0 {
429+
if hasemptycvars(func_) {
430+
if Debug_closure > 0 {
431+
Warnl(func_.Lineno, "closure converted to global")
432+
}
403433
return func_.Func.Closure.Func.Nname
434+
} else {
435+
closuredebugruntimecheck(func_)
404436
}
405437

406438
// Create closure in the form of a composite literal.

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ var (
3131
)
3232

3333
var (
34-
Debug_append int
35-
Debug_panic int
36-
Debug_slice int
37-
Debug_wb int
34+
Debug_append int
35+
Debug_closure int
36+
Debug_panic int
37+
Debug_slice int
38+
Debug_wb int
3839
)
3940

4041
// Debug arguments.
@@ -46,6 +47,7 @@ var debugtab = []struct {
4647
val *int
4748
}{
4849
{"append", &Debug_append}, // print information about append compilation
50+
{"closure", &Debug_closure}, // print information about closure compilation
4951
{"disablenil", &Disable_checknil}, // disable nil checks
5052
{"gcprog", &Debug_gcprog}, // print dump of GC programs
5153
{"nil", &Debug_checknil}, // print information about nil checks

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,17 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
479479
break
480480

481481
case OCLOSURE:
482-
if len(r.Func.Cvars.Slice()) == 0 {
482+
if hasemptycvars(r) {
483+
if Debug_closure > 0 {
484+
Warnl(r.Lineno, "closure converted to global")
485+
}
483486
// Closures with no captured variables are globals,
484487
// so the assignment can be done at link time.
485488
n := *l
486489
gdata(&n, r.Func.Closure.Func.Nname, Widthptr)
487490
return true
491+
} else {
492+
closuredebugruntimecheck(r)
488493
}
489494
}
490495

test/fixedbugs/issue14999.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// errorcheck -+
2+
3+
// Copyright 2016 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+
package p
8+
9+
func f(x int) func(int) int {
10+
return func(y int) int { return x + y } // ERROR "heap-allocated closure, not allowed in runtime."
11+
}
12+
13+
func g(x int) func(int) int { // ERROR "x escapes to heap, not allowed in runtime."
14+
return func(y int) int { // ERROR "heap-allocated closure, not allowed in runtime."
15+
x += y
16+
return x + y
17+
}
18+
}

0 commit comments

Comments
 (0)