Skip to content

Commit be09bdf

Browse files
committed
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/152617 Reviewed-by: Keith Randall <[email protected]>
1 parent 8a5797a commit be09bdf

File tree

2 files changed

+81
-34
lines changed

2 files changed

+81
-34
lines changed

src/cmd/compile/internal/gc/esc.go

+64-34
Original file line numberDiff line numberDiff line change
@@ -1652,49 +1652,79 @@ func (e *EscState) esccall(call *Node, parent *Node) {
16521652
Fatalf("graph inconsistency")
16531653
}
16541654

1655-
sawRcvr := false
1656-
for _, n := range fn.Name.Defn.Func.Dcl {
1657-
switch n.Class() {
1658-
case PPARAM:
1659-
if call.Op != OCALLFUNC && !sawRcvr {
1660-
e.escassignWhyWhere(n, call.Left.Left, "call receiver", call)
1661-
sawRcvr = true
1662-
continue
1663-
}
1664-
if len(args) == 0 {
1665-
continue
1666-
}
1667-
arg := args[0]
1668-
if n.IsDDD() && !call.IsDDD() {
1669-
// Introduce ODDDARG node to represent ... allocation.
1670-
arg = nod(ODDDARG, nil, nil)
1671-
arr := types.NewArray(n.Type.Elem(), int64(len(args)))
1672-
arg.Type = types.NewPtr(arr) // make pointer so it will be tracked
1673-
arg.Pos = call.Pos
1674-
e.track(arg)
1675-
call.Right = arg
1655+
i := 0
1656+
1657+
// Receiver.
1658+
if call.Op != OCALLFUNC {
1659+
rf := fntype.Recv()
1660+
if rf.Sym != nil && !rf.Sym.IsBlank() {
1661+
n := fn.Name.Defn.Func.Dcl[0]
1662+
i++
1663+
if n.Class() != PPARAM {
1664+
Fatalf("esccall: not a parameter %+v", n)
16761665
}
1677-
e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help.
1678-
if arg == args[0] {
1666+
e.escassignWhyWhere(n, call.Left.Left, "recursive call receiver", call)
1667+
}
1668+
}
1669+
1670+
// Parameters.
1671+
for _, param := range fntype.Params().FieldSlice() {
1672+
if param.Sym == nil || param.Sym.IsBlank() {
1673+
// Unnamed parameter is not listed in Func.Dcl.
1674+
// But we need to consume the arg.
1675+
if param.IsDDD() && !call.IsDDD() {
1676+
args = nil
1677+
} else {
16791678
args = args[1:]
1680-
continue
16811679
}
1682-
// "..." arguments are untracked
1683-
for _, a := range args {
1684-
if Debug['m'] > 3 {
1685-
fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
1686-
}
1687-
e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call)
1680+
continue
1681+
}
1682+
1683+
n := fn.Name.Defn.Func.Dcl[i]
1684+
i++
1685+
if n.Class() != PPARAM {
1686+
Fatalf("esccall: not a parameter %+v", n)
1687+
}
1688+
if len(args) == 0 {
1689+
continue
1690+
}
1691+
arg := args[0]
1692+
if n.IsDDD() && !call.IsDDD() {
1693+
// Introduce ODDDARG node to represent ... allocation.
1694+
arg = nod(ODDDARG, nil, nil)
1695+
arr := types.NewArray(n.Type.Elem(), int64(len(args)))
1696+
arg.Type = types.NewPtr(arr) // make pointer so it will be tracked
1697+
arg.Pos = call.Pos
1698+
e.track(arg)
1699+
call.Right = arg
1700+
}
1701+
e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help.
1702+
if arg == args[0] {
1703+
args = args[1:]
1704+
continue
1705+
}
1706+
// "..." arguments are untracked
1707+
for _, a := range args {
1708+
if Debug['m'] > 3 {
1709+
fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
16881710
}
1689-
// No more PPARAM processing, but keep
1690-
// going for PPARAMOUT.
1691-
args = nil
1711+
e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call)
1712+
}
1713+
// ... arg consumes all remaining arguments
1714+
args = nil
1715+
}
16921716

1693-
case PPARAMOUT:
1717+
// Results.
1718+
for _, n := range fn.Name.Defn.Func.Dcl[i:] {
1719+
if n.Class() == PPARAMOUT {
16941720
cE.Retval.Append(n)
16951721
}
16961722
}
16971723

1724+
// Sanity check: all arguments must be consumed.
1725+
if len(args) != 0 {
1726+
Fatalf("esccall not consumed all args %+v\n", call)
1727+
}
16981728
return
16991729
}
17001730

test/escape5.go

+17
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,20 @@ func f15730c(args ...interface{}) { // ERROR "leaking param content: args"
228228
}
229229
}
230230
}
231+
232+
// Issue 29000: unnamed parameter is not handled correctly
233+
234+
var sink4 interface{}
235+
var alwaysFalse = false
236+
237+
func f29000(_ int, x interface{}) { // ERROR "leaking param: x"
238+
sink4 = x
239+
if alwaysFalse {
240+
g29000()
241+
}
242+
}
243+
244+
func g29000() {
245+
x := 1
246+
f29000(2, x) // ERROR "x escapes to heap"
247+
}

0 commit comments

Comments
 (0)