src/cmd/compile/internal/gc/esc.go | 21 +++++++++++++++++++++ test/fixedbugs/issue17318.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 90ad75cbeaac5708e9ec86bbef6fd8d4fa854302..69b59646f198710e4deda142ac9e03837b267a72 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -472,8 +472,28 @@ // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); // visit the upstream of each dst, mark address nodes with // addrescapes, mark parameters unsafe + escapes := make([]uint16, len(e.dsts)) + for i, n := range e.dsts { + escapes[i] = n.Esc + } for _, n := range e.dsts { escflood(e, n) + } + for { + done := true + for i, n := range e.dsts { + if n.Esc != escapes[i] { + done = false + if Debug['m'] > 2 { + Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n) + } + escapes[i] = n.Esc + escflood(e, n) + } + } + if done { + break + } } // for all top level functions, tag the typenodes corresponding to the param nodes @@ -1796,6 +1816,7 @@ } } leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth + leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap osrcesc = src.Esc switch src.Op { diff --git a/test/fixedbugs/issue17318.go b/test/fixedbugs/issue17318.go new file mode 100644 index 0000000000000000000000000000000000000000..fe00859bd7d18228dd4b43515d6bf89d119e6a95 --- /dev/null +++ b/test/fixedbugs/issue17318.go @@ -0,0 +1,47 @@ +// errorcheck -0 -N -m -l + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The escape analyzer needs to run till its root set settles +// (this is not that often, it turns out). +// This test is likely to become stale because the leak depends +// on a spurious-escape bug -- return an interface as a named +// output parameter appears to cause the called closure to escape, +// where returning it as a regular type does not. + +package main + +import ( + "fmt" +) + +type closure func(i, j int) ent + +type ent int + +func (e ent) String() string { + return fmt.Sprintf("%d", int(e)) // ERROR "ent.String ... argument does not escape$" "int\(e\) escapes to heap$" +} + +//go:noinline +func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "leaking param: ops$" "leaking param: ops to result err level=0$" + enqueue := func(i int) fmt.Stringer { // ERROR "func literal escapes to heap$" + return ops(i, j) // ERROR "ops\(i, j\) escapes to heap$" + } + err = enqueue(4) + if err != nil { + return err + } + return // return result of enqueue, a fmt.Stringer +} + +func main() { + // 3 identical functions, to get different escape behavior. + f := func(i, j int) ent { // ERROR "func literal escapes to heap$" + return ent(i + j) + } + i := foo(f, 3).(ent) + fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "main ... argument does not escape$" +}