Skip to content

Commit a064a4f

Browse files
committed
cmd/compile: ensure dictionary assignment statements are defining statements
The problem in 51355 is that escape analysis decided that the dictionary variable was captured by reference instead of by value. We want dictionaries to always be captured by value. Escape analysis was confused because it saw what it thought was a reassignment of the dictionary variable. In fact, it was the only assignment, it just wasn't marked as the defining assignment. Fix that. Add an assert to make sure this stays true. Fixes #51355 Change-Id: Ifd9342455fa107b113f5ff521a94cdbf1b8a7733 Reviewed-on: https://go-review.googlesource.com/c/go/+/388115 Trust: Keith Randall <[email protected]> Run-TryBot: Keith Randall <[email protected]> Trust: Cuong Manh Le <[email protected]> Trust: Dan Scales <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Dan Scales <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 286e3e6 commit a064a4f

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

src/cmd/compile/internal/escape/escape.go

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"cmd/compile/internal/base"
1111
"cmd/compile/internal/ir"
1212
"cmd/compile/internal/logopt"
13+
"cmd/compile/internal/typecheck"
1314
"cmd/compile/internal/types"
1415
)
1516

@@ -243,6 +244,9 @@ func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
243244
n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
244245
if !n.Byval() {
245246
n.SetAddrtaken(true)
247+
if n.Sym().Name == typecheck.LocalDictName {
248+
base.FatalfAt(n.Pos(), "dictionary variable not captured by value")
249+
}
246250
}
247251

248252
if base.Flag.LowerM > 1 {

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,8 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
410410
fn, formalParams, formalResults := startClosure(pos, outer, typ)
411411

412412
// This is the dictionary we want to use.
413-
// It may be a constant, or it may be a dictionary acquired from the outer function's dictionary.
413+
// It may be a constant, it may be the outer functions's dictionary, or it may be
414+
// a subdictionary acquired from the outer function's dictionary.
414415
// For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary
415416
// read from the outer function's dictionary.
416417
var dictVar *ir.Name
@@ -1145,6 +1146,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
11451146
newfn.Dcl = append(newfn.Dcl, ldict)
11461147
as := ir.NewAssignStmt(x.Pos(), ldict, cdict)
11471148
as.SetTypecheck(1)
1149+
ldict.Defn = as
11481150
newfn.Body.Append(as)
11491151

11501152
// Create inst info for the instantiated closure. The dict

test/typeparam/issue51355.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// compile -G=3
2+
3+
// Copyright 2022 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+
type Cache[E comparable] struct {
10+
adder func(...E)
11+
}
12+
13+
func New[E comparable]() *Cache[E] {
14+
c := &Cache[E]{}
15+
16+
c.adder = func(elements ...E) {
17+
for _, value := range elements {
18+
value := value
19+
go func() {
20+
println(value)
21+
}()
22+
}
23+
}
24+
25+
return c
26+
}
27+
28+
func main() {
29+
c := New[string]()
30+
c.adder("test")
31+
}

0 commit comments

Comments
 (0)