diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 6d6d18fdd2b2293f8b3b4296b0b18aba9c1e4429..5de6d8b3075eaf465d2dc0c047e41b7854f5e43d 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -302,6 +302,7 @@ func (l Level) guaranteedDereference() int { // heap allocation. type EscStep struct { src, dst *Node // the endpoints of this edge in the escape-to-heap chain. + where *Node // sometimes the endpoints don't match source locations; set 'where' to make that right parent *EscStep // used in flood to record path why string // explanation for this step in the escape-to-heap chain busy bool // used in prevent to snip cycles. @@ -438,11 +439,27 @@ func (e *EscState) stepAssign(step *EscStep, dst, src *Node, why string) *EscSte return nil } if step != nil { // Caller may have known better. + if step.why == "" { + step.why = why + } + if step.dst == nil { + step.dst = dst + } + if step.src == nil { + step.src = src + } return step } return &EscStep{src: src, dst: dst, why: why} } +func (e *EscState) stepAssignWhere(dst, src *Node, why string, where *Node) *EscStep { + if Debug['m'] == 0 { + return nil + } + return &EscStep{src: src, dst: dst, why: why, where: where} +} + // funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way. func funcSym(fn *Node) *Sym { if fn == nil || fn.Func.Nname == nil { @@ -667,7 +684,7 @@ func (e *EscState) esc(n *Node, parent *Node) { } n.Esc = EscHeap addrescapes(n) - e.escassignSinkNilWhy(n, n, "too large for stack") // TODO category: tooLarge + e.escassignSinkWhy(n, n, "too large for stack") // TODO category: tooLarge } e.esc(n.Left, n) @@ -718,9 +735,9 @@ func (e *EscState) esc(n *Node, parent *Node) { // dereferenced (see #12588) if n.Type.IsArray() && !(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) { - e.escassignNilWhy(n.List.Second(), n.Right, "range") + e.escassignWhyWhere(n.List.Second(), n.Right, "range", n) } else { - e.escassignDereference(n.List.Second(), n.Right, e.stepAssign(nil, n.List.Second(), n.Right, "range-deref")) + e.escassignDereference(n.List.Second(), n.Right, e.stepAssignWhere(n.List.Second(), n.Right, "range-deref", n)) } } @@ -731,7 +748,7 @@ func (e *EscState) esc(n *Node, parent *Node) { // n.Left.Right is the argument of the .(type), // it.N().Rlist is the variable per case if n2.Rlist.Len() != 0 { - e.escassignNilWhy(n2.Rlist.First(), n.Left.Right, "switch case") + e.escassignWhyWhere(n2.Rlist.First(), n.Left.Right, "switch case", n) } } } @@ -769,25 +786,25 @@ func (e *EscState) esc(n *Node, parent *Node) { break } - e.escassign(n.Left, n.Right, nil) + e.escassign(n.Left, n.Right, e.stepAssignWhere(nil, nil, "", n)) case OAS2: // x,y = a,b if n.List.Len() == n.Rlist.Len() { rs := n.Rlist.Slice() for i, n := range n.List.Slice() { - e.escassignNilWhy(n, rs[i], "assign-pair") + e.escassignWhyWhere(n, rs[i], "assign-pair", n) } } case OAS2RECV: // v, ok = <-ch - e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-receive") + e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-receive", n) case OAS2MAPR: // v, ok = m[k] - e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-mapr") + e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-mapr", n) case OAS2DOTTYPE: // v, ok = x.(type) - e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-dot-type") + e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-dot-type", n) case OSEND: // ch <- x - e.escassignSinkNilWhy(n, n.Right, "send") + e.escassignSinkWhy(n, n.Right, "send") case ODEFER: if e.loopdepth == 1 { // top level @@ -796,20 +813,20 @@ func (e *EscState) esc(n *Node, parent *Node) { // arguments leak out of scope // TODO: leak to a dummy node instead // defer f(x) - f and x escape - e.escassignSinkNilWhy(n, n.Left.Left, "defer func") + e.escassignSinkWhy(n, n.Left.Left, "defer func") - e.escassignSinkNilWhy(n, n.Left.Right, "defer func ...") // ODDDARG for call + e.escassignSinkWhy(n, n.Left.Right, "defer func ...") // ODDDARG for call for _, n4 := range n.Left.List.Slice() { - e.escassignSinkNilWhy(n, n4, "defer func arg") + e.escassignSinkWhy(n, n4, "defer func arg") } case OPROC: // go f(x) - f and x escape - e.escassignSinkNilWhy(n, n.Left.Left, "go func") + e.escassignSinkWhy(n, n.Left.Left, "go func") - e.escassignSinkNilWhy(n, n.Left.Right, "go func ...") // ODDDARG for call + e.escassignSinkWhy(n, n.Left.Right, "go func ...") // ODDDARG for call for _, n4 := range n.Left.List.Slice() { - e.escassignSinkNilWhy(n, n4, "go func arg") + e.escassignSinkWhy(n, n4, "go func arg") } case OCALLMETH, OCALLFUNC, OCALLINTER: @@ -822,7 +839,7 @@ func (e *EscState) esc(n *Node, parent *Node) { if i >= len(rs) { break } - e.escassignNilWhy(n, rs[i], "assign-pair-func-call") + e.escassignWhyWhere(n, rs[i], "assign-pair-func-call", n) } if n.List.Len() != len(rs) { Fatalf("esc oas2func") @@ -845,7 +862,7 @@ func (e *EscState) esc(n *Node, parent *Node) { if lrn.Op != ONAME || lrn.Class != PPARAMOUT { continue } - e.escassignNilWhy(lrn, retList.Index(i), "return") + e.escassignWhyWhere(lrn, retList.Index(i), "return", n) i++ } @@ -855,37 +872,37 @@ func (e *EscState) esc(n *Node, parent *Node) { // Argument could leak through recover. case OPANIC: - e.escassignSinkNilWhy(n, n.Left, "panic") + e.escassignSinkWhy(n, n.Left, "panic") case OAPPEND: if !n.Isddd { for _, nn := range n.List.Slice()[1:] { - e.escassignSinkNilWhy(n, nn, "appended to slice") // lose track of assign to dereference + e.escassignSinkWhy(n, nn, "appended to slice") // lose track of assign to dereference } } else { // append(slice1, slice2...) -- slice2 itself does not escape, but contents do. slice2 := n.List.Second() - e.escassignDereference(&e.theSink, slice2, e.stepAssign(nil, n, slice2, "appended slice...")) // lose track of assign of dereference + e.escassignDereference(&e.theSink, slice2, e.stepAssignWhere(n, slice2, "appended slice...", n)) // lose track of assign of dereference if Debug['m'] > 3 { Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %S", e.curfnSym(n), n) } } - e.escassignDereference(&e.theSink, n.List.First(), e.stepAssign(nil, n, n.List.First(), "appendee slice")) // The original elements are now leaked, too + e.escassignDereference(&e.theSink, n.List.First(), e.stepAssignWhere(n, n.List.First(), "appendee slice", n)) // The original elements are now leaked, too case OCOPY: - e.escassignDereference(&e.theSink, n.Right, e.stepAssign(nil, n, n.Right, "copied slice")) // lose track of assign of dereference + e.escassignDereference(&e.theSink, n.Right, e.stepAssignWhere(n, n.Right, "copied slice", n)) // lose track of assign of dereference case OCONV, OCONVNOP: - e.escassignNilWhy(n, n.Left, "converted") + e.escassignWhyWhere(n, n.Left, "converted", n) case OCONVIFACE: e.track(n) - e.escassignNilWhy(n, n.Left, "interface-converted") + e.escassignWhyWhere(n, n.Left, "interface-converted", n) case OARRAYLIT: // Link values to array for _, n5 := range n.List.Slice() { - e.escassign(n, n5.Right, e.stepAssign(nil, n, n5.Right, "array literal element")) + e.escassign(n, n5.Right, e.stepAssignWhere(n, n5.Right, "array literal element", n)) } case OSLICELIT: @@ -893,33 +910,33 @@ func (e *EscState) esc(n *Node, parent *Node) { e.track(n) // Link values to slice for _, n5 := range n.List.Slice() { - e.escassign(n, n5.Right, e.stepAssign(nil, n, n5.Right, "slice literal element")) + e.escassign(n, n5.Right, e.stepAssignWhere(n, n5.Right, "slice literal element", n)) } // Link values to struct. case OSTRUCTLIT: for _, n6 := range n.List.Slice() { - e.escassignNilWhy(n, n6.Left, "struct literal element") + e.escassignWhyWhere(n, n6.Left, "struct literal element", n) } case OPTRLIT: e.track(n) // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. - e.escassignNilWhy(n, n.Left, "pointer literal [assign]") + e.escassignWhyWhere(n, n.Left, "pointer literal [assign]", n) case OCALLPART: e.track(n) // Contents make it to memory, lose track. - e.escassignSinkNilWhy(n, n.Left, "call part") + e.escassignSinkWhy(n, n.Left, "call part") case OMAPLIT: e.track(n) // Keys and values make it to memory, lose track. for _, n7 := range n.List.Slice() { - e.escassignSinkNilWhy(n, n7.Left, "map literal key") - e.escassignSinkNilWhy(n, n7.Right, "map literal value") + e.escassignSinkWhy(n, n7.Left, "map literal key") + e.escassignSinkWhy(n, n7.Right, "map literal value") } case OCLOSURE: @@ -936,7 +953,7 @@ func (e *EscState) esc(n *Node, parent *Node) { a = typecheck(a, Erv) } - e.escassignNilWhy(n, a, "captured by a closure") + e.escassignWhyWhere(n, a, "captured by a closure", n) } fallthrough @@ -988,19 +1005,19 @@ func (e *EscState) esc(n *Node, parent *Node) { lineno = lno } -// escassignNilWhy bundles a common case of -// escassign(e, dst, src, e.stepAssign(nil, dst, src, reason)) -func (e *EscState) escassignNilWhy(dst, src *Node, reason string) { +// escassignWhyWhere bundles a common case of +// escassign(e, dst, src, e.stepAssignWhere(dst, src, reason, where)) +func (e *EscState) escassignWhyWhere(dst, src *Node, reason string, where *Node) { var step *EscStep if Debug['m'] != 0 { - step = e.stepAssign(nil, dst, src, reason) + step = e.stepAssignWhere(dst, src, reason, where) } e.escassign(dst, src, step) } -// escassignSinkNilWhy bundles a common case of +// escassignSinkWhy bundles a common case of // escassign(e, &e.theSink, src, e.stepAssign(nil, dst, src, reason)) -func (e *EscState) escassignSinkNilWhy(dst, src *Node, reason string) { +func (e *EscState) escassignSinkWhy(dst, src *Node, reason string) { var step *EscStep if Debug['m'] != 0 { step = e.stepAssign(nil, dst, src, reason) @@ -1008,6 +1025,16 @@ func (e *EscState) escassignSinkNilWhy(dst, src *Node, reason string) { e.escassign(&e.theSink, src, step) } +// escassignSinkWhyWhere is escassignSinkWhy but includes a call site +// for accurate location reporting. +func (e *EscState) escassignSinkWhyWhere(dst, src *Node, reason string, call *Node) { + var step *EscStep + if Debug['m'] != 0 { + step = e.stepAssignWhere(dst, src, reason, call) + } + e.escassign(&e.theSink, src, step) +} + // Assert that expr somehow gets assigned to dst, if non nil. for // dst==nil, any name node expr still must be marked as being // evaluated in curfn. For expr==nil, dst must still be examined for @@ -1288,7 +1315,7 @@ func describeEscape(em uint16) string { // escassignfromtag models the input-to-output assignment flow of one of a function // calls arguments, where the flow is encoded in "note". -func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 { +func (e *EscState) escassignfromtag(note string, dsts Nodes, src, call *Node) uint16 { em := parsetag(note) if src.Op == OLITERAL { return em @@ -1300,7 +1327,7 @@ func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 { } if em == EscUnknown { - e.escassignSinkNilWhy(src, src, "passed to function[unknown]") + e.escassignSinkWhyWhere(src, src, "passed to call[argument escapes]", call) return em } @@ -1311,7 +1338,7 @@ func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 { // If content inside parameter (reached via indirection) // escapes to heap, mark as such. if em&EscContentEscapes != 0 { - e.escassign(&e.theSink, e.addDereference(src), e.stepAssign(nil, src, src, "passed to function[content escapes]")) + e.escassign(&e.theSink, e.addDereference(src), e.stepAssignWhere(src, src, "passed to call[argument content escapes]", call)) } em0 := em @@ -1328,7 +1355,7 @@ func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 { for i := uint16(0); i < embits-1; i++ { n = e.addDereference(n) // encode level>0 as indirections } - e.escassign(dsts.Index(dstsi), n, e.stepAssign(nil, dsts.Index(dstsi), src, "passed-to-and-returned-from-function")) + e.escassign(dsts.Index(dstsi), n, e.stepAssignWhere(dsts.Index(dstsi), src, "passed-to-and-returned-from-call", call)) } dstsi++ } @@ -1464,7 +1491,7 @@ func (e *EscState) esccall(call *Node, parent *Node) { // We know nothing! // Leak all the parameters for _, arg := range args { - e.escassignSinkNilWhy(call, arg, "parameter to indirect call") + e.escassignSinkWhy(call, arg, "parameter to indirect call") if Debug['m'] > 3 { fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), arg) } @@ -1476,12 +1503,12 @@ func (e *EscState) esccall(call *Node, parent *Node) { rf := fntype.Recv() r := call.Left.Left if haspointers(rf.Type) { - e.escassignSinkNilWhy(call, r, "receiver in indirect call") + e.escassignSinkWhy(call, r, "receiver in indirect call") } } else { // indirect and OCALLFUNC = could be captured variables, too. (#14409) rets := e.nodeEscState(call).Retval.Slice() for _, ret := range rets { - e.escassignDereference(ret, fn, e.stepAssign(nil, ret, fn, "captured by called closure")) + e.escassignDereference(ret, fn, e.stepAssignWhere(ret, fn, "captured by called closure", call)) } } return @@ -1505,7 +1532,7 @@ func (e *EscState) esccall(call *Node, parent *Node) { switch n.Class { case PPARAM: if call.Op != OCALLFUNC && !sawRcvr { - e.escassignNilWhy(n, call.Left.Left, "call receiver") + e.escassignWhyWhere(n, call.Left.Left, "call receiver", call) sawRcvr = true continue } @@ -1522,14 +1549,14 @@ func (e *EscState) esccall(call *Node, parent *Node) { e.track(arg) call.Right = arg } - e.escassignNilWhy(n, arg, "arg to recursive call") + e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help. if arg != args[0] { // "..." arguments are untracked for _, a := range args { if Debug['m'] > 3 { fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a) } - e.escassignSinkNilWhy(arg, a, "... arg to recursive call") + e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call) } // No more PPARAM processing, but keep // going for PPARAMOUT. @@ -1565,7 +1592,7 @@ func (e *EscState) esccall(call *Node, parent *Node) { rf := fntype.Recv() r := call.Left.Left if haspointers(rf.Type) { - e.escassignfromtag(rf.Note, cE.Retval, r) + e.escassignfromtag(rf.Note, cE.Retval, r, call) } } @@ -1587,7 +1614,7 @@ func (e *EscState) esccall(call *Node, parent *Node) { } if haspointers(param.Type) { - if e.escassignfromtag(note, cE.Retval, arg)&EscMask == EscNone && parent.Op != ODEFER && parent.Op != OPROC { + if e.escassignfromtag(note, cE.Retval, arg, call)&EscMask == EscNone && parent.Op != ODEFER && parent.Op != OPROC { a := arg for a.Op == OCONVNOP { a = a.Left @@ -1620,7 +1647,7 @@ func (e *EscState) esccall(call *Node, parent *Node) { } if note == uintptrEscapesTag { - e.escassignSinkNilWhy(arg, arg, "escaping uintptr") + e.escassignSinkWhy(arg, arg, "escaping uintptr") } param = it.Next() @@ -1632,9 +1659,9 @@ func (e *EscState) esccall(call *Node, parent *Node) { fmt.Printf("%v::esccall:: ... <- %S\n", linestr(lineno), args[i]) } if note == uintptrEscapesTag { - e.escassignSinkNilWhy(arg, args[i], "arg to uintptrescapes ...") + e.escassignSinkWhyWhere(arg, args[i], "arg to uintptrescapes ...", call) } else { - e.escassignNilWhy(arg, args[i], "arg to ...") + e.escassignWhyWhere(arg, args[i], "arg to ...", call) } } } @@ -1722,10 +1749,14 @@ func (es *EscStep) describe(src *Node) { // case it is step.dst. nextDest := step.parent dst := step.dst + where := step.where if nextDest != nil { dst = nextDest.src } - Warnl(src.Lineno, "\tfrom %v (%s) at %s", dst, step.why, dst.Line()) + if where == nil { + where = dst + } + Warnl(src.Lineno, "\tfrom %v (%s) at %s", dst, step.why, where.Line()) } for step := step0; step != nil && step.busy; step = step.parent { step.busy = false diff --git a/test/escape_because.go b/test/escape_because.go index f0bbd0b93aa7adbc487e93f705c6e78e46c26b0a..7d349b7a18d360ac1c9385f6df15feaf886d7948 100644 --- a/test/escape_because.go +++ b/test/escape_because.go @@ -30,22 +30,22 @@ func (p *pair) EqualParts() bool { // ERROR "\(\*pair\).EqualParts p does not es return p != nil && (p.x == p.y || *p.x == *p.y) } -func f1(p *int) { // ERROR "from \[3\]\*int literal \(array literal element\) at escape_because.go:34$" "from a \(assigned\) at escape_because.go:34$" "from a \(interface-converted\) at escape_because.go:35$" "from sink \(assigned to top level variable\) at escape_because.go:19$" "leaking param: p$" +func f1(p *int) { // ERROR "from \[3\]\*int literal \(array literal element\) at escape_because.go:34$" "from a \(assigned\) at escape_because.go:34$" "from a \(interface-converted\) at escape_because.go:35$" "from sink \(assigned to top level variable\) at escape_because.go:35$" "leaking param: p$" a := [3]*int{p, nil, nil} - sink = a // ERROR "a escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:19$" + sink = a // ERROR "a escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:35$" } -func f2(q *int) { // ERROR "from &u \(address-of\) at escape_because.go:43$" "from &u \(interface-converted\) at escape_because.go:43$" "from pair literal \(struct literal element\) at escape_because.go:41$" "from s \(assigned\) at escape_because.go:40$" "from sink \(assigned to top level variable\) at escape_because.go:19$" "from t \(assigned\) at escape_because.go:41$" "from u \(assigned\) at escape_because.go:42$" "leaking param: q$" +func f2(q *int) { // ERROR "from &u \(address-of\) at escape_because.go:43$" "from &u \(interface-converted\) at escape_because.go:43$" "from pair literal \(struct literal element\) at escape_because.go:41$" "from s \(assigned\) at escape_because.go:40$" "from sink \(assigned to top level variable\) at escape_because.go:43$" "from t \(assigned\) at escape_because.go:41$" "from u \(assigned\) at escape_because.go:42$" "leaking param: q$" s := q t := pair{s, nil} u := t // ERROR "moved to heap: u$" - sink = &u // ERROR "&u escapes to heap$" "from &u \(interface-converted\) at escape_because.go:43$" "from sink \(assigned to top level variable\) at escape_because.go:19$" + sink = &u // ERROR "&u escapes to heap$" "from &u \(interface-converted\) at escape_because.go:43$" "from sink \(assigned to top level variable\) at escape_because.go:43$" } -func f3(r *int) interface{} { // ERROR "from \[\]\*int literal \(slice-literal-element\) at escape_because.go:47$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:46$" "leaking param: r to result ~r1 level=-1$" - c := []*int{r} // ERROR "\[\]\*int literal escapes to heap$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:46$" - return c // "return" // ERROR "c escapes to heap$" "from ~r1 \(return\) at escape_because.go:46$" +func f3(r *int) interface{} { // ERROR "from \[\]\*int literal \(slice-literal-element\) at escape_because.go:47$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$" "leaking param: r to result ~r1 level=-1$" + c := []*int{r} // ERROR "\[\]\*int literal escapes to heap$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$" + return c // "return" // ERROR "c escapes to heap$" "from ~r1 \(return\) at escape_because.go:48$" } func f4(a *int, s []*int) int { // ERROR "from \*s \(indirection\) at escape_because.go:51$" "from append\(s, a\) \(appended to slice\) at escape_because.go:52$" "from append\(s, a\) \(appendee slice\) at escape_because.go:52$" "leaking param content: s$" "leaking param: a$" @@ -73,15 +73,15 @@ func f7(x map[int]*int, y int) *int { // ERROR "f7 x does not escape$" return z } -func f8(x int, y *int) *int { // ERROR "from ~r2 \(return\) at escape_because.go:76$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" "leaking param: y$" "moved to heap: x$" +func f8(x int, y *int) *int { // ERROR "from ~r2 \(return\) at escape_because.go:78$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" "leaking param: y$" "moved to heap: x$" if x <= 0 { return y } x-- - return f8(*y, &x) // ERROR "&x escapes to heap$" "from y \(arg to recursive call\) at escape_because.go:76$" "from ~r2 \(return\) at escape_because.go:76$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" + return f8(*y, &x) // ERROR "&x escapes to heap$" "from y \(arg to recursive call\) at escape_because.go:81$" "from ~r2 \(return\) at escape_because.go:78$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" } -func f9(x int, y ...*int) *int { // ERROR "from y\[0\] \(dot of pointer\) at escape_because.go:86$" "from ~r2 \(return\) at escape_because.go:84$" "from ~r2 \(returned from recursive function\) at escape_because.go:84$" "leaking param content: y$" "leaking param: y to result ~r2 level=1$" "moved to heap: x$" +func f9(x int, y ...*int) *int { // ERROR "from y\[0\] \(dot of pointer\) at escape_because.go:86$" "from ~r2 \(return\) at escape_because.go:86$" "from ~r2 \(returned from recursive function\) at escape_because.go:84$" "leaking param content: y$" "leaking param: y to result ~r2 level=1$" "moved to heap: x$" if x <= 0 { return y[0] } @@ -95,7 +95,27 @@ func f10(x map[*int]*int, y, z *int) *int { // ERROR "f10 x does not escape$" "f } func f11(x map[*int]*int, y, z *int) map[*int]*int { // ERROR "f11 x does not escape$" "from map\[\*int\]\*int literal \(map literal key\) at escape_because.go:98$" "from map\[\*int\]\*int literal \(map literal value\) at escape_because.go:98$" "leaking param: y$" "leaking param: z$" - return map[*int]*int{y: z} // ERROR "from ~r3 \(return\) at escape_because.go:97$" "map\[\*int\]\*int literal escapes to heap$" + return map[*int]*int{y: z} // ERROR "from ~r3 \(return\) at escape_because.go:98$" "map\[\*int\]\*int literal escapes to heap$" +} + +func f12() { + b := []byte("test") // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from b \(assigned\) at escape_because.go:102$" "from b \(passed to call\[argument escapes\]\) at escape_because.go:103$" + escape(b) +} + +func escape(b []byte) { // ERROR "from panic\(b\) \(panic\) at escape_because.go:107$" "leaking param: b$" + panic(b) +} + +func f13() { + b := []byte("test") // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from .out0 \(passed-to-and-returned-from-call\) at escape_because.go:112$" "from b \(assigned\) at escape_because.go:111$" "from c \(assigned\) at escape_because.go:112$" "from c \(passed to call\[argument escapes\]\) at escape_because.go:113$" + c := transmit(b) + escape(c) +} + +//go:noinline +func transmit(b []byte) []byte { // ERROR "from ~r1 \(return\) at escape_because.go:118$" "leaking param: b to result ~r1 level=0$" + return b } // The list below is all of the why-escapes messages seen building the escape analysis tests. @@ -142,9 +162,9 @@ key of map put map literal key map literal value parameter to indirect call -passed to function[content escapes] -passed to function[unknown] -passed-to-and-returned-from-function +passed to call[argument content escapes] +passed to call[argument escapes] +passed-to-and-returned-from-call pointer literal range range-deref