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 <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Tim King <taking@google.com>
This commit is contained in:
David Chase 2024-01-26 17:49:33 -05:00
parent 643865856c
commit 5a9dabc2ba
16 changed files with 1721 additions and 570 deletions

View file

@ -508,7 +508,7 @@ opSwitch:
case "throw":
v.budget -= inlineExtraThrowCost
break opSwitch
case "panicrangeexit":
case "panicrangestate":
cheap = true
}
// Special case for reflect.noescape. It does just type

View file

@ -90,15 +90,19 @@ type Func struct {
Inl *Inline
// funcLitGen and goDeferGen track how many closures have been
// created in this function for function literals and go/defer
// wrappers, respectively. Used by closureName for creating unique
// function names.
//
// RangeParent, if non-nil, is the first non-range body function containing
// the closure for the body of a range function.
RangeParent *Func
// funcLitGen, rangeLitGen and goDeferGen track how many closures have been
// created in this function for function literals, range-over-func loops,
// and go/defer wrappers, respectively. Used by closureName for creating
// unique function names.
// Tracking goDeferGen separately avoids wrappers throwing off
// function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11).
funcLitGen int32
goDeferGen int32
funcLitGen int32
rangeLitGen int32
goDeferGen int32
Label int32 // largest auto-generated label in this function
@ -417,20 +421,25 @@ var globClosgen int32
// closureName generates a new unique name for a closure within outerfn at pos.
func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
if outerfn != nil && outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
outerfn = outerfn.OClosure.Func.RangeParent
}
pkg := types.LocalPkg
outer := "glob."
var prefix string
var prefix string = "."
switch why {
default:
base.FatalfAt(pos, "closureName: bad Op: %v", why)
case OCLOSURE:
if outerfn == nil || outerfn.OClosure == nil {
prefix = "func"
prefix = ".func"
}
case ORANGE:
prefix = "-range"
case OGO:
prefix = "gowrap"
prefix = ".gowrap"
case ODEFER:
prefix = "deferwrap"
prefix = ".deferwrap"
}
gen := &globClosgen
@ -441,9 +450,12 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
pkg = outerfn.Sym().Pkg
outer = FuncName(outerfn)
if why == OCLOSURE {
switch why {
case OCLOSURE:
gen = &outerfn.funcLitGen
} else {
case ORANGE:
gen = &outerfn.rangeLitGen
default:
gen = &outerfn.goDeferGen
}
}
@ -460,7 +472,7 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
}
*gen++
return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
return pkg.Lookup(fmt.Sprintf("%s%s%d", outer, prefix, *gen))
}
// 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,
clo.pos = cpos
clo.SetType(typ)
clo.SetTypecheck(1)
if why == ORANGE {
clo.Func.RangeParent = outerfn
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
clo.Func.RangeParent = outerfn.OClosure.Func.RangeParent
}
}
fn.OClosure = clo
fn.Nname.Defn = fn

View file

@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
{Func{}, 168, 288},
{Func{}, 176, 296},
{Name{}, 96, 168},
}

View file

@ -22,7 +22,8 @@ var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`)
// checkFiles configures and runs the types2 checker on the given
// parsed source files and then returns the result.
func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
// The map result value indicates which closures are generated from the bodies of range function loops.
func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info, map[*syntax.FuncLit]bool) {
if base.SyntaxErrors() != 0 {
base.ErrorExit()
}
@ -150,9 +151,9 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
// If we do the rewrite in the back end, like between typecheck and walk,
// then the new implicit closure will not have a unified IR inline body,
// and bodyReaderFor will fail.
rangefunc.Rewrite(pkg, info, files)
rangeInfo := rangefunc.Rewrite(pkg, info, files)
return pkg, info
return pkg, info, rangeInfo
}
// A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).

View file

@ -2704,7 +2704,7 @@ func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack b
return false
}
fn := r.inlClosureFunc(origPos, typ)
fn := r.inlClosureFunc(origPos, typ, ir.OCLOSURE)
fn.SetWrapper(true)
clo := fn.OClosure
@ -3035,8 +3035,12 @@ func (r *reader) funcLit() ir.Node {
origPos := r.pos()
sig := r.signature(nil)
r.suppressInlPos--
why := ir.OCLOSURE
if r.Bool() {
why = ir.ORANGE
}
fn := r.inlClosureFunc(origPos, sig)
fn := r.inlClosureFunc(origPos, sig, why)
fn.ClosureVars = make([]*ir.Name, 0, r.Len())
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
@ -3062,14 +3066,14 @@ func (r *reader) funcLit() ir.Node {
// inlClosureFunc constructs a new closure function, but correctly
// handles inlining.
func (r *reader) inlClosureFunc(origPos src.XPos, sig *types.Type) *ir.Func {
func (r *reader) inlClosureFunc(origPos src.XPos, sig *types.Type, why ir.Op) *ir.Func {
curfn := r.inlCaller
if curfn == nil {
curfn = r.curfn
}
// TODO(mdempsky): Remove hard-coding of typecheck.Target.
return ir.NewClosureFunc(origPos, r.inlPos(origPos), ir.OCLOSURE, sig, curfn, typecheck.Target)
return ir.NewClosureFunc(origPos, r.inlPos(origPos), why, sig, curfn, typecheck.Target)
}
func (r *reader) exprList() []ir.Node {

View file

@ -304,9 +304,9 @@ func readBodies(target *ir.Package, duringInlining bool) {
// writes an export data package stub representing them,
// and returns the result.
func writePkgStub(m posMap, noders []*noder) string {
pkg, info := checkFiles(m, noders)
pkg, info, otherInfo := checkFiles(m, noders)
pw := newPkgWriter(m, pkg, info)
pw := newPkgWriter(m, pkg, info, otherInfo)
pw.collectDecls(noders)

View file

@ -63,9 +63,10 @@ import (
type pkgWriter struct {
pkgbits.PkgEncoder
m posMap
curpkg *types2.Package
info *types2.Info
m posMap
curpkg *types2.Package
info *types2.Info
rangeFuncBodyClosures map[*syntax.FuncLit]bool // non-public information, e.g., which functions are closures range function bodies?
// Indices for previously written syntax and types2 things.
@ -90,13 +91,14 @@ type pkgWriter struct {
// newPkgWriter returns an initialized pkgWriter for the specified
// package.
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {
return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
m: m,
curpkg: pkg,
info: info,
m: m,
curpkg: pkg,
info: info,
rangeFuncBodyClosures: otherInfo,
pkgsIdx: make(map[*types2.Package]pkgbits.Index),
objsIdx: make(map[types2.Object]pkgbits.Index),
@ -2336,6 +2338,7 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
w.Sync(pkgbits.SyncFuncLit)
w.pos(expr)
w.signature(sig)
w.Bool(w.p.rangeFuncBodyClosures[expr])
w.Len(len(closureVars))
for _, cv := range closureVars {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1461,7 +1461,11 @@ func (s *state) stmt(n ir.Node) {
s.callResult(n, callNormal)
if n.Op() == ir.OCALLFUNC && n.Fun.Op() == ir.ONAME && n.Fun.(*ir.Name).Class == ir.PFUNC {
if fn := n.Fun.Sym().Name; base.Flag.CompilingRuntime && fn == "throw" ||
n.Fun.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap" || fn == "panicunsafeslicelen" || fn == "panicunsafeslicenilptr" || fn == "panicunsafestringlen" || fn == "panicunsafestringnilptr") {
n.Fun.Sym().Pkg == ir.Pkgs.Runtime &&
(fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" ||
fn == "panicmakeslicelen" || fn == "panicmakeslicecap" || fn == "panicunsafeslicelen" ||
fn == "panicunsafeslicenilptr" || fn == "panicunsafestringlen" || fn == "panicunsafestringnilptr" ||
fn == "panicrangestate") {
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockExit

View file

@ -117,8 +117,8 @@ func interfaceSwitch(s *byte, t *byte) (int, *byte)
func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
// panic for iteration after exit in range func
func panicrangeexit()
// panic for various rangefunc iterator errors
func panicrangestate(state int)
// defer in range over func
func deferrangefunc() interface{}

View file

@ -102,142 +102,142 @@ var runtimeDecls = [...]struct {
{"interfaceSwitch", funcTag, 70},
{"ifaceeq", funcTag, 72},
{"efaceeq", funcTag, 72},
{"panicrangeexit", funcTag, 9},
{"deferrangefunc", funcTag, 73},
{"rand32", funcTag, 74},
{"makemap64", funcTag, 76},
{"makemap", funcTag, 77},
{"makemap_small", funcTag, 78},
{"mapaccess1", funcTag, 79},
{"mapaccess1_fast32", funcTag, 80},
{"mapaccess1_fast64", funcTag, 81},
{"mapaccess1_faststr", funcTag, 82},
{"mapaccess1_fat", funcTag, 83},
{"mapaccess2", funcTag, 84},
{"mapaccess2_fast32", funcTag, 85},
{"mapaccess2_fast64", funcTag, 86},
{"mapaccess2_faststr", funcTag, 87},
{"mapaccess2_fat", funcTag, 88},
{"mapassign", funcTag, 79},
{"mapassign_fast32", funcTag, 80},
{"mapassign_fast32ptr", funcTag, 89},
{"mapassign_fast64", funcTag, 81},
{"mapassign_fast64ptr", funcTag, 89},
{"mapassign_faststr", funcTag, 82},
{"mapiterinit", funcTag, 90},
{"mapdelete", funcTag, 90},
{"mapdelete_fast32", funcTag, 91},
{"mapdelete_fast64", funcTag, 92},
{"mapdelete_faststr", funcTag, 93},
{"mapiternext", funcTag, 94},
{"mapclear", funcTag, 95},
{"makechan64", funcTag, 97},
{"makechan", funcTag, 98},
{"chanrecv1", funcTag, 100},
{"chanrecv2", funcTag, 101},
{"chansend1", funcTag, 103},
{"closechan", funcTag, 104},
{"chanlen", funcTag, 105},
{"chancap", funcTag, 105},
{"writeBarrier", varTag, 107},
{"typedmemmove", funcTag, 108},
{"typedmemclr", funcTag, 109},
{"typedslicecopy", funcTag, 110},
{"selectnbsend", funcTag, 111},
{"selectnbrecv", funcTag, 112},
{"selectsetpc", funcTag, 113},
{"selectgo", funcTag, 114},
{"panicrangestate", funcTag, 73},
{"deferrangefunc", funcTag, 74},
{"rand32", funcTag, 75},
{"makemap64", funcTag, 77},
{"makemap", funcTag, 78},
{"makemap_small", funcTag, 79},
{"mapaccess1", funcTag, 80},
{"mapaccess1_fast32", funcTag, 81},
{"mapaccess1_fast64", funcTag, 82},
{"mapaccess1_faststr", funcTag, 83},
{"mapaccess1_fat", funcTag, 84},
{"mapaccess2", funcTag, 85},
{"mapaccess2_fast32", funcTag, 86},
{"mapaccess2_fast64", funcTag, 87},
{"mapaccess2_faststr", funcTag, 88},
{"mapaccess2_fat", funcTag, 89},
{"mapassign", funcTag, 80},
{"mapassign_fast32", funcTag, 81},
{"mapassign_fast32ptr", funcTag, 90},
{"mapassign_fast64", funcTag, 82},
{"mapassign_fast64ptr", funcTag, 90},
{"mapassign_faststr", funcTag, 83},
{"mapiterinit", funcTag, 91},
{"mapdelete", funcTag, 91},
{"mapdelete_fast32", funcTag, 92},
{"mapdelete_fast64", funcTag, 93},
{"mapdelete_faststr", funcTag, 94},
{"mapiternext", funcTag, 95},
{"mapclear", funcTag, 96},
{"makechan64", funcTag, 98},
{"makechan", funcTag, 99},
{"chanrecv1", funcTag, 101},
{"chanrecv2", funcTag, 102},
{"chansend1", funcTag, 104},
{"closechan", funcTag, 105},
{"chanlen", funcTag, 106},
{"chancap", funcTag, 106},
{"writeBarrier", varTag, 108},
{"typedmemmove", funcTag, 109},
{"typedmemclr", funcTag, 110},
{"typedslicecopy", funcTag, 111},
{"selectnbsend", funcTag, 112},
{"selectnbrecv", funcTag, 113},
{"selectsetpc", funcTag, 114},
{"selectgo", funcTag, 115},
{"block", funcTag, 9},
{"makeslice", funcTag, 115},
{"makeslice64", funcTag, 116},
{"makeslicecopy", funcTag, 117},
{"growslice", funcTag, 119},
{"unsafeslicecheckptr", funcTag, 120},
{"makeslice", funcTag, 116},
{"makeslice64", funcTag, 117},
{"makeslicecopy", funcTag, 118},
{"growslice", funcTag, 120},
{"unsafeslicecheckptr", funcTag, 121},
{"panicunsafeslicelen", funcTag, 9},
{"panicunsafeslicenilptr", funcTag, 9},
{"unsafestringcheckptr", funcTag, 121},
{"unsafestringcheckptr", funcTag, 122},
{"panicunsafestringlen", funcTag, 9},
{"panicunsafestringnilptr", funcTag, 9},
{"memmove", funcTag, 122},
{"memclrNoHeapPointers", funcTag, 123},
{"memclrHasPointers", funcTag, 123},
{"memequal", funcTag, 124},
{"memequal0", funcTag, 125},
{"memequal8", funcTag, 125},
{"memequal16", funcTag, 125},
{"memequal32", funcTag, 125},
{"memequal64", funcTag, 125},
{"memequal128", funcTag, 125},
{"f32equal", funcTag, 126},
{"f64equal", funcTag, 126},
{"c64equal", funcTag, 126},
{"c128equal", funcTag, 126},
{"strequal", funcTag, 126},
{"interequal", funcTag, 126},
{"nilinterequal", funcTag, 126},
{"memhash", funcTag, 127},
{"memhash0", funcTag, 128},
{"memhash8", funcTag, 128},
{"memhash16", funcTag, 128},
{"memhash32", funcTag, 128},
{"memhash64", funcTag, 128},
{"memhash128", funcTag, 128},
{"f32hash", funcTag, 129},
{"f64hash", funcTag, 129},
{"c64hash", funcTag, 129},
{"c128hash", funcTag, 129},
{"strhash", funcTag, 129},
{"interhash", funcTag, 129},
{"nilinterhash", funcTag, 129},
{"int64div", funcTag, 130},
{"uint64div", funcTag, 131},
{"int64mod", funcTag, 130},
{"uint64mod", funcTag, 131},
{"float64toint64", funcTag, 132},
{"float64touint64", funcTag, 133},
{"float64touint32", funcTag, 134},
{"int64tofloat64", funcTag, 135},
{"int64tofloat32", funcTag, 137},
{"uint64tofloat64", funcTag, 138},
{"uint64tofloat32", funcTag, 139},
{"uint32tofloat64", funcTag, 140},
{"complex128div", funcTag, 141},
{"getcallerpc", funcTag, 142},
{"getcallersp", funcTag, 142},
{"memmove", funcTag, 123},
{"memclrNoHeapPointers", funcTag, 124},
{"memclrHasPointers", funcTag, 124},
{"memequal", funcTag, 125},
{"memequal0", funcTag, 126},
{"memequal8", funcTag, 126},
{"memequal16", funcTag, 126},
{"memequal32", funcTag, 126},
{"memequal64", funcTag, 126},
{"memequal128", funcTag, 126},
{"f32equal", funcTag, 127},
{"f64equal", funcTag, 127},
{"c64equal", funcTag, 127},
{"c128equal", funcTag, 127},
{"strequal", funcTag, 127},
{"interequal", funcTag, 127},
{"nilinterequal", funcTag, 127},
{"memhash", funcTag, 128},
{"memhash0", funcTag, 129},
{"memhash8", funcTag, 129},
{"memhash16", funcTag, 129},
{"memhash32", funcTag, 129},
{"memhash64", funcTag, 129},
{"memhash128", funcTag, 129},
{"f32hash", funcTag, 130},
{"f64hash", funcTag, 130},
{"c64hash", funcTag, 130},
{"c128hash", funcTag, 130},
{"strhash", funcTag, 130},
{"interhash", funcTag, 130},
{"nilinterhash", funcTag, 130},
{"int64div", funcTag, 131},
{"uint64div", funcTag, 132},
{"int64mod", funcTag, 131},
{"uint64mod", funcTag, 132},
{"float64toint64", funcTag, 133},
{"float64touint64", funcTag, 134},
{"float64touint32", funcTag, 135},
{"int64tofloat64", funcTag, 136},
{"int64tofloat32", funcTag, 138},
{"uint64tofloat64", funcTag, 139},
{"uint64tofloat32", funcTag, 140},
{"uint32tofloat64", funcTag, 141},
{"complex128div", funcTag, 142},
{"getcallerpc", funcTag, 143},
{"getcallersp", funcTag, 143},
{"racefuncenter", funcTag, 31},
{"racefuncexit", funcTag, 9},
{"raceread", funcTag, 31},
{"racewrite", funcTag, 31},
{"racereadrange", funcTag, 143},
{"racewriterange", funcTag, 143},
{"msanread", funcTag, 143},
{"msanwrite", funcTag, 143},
{"msanmove", funcTag, 144},
{"asanread", funcTag, 143},
{"asanwrite", funcTag, 143},
{"checkptrAlignment", funcTag, 145},
{"checkptrArithmetic", funcTag, 147},
{"libfuzzerTraceCmp1", funcTag, 148},
{"libfuzzerTraceCmp2", funcTag, 149},
{"libfuzzerTraceCmp4", funcTag, 150},
{"libfuzzerTraceCmp8", funcTag, 151},
{"libfuzzerTraceConstCmp1", funcTag, 148},
{"libfuzzerTraceConstCmp2", funcTag, 149},
{"libfuzzerTraceConstCmp4", funcTag, 150},
{"libfuzzerTraceConstCmp8", funcTag, 151},
{"libfuzzerHookStrCmp", funcTag, 152},
{"libfuzzerHookEqualFold", funcTag, 152},
{"addCovMeta", funcTag, 154},
{"racereadrange", funcTag, 144},
{"racewriterange", funcTag, 144},
{"msanread", funcTag, 144},
{"msanwrite", funcTag, 144},
{"msanmove", funcTag, 145},
{"asanread", funcTag, 144},
{"asanwrite", funcTag, 144},
{"checkptrAlignment", funcTag, 146},
{"checkptrArithmetic", funcTag, 148},
{"libfuzzerTraceCmp1", funcTag, 149},
{"libfuzzerTraceCmp2", funcTag, 150},
{"libfuzzerTraceCmp4", funcTag, 151},
{"libfuzzerTraceCmp8", funcTag, 152},
{"libfuzzerTraceConstCmp1", funcTag, 149},
{"libfuzzerTraceConstCmp2", funcTag, 150},
{"libfuzzerTraceConstCmp4", funcTag, 151},
{"libfuzzerTraceConstCmp8", funcTag, 152},
{"libfuzzerHookStrCmp", funcTag, 153},
{"libfuzzerHookEqualFold", funcTag, 153},
{"addCovMeta", funcTag, 155},
{"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6},
{"armHasVFPv4", varTag, 6},
{"arm64HasATOMICS", varTag, 6},
{"asanregisterglobals", funcTag, 123},
{"asanregisterglobals", funcTag, 124},
}
func runtimeTypes() []*types.Type {
var typs [155]*types.Type
var typs [156]*types.Type
typs[0] = types.ByteType
typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[types.TANY]
@ -311,88 +311,89 @@ func runtimeTypes() []*types.Type {
typs[70] = newSig(params(typs[1], typs[1]), params(typs[15], typs[1]))
typs[71] = types.NewPtr(typs[5])
typs[72] = newSig(params(typs[71], typs[7], typs[7]), params(typs[6]))
typs[73] = newSig(nil, params(typs[10]))
typs[74] = newSig(nil, params(typs[60]))
typs[75] = types.NewMap(typs[2], typs[2])
typs[76] = newSig(params(typs[1], typs[22], typs[3]), params(typs[75]))
typs[77] = newSig(params(typs[1], typs[15], typs[3]), params(typs[75]))
typs[78] = newSig(nil, params(typs[75]))
typs[79] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3]))
typs[80] = newSig(params(typs[1], typs[75], typs[60]), params(typs[3]))
typs[81] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3]))
typs[82] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3]))
typs[83] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3]))
typs[84] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3], typs[6]))
typs[85] = newSig(params(typs[1], typs[75], typs[60]), params(typs[3], typs[6]))
typs[86] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3], typs[6]))
typs[87] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3], typs[6]))
typs[88] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3], typs[6]))
typs[89] = newSig(params(typs[1], typs[75], typs[7]), params(typs[3]))
typs[90] = newSig(params(typs[1], typs[75], typs[3]), nil)
typs[91] = newSig(params(typs[1], typs[75], typs[60]), nil)
typs[92] = newSig(params(typs[1], typs[75], typs[24]), nil)
typs[93] = newSig(params(typs[1], typs[75], typs[28]), nil)
typs[94] = newSig(params(typs[3]), nil)
typs[95] = newSig(params(typs[1], typs[75]), nil)
typs[96] = types.NewChan(typs[2], types.Cboth)
typs[97] = newSig(params(typs[1], typs[22]), params(typs[96]))
typs[98] = newSig(params(typs[1], typs[15]), params(typs[96]))
typs[99] = types.NewChan(typs[2], types.Crecv)
typs[100] = newSig(params(typs[99], typs[3]), nil)
typs[101] = newSig(params(typs[99], typs[3]), params(typs[6]))
typs[102] = types.NewChan(typs[2], types.Csend)
typs[103] = newSig(params(typs[102], typs[3]), nil)
typs[104] = newSig(params(typs[102]), nil)
typs[105] = newSig(params(typs[2]), params(typs[15]))
typs[106] = types.NewArray(typs[0], 3)
typs[107] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[106]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
typs[108] = newSig(params(typs[1], typs[3], typs[3]), nil)
typs[109] = newSig(params(typs[1], typs[3]), nil)
typs[110] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
typs[111] = newSig(params(typs[102], typs[3]), params(typs[6]))
typs[112] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6]))
typs[113] = newSig(params(typs[71]), nil)
typs[114] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
typs[115] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
typs[116] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
typs[117] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[118] = types.NewSlice(typs[2])
typs[119] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[118]))
typs[120] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[121] = newSig(params(typs[7], typs[22]), nil)
typs[122] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[123] = newSig(params(typs[7], typs[5]), nil)
typs[124] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
typs[125] = newSig(params(typs[3], typs[3]), params(typs[6]))
typs[126] = newSig(params(typs[7], typs[7]), params(typs[6]))
typs[127] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
typs[128] = newSig(params(typs[7], typs[5]), params(typs[5]))
typs[129] = newSig(params(typs[3], typs[5]), params(typs[5]))
typs[130] = newSig(params(typs[22], typs[22]), params(typs[22]))
typs[131] = newSig(params(typs[24], typs[24]), params(typs[24]))
typs[132] = newSig(params(typs[20]), params(typs[22]))
typs[133] = newSig(params(typs[20]), params(typs[24]))
typs[134] = newSig(params(typs[20]), params(typs[60]))
typs[135] = newSig(params(typs[22]), params(typs[20]))
typs[136] = types.Types[types.TFLOAT32]
typs[137] = newSig(params(typs[22]), params(typs[136]))
typs[138] = newSig(params(typs[24]), params(typs[20]))
typs[139] = newSig(params(typs[24]), params(typs[136]))
typs[140] = newSig(params(typs[60]), params(typs[20]))
typs[141] = newSig(params(typs[26], typs[26]), params(typs[26]))
typs[142] = newSig(nil, params(typs[5]))
typs[143] = newSig(params(typs[5], typs[5]), nil)
typs[144] = newSig(params(typs[5], typs[5], typs[5]), nil)
typs[145] = newSig(params(typs[7], typs[1], typs[5]), nil)
typs[146] = types.NewSlice(typs[7])
typs[147] = newSig(params(typs[7], typs[146]), nil)
typs[148] = newSig(params(typs[64], typs[64], typs[17]), nil)
typs[149] = newSig(params(typs[58], typs[58], typs[17]), nil)
typs[150] = newSig(params(typs[60], typs[60], typs[17]), nil)
typs[151] = newSig(params(typs[24], typs[24], typs[17]), nil)
typs[152] = newSig(params(typs[28], typs[28], typs[17]), nil)
typs[153] = types.NewArray(typs[0], 16)
typs[154] = newSig(params(typs[7], typs[60], typs[153], typs[28], typs[15], typs[64], typs[64]), params(typs[60]))
typs[73] = newSig(params(typs[15]), nil)
typs[74] = newSig(nil, params(typs[10]))
typs[75] = newSig(nil, params(typs[60]))
typs[76] = types.NewMap(typs[2], typs[2])
typs[77] = newSig(params(typs[1], typs[22], typs[3]), params(typs[76]))
typs[78] = newSig(params(typs[1], typs[15], typs[3]), params(typs[76]))
typs[79] = newSig(nil, params(typs[76]))
typs[80] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3]))
typs[81] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3]))
typs[82] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3]))
typs[83] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3]))
typs[84] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3]))
typs[85] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3], typs[6]))
typs[86] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3], typs[6]))
typs[87] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3], typs[6]))
typs[88] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3], typs[6]))
typs[89] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3], typs[6]))
typs[90] = newSig(params(typs[1], typs[76], typs[7]), params(typs[3]))
typs[91] = newSig(params(typs[1], typs[76], typs[3]), nil)
typs[92] = newSig(params(typs[1], typs[76], typs[60]), nil)
typs[93] = newSig(params(typs[1], typs[76], typs[24]), nil)
typs[94] = newSig(params(typs[1], typs[76], typs[28]), nil)
typs[95] = newSig(params(typs[3]), nil)
typs[96] = newSig(params(typs[1], typs[76]), nil)
typs[97] = types.NewChan(typs[2], types.Cboth)
typs[98] = newSig(params(typs[1], typs[22]), params(typs[97]))
typs[99] = newSig(params(typs[1], typs[15]), params(typs[97]))
typs[100] = types.NewChan(typs[2], types.Crecv)
typs[101] = newSig(params(typs[100], typs[3]), nil)
typs[102] = newSig(params(typs[100], typs[3]), params(typs[6]))
typs[103] = types.NewChan(typs[2], types.Csend)
typs[104] = newSig(params(typs[103], typs[3]), nil)
typs[105] = newSig(params(typs[103]), nil)
typs[106] = newSig(params(typs[2]), params(typs[15]))
typs[107] = types.NewArray(typs[0], 3)
typs[108] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[107]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
typs[109] = newSig(params(typs[1], typs[3], typs[3]), nil)
typs[110] = newSig(params(typs[1], typs[3]), nil)
typs[111] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
typs[112] = newSig(params(typs[103], typs[3]), params(typs[6]))
typs[113] = newSig(params(typs[3], typs[100]), params(typs[6], typs[6]))
typs[114] = newSig(params(typs[71]), nil)
typs[115] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
typs[116] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
typs[117] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
typs[118] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[119] = types.NewSlice(typs[2])
typs[120] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[119]))
typs[121] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[122] = newSig(params(typs[7], typs[22]), nil)
typs[123] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[124] = newSig(params(typs[7], typs[5]), nil)
typs[125] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
typs[126] = newSig(params(typs[3], typs[3]), params(typs[6]))
typs[127] = newSig(params(typs[7], typs[7]), params(typs[6]))
typs[128] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
typs[129] = newSig(params(typs[7], typs[5]), params(typs[5]))
typs[130] = newSig(params(typs[3], typs[5]), params(typs[5]))
typs[131] = newSig(params(typs[22], typs[22]), params(typs[22]))
typs[132] = newSig(params(typs[24], typs[24]), params(typs[24]))
typs[133] = newSig(params(typs[20]), params(typs[22]))
typs[134] = newSig(params(typs[20]), params(typs[24]))
typs[135] = newSig(params(typs[20]), params(typs[60]))
typs[136] = newSig(params(typs[22]), params(typs[20]))
typs[137] = types.Types[types.TFLOAT32]
typs[138] = newSig(params(typs[22]), params(typs[137]))
typs[139] = newSig(params(typs[24]), params(typs[20]))
typs[140] = newSig(params(typs[24]), params(typs[137]))
typs[141] = newSig(params(typs[60]), params(typs[20]))
typs[142] = newSig(params(typs[26], typs[26]), params(typs[26]))
typs[143] = newSig(nil, params(typs[5]))
typs[144] = newSig(params(typs[5], typs[5]), nil)
typs[145] = newSig(params(typs[5], typs[5], typs[5]), nil)
typs[146] = newSig(params(typs[7], typs[1], typs[5]), nil)
typs[147] = types.NewSlice(typs[7])
typs[148] = newSig(params(typs[7], typs[147]), nil)
typs[149] = newSig(params(typs[64], typs[64], typs[17]), nil)
typs[150] = newSig(params(typs[58], typs[58], typs[17]), nil)
typs[151] = newSig(params(typs[60], typs[60], typs[17]), nil)
typs[152] = newSig(params(typs[24], typs[24], typs[17]), nil)
typs[153] = newSig(params(typs[28], typs[28], typs[17]), nil)
typs[154] = types.NewArray(typs[0], 16)
typs[155] = newSig(params(typs[7], typs[60], typs[154], typs[28], typs[15], typs[64], typs[64]), params(typs[60]))
return typs[:]
}

View file

@ -0,0 +1,50 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types2
import (
"cmd/compile/internal/syntax"
"fmt"
)
// This file should not be copied to go/types. See go.dev/issue/67477
// RenameResult takes an array of (result) fields and an index, and if the indexed field
// does not have a name and if the result in the signature also does not have a name,
// then the signature and field are renamed to
//
// fmt.Sprintf("#rv%d", i+1)`
//
// the newly named object is inserted into the signature's scope,
// and the object and new field name are returned.
//
// The intended use for RenameResult is to allow rangefunc to assign results within a closure.
// This is a hack, as narrowly targeted as possible to discourage abuse.
func (s *Signature) RenameResult(results []*syntax.Field, i int) (*Var, *syntax.Name) {
a := results[i]
obj := s.Results().At(i)
if !(obj.name == "" || obj.name == "_" && a.Name == nil || a.Name.Value == "_") {
panic("Cannot change an existing name")
}
pos := a.Pos()
typ := a.Type.GetTypeInfo().Type
name := fmt.Sprintf("#rv%d", i+1)
obj.name = name
s.scope.Insert(obj)
obj.setScopePos(pos)
tv := syntax.TypeAndValue{Type: typ}
tv.SetIsValue()
n := syntax.NewName(pos, obj.Name())
n.SetTypeInfo(tv)
a.Name = n
return obj, n
}

View file

@ -81,7 +81,7 @@ var builtins = [...]struct {
{"runtime.interfaceSwitch", 1},
{"runtime.ifaceeq", 1},
{"runtime.efaceeq", 1},
{"runtime.panicrangeexit", 1},
{"runtime.panicrangestate", 1},
{"runtime.deferrangefunc", 1},
{"runtime.rand32", 1},
{"runtime.makemap64", 1},
@ -116,6 +116,8 @@ var builtins = [...]struct {
{"runtime.chanrecv2", 1},
{"runtime.chansend1", 1},
{"runtime.closechan", 1},
{"runtime.chanlen", 1},
{"runtime.chancap", 1},
{"runtime.writeBarrier", 0},
{"runtime.typedmemmove", 1},
{"runtime.typedmemclr", 1},

View file

@ -297,11 +297,31 @@ func deferproc(fn func()) {
// been set and must not be clobbered.
}
var rangeExitError = error(errorString("range function continued iteration after exit"))
var rangeDoneError = error(errorString("range function continued iteration after loop body exit"))
var rangePanicError = error(errorString("range function continued iteration after loop body panic"))
var rangeExhaustedError = error(errorString("range function continued iteration after whole loop exit"))
var rangeMissingPanicError = error(errorString("range function recovered a loop body panic and did not resume panicking"))
//go:noinline
func panicrangeexit() {
panic(rangeExitError)
func panicrangestate(state int) {
const (
// These duplicate magic numbers in cmd/compile/internal/rangefunc
DONE = 0 // body of loop has exited in a non-panic way
PANIC = 2 // body of loop is either currently running, or has panicked
EXHAUSTED = 3 // iterator function return, i.e., sequence is "exhausted"
MISSING_PANIC = 4 // body of loop panicked but iterator function defer-recovered it away
)
switch state {
case DONE:
panic(rangeDoneError)
case PANIC:
panic(rangePanicError)
case EXHAUSTED:
panic(rangeExhaustedError)
case MISSING_PANIC:
panic(rangeMissingPanicError)
}
throw("unexpected state passed to panicrangestate")
}
// deferrangefunc is called by functions that are about to

View file

@ -0,0 +1,77 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build goexperiment.rangefunc
package race_test
import (
"runtime"
"sync/atomic"
"testing"
)
type Seq2[T1, T2 any] func(yield func(T1, T2) bool)
// ofSliceIndex returns a Seq over the elements of s. It is equivalent
// to range s, except that it splits s into two halves and iterates
// in two separate goroutines. This is racy if yield is racy, and yield
// will be racy if it contains an early exit.
func ofSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
return func(yield func(int, T) bool) {
c := make(chan bool, 2)
var done atomic.Bool
go func() {
for i := 0; i < len(s)/2; i++ {
if !done.Load() && !yield(i, s[i]) {
done.Store(true)
c <- false
}
}
c <- true
}()
go func() {
for i := len(s) / 2; i < len(s); i++ {
if !done.Load() && !yield(i, s[i]) {
done.Store(true)
c <- false
}
}
c <- true
return
}()
if !<-c {
return
}
<-c
}
}
// foo is racy, or not, depending on the value of v
// (0-4 == racy, otherwise, not racy).
func foo(v int) int64 {
var asum atomic.Int64
for i, x := range ofSliceIndex([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
if i%5 == v {
break
}
asum.Add(x) // don't race on asum
runtime.Gosched()
}
return 100 + asum.Load()
}
// TestRaceRangeFuncIterator races because x%5 can be equal to 4,
// therefore foo can early exit.
func TestRaceRangeFuncIterator(t *testing.T) {
x := foo(4)
t.Logf("foo(4)=%d", x)
}
// TestNoRaceRangeFuncIterator does not race because x%5 is never 5,
// therefore foo's loop will not exit early, and this it will not race.
func TestNoRaceRangeFuncIterator(t *testing.T) {
x := foo(5)
t.Logf("foo(5)=%d", x)
}