summaryrefslogtreecommitdiff
path: root/test/escape2.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-04-06 18:17:20 +0300
committerDmitry Vyukov <dvyukov@google.com>2015-04-09 09:56:27 +0000
commit878a86a129ceb771f677c8391a9f5c891e5be7c3 (patch)
tree48798aa4567d214e75a742a136a86de6f6f86e83 /test/escape2.go
parent6e774faed773afa1ff7345e2c2d4367e9510388d (diff)
downloadgo-git-878a86a129ceb771f677c8391a9f5c891e5be7c3.tar.gz
cmd/gc: fix escape analysis of closures
Fixes #10353 See test/escape2.go:issue10353. Previously new(int) did not escape to heap, and so heap-allcated closure was referencing a stack var. This breaks the invariant that heap must not contain pointers to stack. Look at the following program: package main func main() { foo(new(int)) bar(new(int)) } func foo(x *int) func() { return func() { println(*x) } } // Models what foo effectively does. func bar(x *int) *C { return &C{x} } type C struct { x *int } Without this patch escape analysis works as follows: $ go build -gcflags="-m -m -m -l" esc.go escflood:1: dst ~r1 scope:foo[0] escwalk: level:0 depth:0 func literal( l(9) f(1) esc(no) ld(1)) scope:foo[1] /tmp/live2.go:9: func literal escapes to heap escwalk: level:0 depth:1 x( l(8) class(PPARAM) f(1) esc(no) ld(1)) scope:foo[1] /tmp/live2.go:8: leaking param: x to result ~r1 escflood:2: dst ~r1 scope:bar[0] escwalk: level:0 depth:0 &C literal( l(15) esc(no) ld(1)) scope:bar[1] /tmp/live2.go:15: &C literal escapes to heap escwalk: level:-1 depth:1 &C literal( l(15)) scope:bar[0] escwalk: level:-1 depth:2 x( l(14) class(PPARAM) f(1) esc(no) ld(1)) scope:bar[1] /tmp/live2.go:14: leaking param: x /tmp/live2.go:5: new(int) escapes to heap /tmp/live2.go:4: main new(int) does not escape new(int) does not escape while being captured by the closure. With this patch escape analysis of foo and bar works similarly: $ go build -gcflags="-m -m -m -l" esc.go escflood:1: dst ~r1 scope:foo[0] escwalk: level:0 depth:0 &(func literal)( l(9)) scope:foo[0] escwalk: level:-1 depth:1 func literal( l(9) f(1) esc(no) ld(1)) scope:foo[1] /tmp/live2.go:9: func literal escapes to heap escwalk: level:-1 depth:2 x( l(8) class(PPARAM) f(1) esc(no) ld(1)) scope:foo[1] /tmp/live2.go:8: leaking param: x escflood:2: dst ~r1 scope:bar[0] escwalk: level:0 depth:0 &C literal( l(15) esc(no) ld(1)) scope:bar[1] /tmp/live2.go:15: &C literal escapes to heap escwalk: level:-1 depth:1 &C literal( l(15)) scope:bar[0] escwalk: level:-1 depth:2 x( l(14) class(PPARAM) f(1) esc(no) ld(1)) scope:bar[1] /tmp/live2.go:14: leaking param: x /tmp/live2.go:4: new(int) escapes to heap /tmp/live2.go:5: new(int) escapes to heap Change-Id: Ifd14b7ae3fc11820e3b5eb31eb07f35a22ed0932 Reviewed-on: https://go-review.googlesource.com/8408 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Dmitry Vyukov <dvyukov@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'test/escape2.go')
-rw-r--r--test/escape2.go22
1 files changed, 22 insertions, 0 deletions
diff --git a/test/escape2.go b/test/escape2.go
index 65dbd7a2fe..6b25e68616 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -1797,3 +1797,25 @@ func nonescapingEface(m map[interface{}]bool) bool { // ERROR "m does not escape
func nonescapingIface(m map[M]bool) bool { // ERROR "m does not escape"
return m[MV(0)] // ERROR "MV\(0\) does not escape"
}
+
+func issue10353() {
+ x := new(int) // ERROR "new\(int\) escapes to heap"
+ issue10353a(x)()
+}
+
+func issue10353a(x *int) func() { // ERROR "leaking param: x"
+ return func() { // ERROR "func literal escapes to heap"
+ println(*x)
+ }
+}
+
+func issue10353b() {
+ var f func()
+ for {
+ x := new(int) // ERROR "new\(int\) escapes to heap"
+ f = func() { // ERROR "func literal escapes to heap"
+ println(*x)
+ }
+ }
+ _ = f
+}