Skip to content

Commit 8486ced

Browse files
committed
cmd/compile: copy captured dictionary var to local var
When starting a closure that needs a dictionary, copy the closure variable to a local variable. This lets child closures capture that dictionary variable correctly. This is a better fix for #47684, which does not cause problems like #47723. Fixes #47723 Update #47684 Change-Id: Ib5d9ffc68a5142e28daa7d0d75683e7a35508540 Reviewed-on: https://go-review.googlesource.com/c/go/+/343871 Trust: Keith Randall <khr@golang.org> Trust: Dan Scales <danscales@google.com> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Dan Scales <danscales@google.com>
1 parent aeec6db commit 8486ced

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

src/cmd/compile/internal/ir/name.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,7 @@ func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
404404
if n.Op() != ONAME || n.Curfn == nil {
405405
return n // okay to use directly
406406
}
407-
if n.IsClosureVar() && n.Sym().Name != ".dict" {
408-
// Note: capturing dictionary closure variables is ok. This makes
409-
// sure the generated code is correctly optimized.
407+
if n.IsClosureVar() {
410408
base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
411409
}
412410

src/cmd/compile/internal/noder/stencil.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -1087,13 +1087,25 @@ func (subst *subster) node(n ir.Node) ir.Node {
10871087
ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
10881088
newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)
10891089

1090+
// Copy that closure variable to a local one.
1091+
// Note: this allows the dictionary to be captured by child closures.
1092+
// See issue 47723.
1093+
ldict := ir.NewNameAt(x.Pos(), subst.info.gf.Sym().Pkg.Lookup(".dict"))
1094+
typed(types.Types[types.TUINTPTR], ldict)
1095+
ldict.Class = ir.PAUTO
1096+
ldict.Curfn = newfn
1097+
newfn.Dcl = append(newfn.Dcl, ldict)
1098+
as := ir.NewAssignStmt(x.Pos(), ldict, cdict)
1099+
as.SetTypecheck(1)
1100+
newfn.Body.Append(as)
1101+
10901102
// Create inst info for the instantiated closure. The dict
10911103
// param is the closure variable for the dictionary of the
10921104
// outer function. Since the dictionary is shared, use the
10931105
// same entries for startSubDict, dictLen, dictEntryMap.
10941106
cinfo := &instInfo{
10951107
fun: newfn,
1096-
dictParam: cdict,
1108+
dictParam: ldict,
10971109
gf: subst.info.gf,
10981110
gfInfo: subst.info.gfInfo,
10991111
startSubDict: subst.info.startSubDict,
@@ -1110,7 +1122,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
11101122
outerinfo := subst.info
11111123
subst.info = cinfo
11121124
// Make sure type of closure function is set before doing body.
1113-
newfn.Body = subst.list(oldfn.Body)
1125+
newfn.Body.Append(subst.list(oldfn.Body)...)
11141126
subst.info = outerinfo
11151127
subst.newf = saveNewf
11161128
ir.CurFunc = saveNewf

test/typeparam/issue47723.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run -gcflags=-G=3
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
func f[_ any]() int {
10+
var a [1]int
11+
_ = func() int {
12+
return func() int {
13+
return 0
14+
}()
15+
}()
16+
return a[func() int {
17+
return 0
18+
}()]
19+
}
20+
21+
func main() {
22+
f[int]()
23+
}

0 commit comments

Comments
 (0)