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": case "throw":
v.budget -= inlineExtraThrowCost v.budget -= inlineExtraThrowCost
break opSwitch break opSwitch
case "panicrangeexit": case "panicrangestate":
cheap = true cheap = true
} }
// Special case for reflect.noescape. It does just type // Special case for reflect.noescape. It does just type

View file

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

View file

@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms _32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms _64bit uintptr // size on 64bit platforms
}{ }{
{Func{}, 168, 288}, {Func{}, 176, 296},
{Name{}, 96, 168}, {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 // checkFiles configures and runs the types2 checker on the given
// parsed source files and then returns the result. // 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 { if base.SyntaxErrors() != 0 {
base.ErrorExit() 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, // 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, // then the new implicit closure will not have a unified IR inline body,
// and bodyReaderFor will fail. // 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). // 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 return false
} }
fn := r.inlClosureFunc(origPos, typ) fn := r.inlClosureFunc(origPos, typ, ir.OCLOSURE)
fn.SetWrapper(true) fn.SetWrapper(true)
clo := fn.OClosure clo := fn.OClosure
@ -3035,8 +3035,12 @@ func (r *reader) funcLit() ir.Node {
origPos := r.pos() origPos := r.pos()
sig := r.signature(nil) sig := r.signature(nil)
r.suppressInlPos-- 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()) fn.ClosureVars = make([]*ir.Name, 0, r.Len())
for len(fn.ClosureVars) < cap(fn.ClosureVars) { 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 // inlClosureFunc constructs a new closure function, but correctly
// handles inlining. // 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 curfn := r.inlCaller
if curfn == nil { if curfn == nil {
curfn = r.curfn curfn = r.curfn
} }
// TODO(mdempsky): Remove hard-coding of typecheck.Target. // 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 { 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, // writes an export data package stub representing them,
// and returns the result. // and returns the result.
func writePkgStub(m posMap, noders []*noder) string { 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) pw.collectDecls(noders)

View file

@ -66,6 +66,7 @@ type pkgWriter struct {
m posMap m posMap
curpkg *types2.Package curpkg *types2.Package
info *types2.Info 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. // Indices for previously written syntax and types2 things.
@ -90,13 +91,14 @@ type pkgWriter struct {
// newPkgWriter returns an initialized pkgWriter for the specified // newPkgWriter returns an initialized pkgWriter for the specified
// package. // 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{ return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
m: m, m: m,
curpkg: pkg, curpkg: pkg,
info: info, info: info,
rangeFuncBodyClosures: otherInfo,
pkgsIdx: make(map[*types2.Package]pkgbits.Index), pkgsIdx: make(map[*types2.Package]pkgbits.Index),
objsIdx: make(map[types2.Object]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.Sync(pkgbits.SyncFuncLit)
w.pos(expr) w.pos(expr)
w.signature(sig) w.signature(sig)
w.Bool(w.p.rangeFuncBodyClosures[expr])
w.Len(len(closureVars)) w.Len(len(closureVars))
for _, cv := range 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) s.callResult(n, callNormal)
if n.Op() == ir.OCALLFUNC && n.Fun.Op() == ir.ONAME && n.Fun.(*ir.Name).Class == ir.PFUNC { 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" || 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() m := s.mem()
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockExit 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 ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
func efaceeq(typ *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 // panic for various rangefunc iterator errors
func panicrangeexit() func panicrangestate(state int)
// defer in range over func // defer in range over func
func deferrangefunc() interface{} func deferrangefunc() interface{}

View file

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

View file

@ -297,11 +297,31 @@ func deferproc(fn func()) {
// been set and must not be clobbered. // 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 //go:noinline
func panicrangeexit() { func panicrangestate(state int) {
panic(rangeExitError) 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 // 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)
}