mirror of https://github.com/golang/go.git
cmd/compile: add explicit 'where' to EscStep data for explanations
Sometimes neither the src nor the dst of an escape edge contains the line number appropriate to the edge, so add a field so that can be set correctly. Also updated some of the explanations to be less jargon-y and perhaps more informative, and folded bug example into test. Cleaned up some of the function/method names in esc.go and did a quick sanity check that each "bundling" function was actually called often enough to justify its existence. Fixes #17459. Change-Id: Ieba53ab0a6ba1f7a6c4962bc0b702ede9cc3a3cc Reviewed-on: https://go-review.googlesource.com/31660 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
6d9c8c926d
commit
bea5252a13
|
|
@ -302,6 +302,7 @@ func (l Level) guaranteedDereference() int {
|
||||||
// heap allocation.
|
// heap allocation.
|
||||||
type EscStep struct {
|
type EscStep struct {
|
||||||
src, dst *Node // the endpoints of this edge in the escape-to-heap chain.
|
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
|
parent *EscStep // used in flood to record path
|
||||||
why string // explanation for this step in the escape-to-heap chain
|
why string // explanation for this step in the escape-to-heap chain
|
||||||
busy bool // used in prevent to snip cycles.
|
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
|
return nil
|
||||||
}
|
}
|
||||||
if step != nil { // Caller may have known better.
|
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 step
|
||||||
}
|
}
|
||||||
return &EscStep{src: src, dst: dst, why: why}
|
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.
|
// funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way.
|
||||||
func funcSym(fn *Node) *Sym {
|
func funcSym(fn *Node) *Sym {
|
||||||
if fn == nil || fn.Func.Nname == nil {
|
if fn == nil || fn.Func.Nname == nil {
|
||||||
|
|
@ -667,7 +684,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
}
|
}
|
||||||
n.Esc = EscHeap
|
n.Esc = EscHeap
|
||||||
addrescapes(n)
|
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)
|
e.esc(n.Left, n)
|
||||||
|
|
@ -718,9 +735,9 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
// dereferenced (see #12588)
|
// dereferenced (see #12588)
|
||||||
if n.Type.IsArray() &&
|
if n.Type.IsArray() &&
|
||||||
!(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) {
|
!(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 {
|
} 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),
|
// n.Left.Right is the argument of the .(type),
|
||||||
// it.N().Rlist is the variable per case
|
// it.N().Rlist is the variable per case
|
||||||
if n2.Rlist.Len() != 0 {
|
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
|
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
|
case OAS2: // x,y = a,b
|
||||||
if n.List.Len() == n.Rlist.Len() {
|
if n.List.Len() == n.Rlist.Len() {
|
||||||
rs := n.Rlist.Slice()
|
rs := n.Rlist.Slice()
|
||||||
for i, n := range n.List.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
|
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]
|
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)
|
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
|
case OSEND: // ch <- x
|
||||||
e.escassignSinkNilWhy(n, n.Right, "send")
|
e.escassignSinkWhy(n, n.Right, "send")
|
||||||
|
|
||||||
case ODEFER:
|
case ODEFER:
|
||||||
if e.loopdepth == 1 { // top level
|
if e.loopdepth == 1 { // top level
|
||||||
|
|
@ -796,20 +813,20 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
// arguments leak out of scope
|
// arguments leak out of scope
|
||||||
// TODO: leak to a dummy node instead
|
// TODO: leak to a dummy node instead
|
||||||
// defer f(x) - f and x escape
|
// 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() {
|
for _, n4 := range n.Left.List.Slice() {
|
||||||
e.escassignSinkNilWhy(n, n4, "defer func arg")
|
e.escassignSinkWhy(n, n4, "defer func arg")
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPROC:
|
case OPROC:
|
||||||
// go f(x) - f and x escape
|
// 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() {
|
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:
|
case OCALLMETH, OCALLFUNC, OCALLINTER:
|
||||||
|
|
@ -822,7 +839,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
if i >= len(rs) {
|
if i >= len(rs) {
|
||||||
break
|
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) {
|
if n.List.Len() != len(rs) {
|
||||||
Fatalf("esc oas2func")
|
Fatalf("esc oas2func")
|
||||||
|
|
@ -845,7 +862,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
|
if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
e.escassignNilWhy(lrn, retList.Index(i), "return")
|
e.escassignWhyWhere(lrn, retList.Index(i), "return", n)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -855,37 +872,37 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
|
|
||||||
// Argument could leak through recover.
|
// Argument could leak through recover.
|
||||||
case OPANIC:
|
case OPANIC:
|
||||||
e.escassignSinkNilWhy(n, n.Left, "panic")
|
e.escassignSinkWhy(n, n.Left, "panic")
|
||||||
|
|
||||||
case OAPPEND:
|
case OAPPEND:
|
||||||
if !n.Isddd {
|
if !n.Isddd {
|
||||||
for _, nn := range n.List.Slice()[1:] {
|
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 {
|
} else {
|
||||||
// append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
|
// append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
|
||||||
slice2 := n.List.Second()
|
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 {
|
if Debug['m'] > 3 {
|
||||||
Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %S", e.curfnSym(n), n)
|
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:
|
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:
|
case OCONV, OCONVNOP:
|
||||||
e.escassignNilWhy(n, n.Left, "converted")
|
e.escassignWhyWhere(n, n.Left, "converted", n)
|
||||||
|
|
||||||
case OCONVIFACE:
|
case OCONVIFACE:
|
||||||
e.track(n)
|
e.track(n)
|
||||||
e.escassignNilWhy(n, n.Left, "interface-converted")
|
e.escassignWhyWhere(n, n.Left, "interface-converted", n)
|
||||||
|
|
||||||
case OARRAYLIT:
|
case OARRAYLIT:
|
||||||
// Link values to array
|
// Link values to array
|
||||||
for _, n5 := range n.List.Slice() {
|
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:
|
case OSLICELIT:
|
||||||
|
|
@ -893,33 +910,33 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
e.track(n)
|
e.track(n)
|
||||||
// Link values to slice
|
// Link values to slice
|
||||||
for _, n5 := range n.List.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.
|
// Link values to struct.
|
||||||
case OSTRUCTLIT:
|
case OSTRUCTLIT:
|
||||||
for _, n6 := range n.List.Slice() {
|
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:
|
case OPTRLIT:
|
||||||
e.track(n)
|
e.track(n)
|
||||||
|
|
||||||
// Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too.
|
// 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:
|
case OCALLPART:
|
||||||
e.track(n)
|
e.track(n)
|
||||||
|
|
||||||
// Contents make it to memory, lose track.
|
// Contents make it to memory, lose track.
|
||||||
e.escassignSinkNilWhy(n, n.Left, "call part")
|
e.escassignSinkWhy(n, n.Left, "call part")
|
||||||
|
|
||||||
case OMAPLIT:
|
case OMAPLIT:
|
||||||
e.track(n)
|
e.track(n)
|
||||||
// Keys and values make it to memory, lose track.
|
// Keys and values make it to memory, lose track.
|
||||||
for _, n7 := range n.List.Slice() {
|
for _, n7 := range n.List.Slice() {
|
||||||
e.escassignSinkNilWhy(n, n7.Left, "map literal key")
|
e.escassignSinkWhy(n, n7.Left, "map literal key")
|
||||||
e.escassignSinkNilWhy(n, n7.Right, "map literal value")
|
e.escassignSinkWhy(n, n7.Right, "map literal value")
|
||||||
}
|
}
|
||||||
|
|
||||||
case OCLOSURE:
|
case OCLOSURE:
|
||||||
|
|
@ -936,7 +953,7 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
a = typecheck(a, Erv)
|
a = typecheck(a, Erv)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.escassignNilWhy(n, a, "captured by a closure")
|
e.escassignWhyWhere(n, a, "captured by a closure", n)
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
|
|
@ -988,19 +1005,19 @@ func (e *EscState) esc(n *Node, parent *Node) {
|
||||||
lineno = lno
|
lineno = lno
|
||||||
}
|
}
|
||||||
|
|
||||||
// escassignNilWhy bundles a common case of
|
// escassignWhyWhere bundles a common case of
|
||||||
// escassign(e, dst, src, e.stepAssign(nil, dst, src, reason))
|
// escassign(e, dst, src, e.stepAssignWhere(dst, src, reason, where))
|
||||||
func (e *EscState) escassignNilWhy(dst, src *Node, reason string) {
|
func (e *EscState) escassignWhyWhere(dst, src *Node, reason string, where *Node) {
|
||||||
var step *EscStep
|
var step *EscStep
|
||||||
if Debug['m'] != 0 {
|
if Debug['m'] != 0 {
|
||||||
step = e.stepAssign(nil, dst, src, reason)
|
step = e.stepAssignWhere(dst, src, reason, where)
|
||||||
}
|
}
|
||||||
e.escassign(dst, src, step)
|
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))
|
// 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
|
var step *EscStep
|
||||||
if Debug['m'] != 0 {
|
if Debug['m'] != 0 {
|
||||||
step = e.stepAssign(nil, dst, src, reason)
|
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)
|
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
|
// Assert that expr somehow gets assigned to dst, if non nil. for
|
||||||
// dst==nil, any name node expr still must be marked as being
|
// dst==nil, any name node expr still must be marked as being
|
||||||
// evaluated in curfn. For expr==nil, dst must still be examined for
|
// 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
|
// escassignfromtag models the input-to-output assignment flow of one of a function
|
||||||
// calls arguments, where the flow is encoded in "note".
|
// 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)
|
em := parsetag(note)
|
||||||
if src.Op == OLITERAL {
|
if src.Op == OLITERAL {
|
||||||
return em
|
return em
|
||||||
|
|
@ -1300,7 +1327,7 @@ func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if em == EscUnknown {
|
if em == EscUnknown {
|
||||||
e.escassignSinkNilWhy(src, src, "passed to function[unknown]")
|
e.escassignSinkWhyWhere(src, src, "passed to call[argument escapes]", call)
|
||||||
return em
|
return em
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1311,7 +1338,7 @@ func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 {
|
||||||
// If content inside parameter (reached via indirection)
|
// If content inside parameter (reached via indirection)
|
||||||
// escapes to heap, mark as such.
|
// escapes to heap, mark as such.
|
||||||
if em&EscContentEscapes != 0 {
|
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
|
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++ {
|
for i := uint16(0); i < embits-1; i++ {
|
||||||
n = e.addDereference(n) // encode level>0 as indirections
|
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++
|
dstsi++
|
||||||
}
|
}
|
||||||
|
|
@ -1464,7 +1491,7 @@ func (e *EscState) esccall(call *Node, parent *Node) {
|
||||||
// We know nothing!
|
// We know nothing!
|
||||||
// Leak all the parameters
|
// Leak all the parameters
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
e.escassignSinkNilWhy(call, arg, "parameter to indirect call")
|
e.escassignSinkWhy(call, arg, "parameter to indirect call")
|
||||||
if Debug['m'] > 3 {
|
if Debug['m'] > 3 {
|
||||||
fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), arg)
|
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()
|
rf := fntype.Recv()
|
||||||
r := call.Left.Left
|
r := call.Left.Left
|
||||||
if haspointers(rf.Type) {
|
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)
|
} else { // indirect and OCALLFUNC = could be captured variables, too. (#14409)
|
||||||
rets := e.nodeEscState(call).Retval.Slice()
|
rets := e.nodeEscState(call).Retval.Slice()
|
||||||
for _, ret := range rets {
|
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
|
return
|
||||||
|
|
@ -1505,7 +1532,7 @@ func (e *EscState) esccall(call *Node, parent *Node) {
|
||||||
switch n.Class {
|
switch n.Class {
|
||||||
case PPARAM:
|
case PPARAM:
|
||||||
if call.Op != OCALLFUNC && !sawRcvr {
|
if call.Op != OCALLFUNC && !sawRcvr {
|
||||||
e.escassignNilWhy(n, call.Left.Left, "call receiver")
|
e.escassignWhyWhere(n, call.Left.Left, "call receiver", call)
|
||||||
sawRcvr = true
|
sawRcvr = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -1522,14 +1549,14 @@ func (e *EscState) esccall(call *Node, parent *Node) {
|
||||||
e.track(arg)
|
e.track(arg)
|
||||||
call.Right = 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] {
|
if arg != args[0] {
|
||||||
// "..." arguments are untracked
|
// "..." arguments are untracked
|
||||||
for _, a := range args {
|
for _, a := range args {
|
||||||
if Debug['m'] > 3 {
|
if Debug['m'] > 3 {
|
||||||
fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
|
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
|
// No more PPARAM processing, but keep
|
||||||
// going for PPARAMOUT.
|
// going for PPARAMOUT.
|
||||||
|
|
@ -1565,7 +1592,7 @@ func (e *EscState) esccall(call *Node, parent *Node) {
|
||||||
rf := fntype.Recv()
|
rf := fntype.Recv()
|
||||||
r := call.Left.Left
|
r := call.Left.Left
|
||||||
if haspointers(rf.Type) {
|
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 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
|
a := arg
|
||||||
for a.Op == OCONVNOP {
|
for a.Op == OCONVNOP {
|
||||||
a = a.Left
|
a = a.Left
|
||||||
|
|
@ -1620,7 +1647,7 @@ func (e *EscState) esccall(call *Node, parent *Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if note == uintptrEscapesTag {
|
if note == uintptrEscapesTag {
|
||||||
e.escassignSinkNilWhy(arg, arg, "escaping uintptr")
|
e.escassignSinkWhy(arg, arg, "escaping uintptr")
|
||||||
}
|
}
|
||||||
|
|
||||||
param = it.Next()
|
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])
|
fmt.Printf("%v::esccall:: ... <- %S\n", linestr(lineno), args[i])
|
||||||
}
|
}
|
||||||
if note == uintptrEscapesTag {
|
if note == uintptrEscapesTag {
|
||||||
e.escassignSinkNilWhy(arg, args[i], "arg to uintptrescapes ...")
|
e.escassignSinkWhyWhere(arg, args[i], "arg to uintptrescapes ...", call)
|
||||||
} else {
|
} 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.
|
// case it is step.dst.
|
||||||
nextDest := step.parent
|
nextDest := step.parent
|
||||||
dst := step.dst
|
dst := step.dst
|
||||||
|
where := step.where
|
||||||
if nextDest != nil {
|
if nextDest != nil {
|
||||||
dst = nextDest.src
|
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 {
|
for step := step0; step != nil && step.busy; step = step.parent {
|
||||||
step.busy = false
|
step.busy = false
|
||||||
|
|
|
||||||
|
|
@ -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)
|
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}
|
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
|
s := q
|
||||||
t := pair{s, nil}
|
t := pair{s, nil}
|
||||||
u := t // ERROR "moved to heap: u$"
|
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$"
|
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:46$"
|
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:46$"
|
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$"
|
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
|
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 {
|
if x <= 0 {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
x--
|
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 {
|
if x <= 0 {
|
||||||
return y[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$"
|
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.
|
// 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 key
|
||||||
map literal value
|
map literal value
|
||||||
parameter to indirect call
|
parameter to indirect call
|
||||||
passed to function[content escapes]
|
passed to call[argument content escapes]
|
||||||
passed to function[unknown]
|
passed to call[argument escapes]
|
||||||
passed-to-and-returned-from-function
|
passed-to-and-returned-from-call
|
||||||
pointer literal
|
pointer literal
|
||||||
range
|
range
|
||||||
range-deref
|
range-deref
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue