Skip to content

Commit 5a9dabc

Browse files
committed
cmd/compile: for rangefunc, add checks and tests, fix panic interactions
Modify rangefunc #next protocol to make it more robust Extra-terrible nests of rangefunc iterators caused the prior implementation to misbehave non-locally (in outer loops). Add more rangefunc exit flag tests, parallel and tricky This tests the assertion that a rangefunc iterator running in parallel can trigger the race detector if any of the parallel goroutines attempts an early exit. It also verifies that if everything else is carefully written, that it does NOT trigger the race detector if all the parts run time completion. Another test tries to rerun a yield function within a loop, so that any per-line shared checking would be fooled. Added all the use-of-body/yield-function checking. These checks handle pathological cases that would cause rangefunc for loops to behave in surprising ways (compared to "regular" for loops). For example, a rangefunc iterator might defer-recover a panic thrown in the syntactic body of a loop; this notices the fault and panics with an explanation Modified closure naming to ID rangefunc bodies Add a "-range<N>" suffix to the name of any closure generated for a rangefunc loop body, as provided in Alessandro Arzilli's CL (which is merged into this one). Fix return values for panicky range functions This removes the delayed implementation of "return x" by ensuring that return values (in rangefunc-return-containing functions) always have names and translating the "return x" into "#rv1 = x" where #rv1 is the synthesized name of the first result. Updates #61405. Change-Id: I933299ecce04ceabcf1c0c2de8e610b2ecd1cfd8 Reviewed-on: https://go-review.googlesource.com/c/go/+/584596 Reviewed-by: Matthew Dempsky <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Tim King <[email protected]>
1 parent 6438658 commit 5a9dabc

File tree

16 files changed

+1723
-572
lines changed

16 files changed

+1723
-572
lines changed

src/cmd/compile/internal/inline/inl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ opSwitch:
508508
case "throw":
509509
v.budget -= inlineExtraThrowCost
510510
break opSwitch
511-
case "panicrangeexit":
511+
case "panicrangestate":
512512
cheap = true
513513
}
514514
// Special case for reflect.noescape. It does just type

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

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,19 @@ type Func struct {
9090

9191
Inl *Inline
9292

93-
// funcLitGen and goDeferGen track how many closures have been
94-
// created in this function for function literals and go/defer
95-
// wrappers, respectively. Used by closureName for creating unique
96-
// function names.
97-
//
93+
// RangeParent, if non-nil, is the first non-range body function containing
94+
// the closure for the body of a range function.
95+
RangeParent *Func
96+
97+
// funcLitGen, rangeLitGen and goDeferGen track how many closures have been
98+
// created in this function for function literals, range-over-func loops,
99+
// and go/defer wrappers, respectively. Used by closureName for creating
100+
// unique function names.
98101
// Tracking goDeferGen separately avoids wrappers throwing off
99102
// function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11).
100-
funcLitGen int32
101-
goDeferGen int32
103+
funcLitGen int32
104+
rangeLitGen int32
105+
goDeferGen int32
102106

103107
Label int32 // largest auto-generated label in this function
104108

@@ -417,20 +421,25 @@ var globClosgen int32
417421

418422
// closureName generates a new unique name for a closure within outerfn at pos.
419423
func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
424+
if outerfn != nil && outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
425+
outerfn = outerfn.OClosure.Func.RangeParent
426+
}
420427
pkg := types.LocalPkg
421428
outer := "glob."
422-
var prefix string
429+
var prefix string = "."
423430
switch why {
424431
default:
425432
base.FatalfAt(pos, "closureName: bad Op: %v", why)
426433
case OCLOSURE:
427434
if outerfn == nil || outerfn.OClosure == nil {
428-
prefix = "func"
435+
prefix = ".func"
429436
}
437+
case ORANGE:
438+
prefix = "-range"
430439
case OGO:
431-
prefix = "gowrap"
440+
prefix = ".gowrap"
432441
case ODEFER:
433-
prefix = "deferwrap"
442+
prefix = ".deferwrap"
434443
}
435444
gen := &globClosgen
436445

@@ -441,9 +450,12 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
441450
pkg = outerfn.Sym().Pkg
442451
outer = FuncName(outerfn)
443452

444-
if why == OCLOSURE {
453+
switch why {
454+
case OCLOSURE:
445455
gen = &outerfn.funcLitGen
446-
} else {
456+
case ORANGE:
457+
gen = &outerfn.rangeLitGen
458+
default:
447459
gen = &outerfn.goDeferGen
448460
}
449461
}
@@ -460,7 +472,7 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
460472
}
461473

462474
*gen++
463-
return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
475+
return pkg.Lookup(fmt.Sprintf("%s%s%d", outer, prefix, *gen))
464476
}
465477

466478
// NewClosureFunc creates a new Func to represent a function literal
@@ -490,6 +502,12 @@ func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func,
490502
clo.pos = cpos
491503
clo.SetType(typ)
492504
clo.SetTypecheck(1)
505+
if why == ORANGE {
506+
clo.Func.RangeParent = outerfn
507+
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
508+
clo.Func.RangeParent = outerfn.OClosure.Func.RangeParent
509+
}
510+
}
493511
fn.OClosure = clo
494512

495513
fn.Nname.Defn = fn

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
2020
_32bit uintptr // size on 32bit platforms
2121
_64bit uintptr // size on 64bit platforms
2222
}{
23-
{Func{}, 168, 288},
23+
{Func{}, 176, 296},
2424
{Name{}, 96, 168},
2525
}
2626

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`)
2222

2323
// checkFiles configures and runs the types2 checker on the given
2424
// parsed source files and then returns the result.
25-
func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
25+
// The map result value indicates which closures are generated from the bodies of range function loops.
26+
func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info, map[*syntax.FuncLit]bool) {
2627
if base.SyntaxErrors() != 0 {
2728
base.ErrorExit()
2829
}
@@ -150,9 +151,9 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
150151
// If we do the rewrite in the back end, like between typecheck and walk,
151152
// then the new implicit closure will not have a unified IR inline body,
152153
// and bodyReaderFor will fail.
153-
rangefunc.Rewrite(pkg, info, files)
154+
rangeInfo := rangefunc.Rewrite(pkg, info, files)
154155

155-
return pkg, info
156+
return pkg, info, rangeInfo
156157
}
157158

158159
// A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,7 +2704,7 @@ func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack b
27042704
return false
27052705
}
27062706

2707-
fn := r.inlClosureFunc(origPos, typ)
2707+
fn := r.inlClosureFunc(origPos, typ, ir.OCLOSURE)
27082708
fn.SetWrapper(true)
27092709

27102710
clo := fn.OClosure
@@ -3035,8 +3035,12 @@ func (r *reader) funcLit() ir.Node {
30353035
origPos := r.pos()
30363036
sig := r.signature(nil)
30373037
r.suppressInlPos--
3038+
why := ir.OCLOSURE
3039+
if r.Bool() {
3040+
why = ir.ORANGE
3041+
}
30383042

3039-
fn := r.inlClosureFunc(origPos, sig)
3043+
fn := r.inlClosureFunc(origPos, sig, why)
30403044

30413045
fn.ClosureVars = make([]*ir.Name, 0, r.Len())
30423046
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
@@ -3062,14 +3066,14 @@ func (r *reader) funcLit() ir.Node {
30623066

30633067
// inlClosureFunc constructs a new closure function, but correctly
30643068
// handles inlining.
3065-
func (r *reader) inlClosureFunc(origPos src.XPos, sig *types.Type) *ir.Func {
3069+
func (r *reader) inlClosureFunc(origPos src.XPos, sig *types.Type, why ir.Op) *ir.Func {
30663070
curfn := r.inlCaller
30673071
if curfn == nil {
30683072
curfn = r.curfn
30693073
}
30703074

30713075
// TODO(mdempsky): Remove hard-coding of typecheck.Target.
3072-
return ir.NewClosureFunc(origPos, r.inlPos(origPos), ir.OCLOSURE, sig, curfn, typecheck.Target)
3076+
return ir.NewClosureFunc(origPos, r.inlPos(origPos), why, sig, curfn, typecheck.Target)
30733077
}
30743078

30753079
func (r *reader) exprList() []ir.Node {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ func readBodies(target *ir.Package, duringInlining bool) {
304304
// writes an export data package stub representing them,
305305
// and returns the result.
306306
func writePkgStub(m posMap, noders []*noder) string {
307-
pkg, info := checkFiles(m, noders)
307+
pkg, info, otherInfo := checkFiles(m, noders)
308308

309-
pw := newPkgWriter(m, pkg, info)
309+
pw := newPkgWriter(m, pkg, info, otherInfo)
310310

311311
pw.collectDecls(noders)
312312

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ import (
6363
type pkgWriter struct {
6464
pkgbits.PkgEncoder
6565

66-
m posMap
67-
curpkg *types2.Package
68-
info *types2.Info
66+
m posMap
67+
curpkg *types2.Package
68+
info *types2.Info
69+
rangeFuncBodyClosures map[*syntax.FuncLit]bool // non-public information, e.g., which functions are closures range function bodies?
6970

7071
// Indices for previously written syntax and types2 things.
7172

@@ -90,13 +91,14 @@ type pkgWriter struct {
9091

9192
// newPkgWriter returns an initialized pkgWriter for the specified
9293
// package.
93-
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
94+
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {
9495
return &pkgWriter{
9596
PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
9697

97-
m: m,
98-
curpkg: pkg,
99-
info: info,
98+
m: m,
99+
curpkg: pkg,
100+
info: info,
101+
rangeFuncBodyClosures: otherInfo,
100102

101103
pkgsIdx: make(map[*types2.Package]pkgbits.Index),
102104
objsIdx: make(map[types2.Object]pkgbits.Index),
@@ -2336,6 +2338,7 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
23362338
w.Sync(pkgbits.SyncFuncLit)
23372339
w.pos(expr)
23382340
w.signature(sig)
2341+
w.Bool(w.p.rangeFuncBodyClosures[expr])
23392342

23402343
w.Len(len(closureVars))
23412344
for _, cv := range closureVars {

0 commit comments

Comments
 (0)