Commit be09bdf5 authored by Cherry Zhang's avatar Cherry Zhang

cmd/compile: fix unnamed parameter handling in escape analysis

For recursive functions, the parameters were iterated using
fn.Name.Defn.Func.Dcl, which does not include unnamed/blank
parameters. This results in a mismatch in formal-actual
assignments, for example,

func f(_ T, x T)

f(a, b) should result in { _=a, x=b }, but the escape analysis
currently sees only { x=a } and drops b on the floor. This may
cause b to not escape when it should (or a escape when it should
not).

Fix this by using fntype.Params().FieldSlice() instead, which
does include unnamed parameters.

Also add a sanity check that ensures all the actual parameters
are consumed.

Fixes #29000

Change-Id: Icd86f2b5d71e7ebbab76e375b7702f62efcf59ae
Reviewed-on: https://go-review.googlesource.com/c/152617Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 8a5797a0
...@@ -1652,49 +1652,79 @@ func (e *EscState) esccall(call *Node, parent *Node) { ...@@ -1652,49 +1652,79 @@ func (e *EscState) esccall(call *Node, parent *Node) {
Fatalf("graph inconsistency") Fatalf("graph inconsistency")
} }
sawRcvr := false i := 0
for _, n := range fn.Name.Defn.Func.Dcl {
switch n.Class() { // Receiver.
case PPARAM: if call.Op != OCALLFUNC {
if call.Op != OCALLFUNC && !sawRcvr { rf := fntype.Recv()
e.escassignWhyWhere(n, call.Left.Left, "call receiver", call) if rf.Sym != nil && !rf.Sym.IsBlank() {
sawRcvr = true n := fn.Name.Defn.Func.Dcl[0]
continue i++
} if n.Class() != PPARAM {
if len(args) == 0 { Fatalf("esccall: not a parameter %+v", n)
continue
}
arg := args[0]
if n.IsDDD() && !call.IsDDD() {
// Introduce ODDDARG node to represent ... allocation.
arg = nod(ODDDARG, nil, nil)
arr := types.NewArray(n.Type.Elem(), int64(len(args)))
arg.Type = types.NewPtr(arr) // make pointer so it will be tracked
arg.Pos = call.Pos
e.track(arg)
call.Right = arg
} }
e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help. e.escassignWhyWhere(n, call.Left.Left, "recursive call receiver", call)
if arg == args[0] { }
}
// Parameters.
for _, param := range fntype.Params().FieldSlice() {
if param.Sym == nil || param.Sym.IsBlank() {
// Unnamed parameter is not listed in Func.Dcl.
// But we need to consume the arg.
if param.IsDDD() && !call.IsDDD() {
args = nil
} else {
args = args[1:] args = args[1:]
continue
} }
// "..." arguments are untracked continue
for _, a := range args { }
if Debug['m'] > 3 {
fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a) n := fn.Name.Defn.Func.Dcl[i]
} i++
e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call) if n.Class() != PPARAM {
Fatalf("esccall: not a parameter %+v", n)
}
if len(args) == 0 {
continue
}
arg := args[0]
if n.IsDDD() && !call.IsDDD() {
// Introduce ODDDARG node to represent ... allocation.
arg = nod(ODDDARG, nil, nil)
arr := types.NewArray(n.Type.Elem(), int64(len(args)))
arg.Type = types.NewPtr(arr) // make pointer so it will be tracked
arg.Pos = call.Pos
e.track(arg)
call.Right = arg
}
e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help.
if arg == args[0] {
args = args[1:]
continue
}
// "..." arguments are untracked
for _, a := range args {
if Debug['m'] > 3 {
fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
} }
// No more PPARAM processing, but keep e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call)
// going for PPARAMOUT. }
args = nil // ... arg consumes all remaining arguments
args = nil
}
case PPARAMOUT: // Results.
for _, n := range fn.Name.Defn.Func.Dcl[i:] {
if n.Class() == PPARAMOUT {
cE.Retval.Append(n) cE.Retval.Append(n)
} }
} }
// Sanity check: all arguments must be consumed.
if len(args) != 0 {
Fatalf("esccall not consumed all args %+v\n", call)
}
return return
} }
......
...@@ -228,3 +228,20 @@ func f15730c(args ...interface{}) { // ERROR "leaking param content: args" ...@@ -228,3 +228,20 @@ func f15730c(args ...interface{}) { // ERROR "leaking param content: args"
} }
} }
} }
// Issue 29000: unnamed parameter is not handled correctly
var sink4 interface{}
var alwaysFalse = false
func f29000(_ int, x interface{}) { // ERROR "leaking param: x"
sink4 = x
if alwaysFalse {
g29000()
}
}
func g29000() {
x := 1
f29000(2, x) // ERROR "x escapes to heap"
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment