mirror of
https://github.com/golang/go.git
synced 2024-09-21 10:28:27 +00:00
cmd/compile: implement range over func
Add compiler support for range over functions. See the large comment at the top of cmd/compile/internal/rangefunc/rewrite.go for details. This is only reachable if GOEXPERIMENT=range is set, because otherwise type checking will fail. For proposal #61405 (but behind a GOEXPERIMENT). For #61717. Change-Id: I05717f94e63089c503acc49b28b47edeb4e011b4 Reviewed-on: https://go-review.googlesource.com/c/go/+/510541 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Auto-Submit: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
a94347a05c
commit
2fba42cb52
@ -185,7 +185,7 @@ func (e *escape) call(ks []hole, call ir.Node) {
|
||||
// goDeferStmt analyzes a "go" or "defer" statement.
|
||||
func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
|
||||
k := e.heapHole()
|
||||
if n.Op() == ir.ODEFER && e.loopDepth == 1 {
|
||||
if n.Op() == ir.ODEFER && e.loopDepth == 1 && n.DeferAt == nil {
|
||||
// Top-level defer arguments don't escape to the heap,
|
||||
// but they do need to last until they're invoked.
|
||||
k = e.later(e.discardHole())
|
||||
|
@ -186,6 +186,7 @@ type CallExpr struct {
|
||||
miniExpr
|
||||
X Node
|
||||
Args Nodes
|
||||
DeferAt Node
|
||||
RType Node `mknode:"-"` // see reflectdata/helpers.go
|
||||
KeepAlive []*Name // vars to be kept alive until call returns
|
||||
IsDDD bool
|
||||
|
@ -242,7 +242,8 @@ func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node, distinctV
|
||||
// in a different context (a separate goroutine or a later time).
|
||||
type GoDeferStmt struct {
|
||||
miniStmt
|
||||
Call Node
|
||||
Call Node
|
||||
DeferAt Expr
|
||||
}
|
||||
|
||||
func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
|
||||
|
@ -10,7 +10,9 @@ import (
|
||||
)
|
||||
|
||||
// Syms holds known symbols.
|
||||
var Syms struct {
|
||||
var Syms symsStruct
|
||||
|
||||
type symsStruct struct {
|
||||
AssertE2I *obj.LSym
|
||||
AssertE2I2 *obj.LSym
|
||||
AssertI2I *obj.LSym
|
||||
@ -21,6 +23,7 @@ var Syms struct {
|
||||
CgoCheckPtrWrite *obj.LSym
|
||||
CheckPtrAlignment *obj.LSym
|
||||
Deferproc *obj.LSym
|
||||
Deferprocat *obj.LSym
|
||||
DeferprocStack *obj.LSym
|
||||
Deferreturn *obj.LSym
|
||||
Duffcopy *obj.LSym
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/rangefunc"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
@ -70,6 +71,10 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
|
||||
}
|
||||
|
||||
pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
|
||||
base.ExitIfErrors()
|
||||
if err != nil {
|
||||
base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
|
||||
}
|
||||
|
||||
// Check for anonymous interface cycles (#56103).
|
||||
if base.Debug.InterfaceCycles == 0 {
|
||||
@ -90,6 +95,7 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
|
||||
})
|
||||
}
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
|
||||
// Implementation restriction: we don't allow not-in-heap types to
|
||||
// be used as type arguments (#54765).
|
||||
@ -115,11 +121,16 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
|
||||
base.ErrorfAt(targ.pos, 0, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ)
|
||||
}
|
||||
}
|
||||
|
||||
base.ExitIfErrors()
|
||||
if err != nil {
|
||||
base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
|
||||
}
|
||||
|
||||
// Rewrite range over function to explicit function calls
|
||||
// with the loop bodies converted into new implicit closures.
|
||||
// We do this now, before serialization to unified IR, so that if the
|
||||
// implicit closures are inlined, we will have the unified IR form.
|
||||
// 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)
|
||||
|
||||
return pkg, info
|
||||
}
|
||||
|
@ -677,6 +677,9 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
||||
if pri, ok := objReader[sym]; ok {
|
||||
return pri.pr.objIdx(pri.idx, nil, explicits, shaped)
|
||||
}
|
||||
if sym.Pkg.Path == "runtime" {
|
||||
return typecheck.LookupRuntime(sym.Name)
|
||||
}
|
||||
base.Fatalf("unresolved stub: %v", sym)
|
||||
}
|
||||
|
||||
@ -1646,7 +1649,14 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
|
||||
pos := r.pos()
|
||||
op := r.op()
|
||||
call := r.expr()
|
||||
return ir.NewGoDeferStmt(pos, op, call)
|
||||
stmt := ir.NewGoDeferStmt(pos, op, call)
|
||||
if op == ir.ODEFER {
|
||||
x := r.optExpr()
|
||||
if x != nil {
|
||||
stmt.DeferAt = x.(ir.Expr)
|
||||
}
|
||||
}
|
||||
return stmt
|
||||
|
||||
case stmtExpr:
|
||||
return r.expr()
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"go/token"
|
||||
"internal/buildcfg"
|
||||
"internal/pkgbits"
|
||||
"os"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
@ -1055,6 +1056,9 @@ func (w *writer) funcExt(obj *types2.Func) {
|
||||
|
||||
sig, block := obj.Type().(*types2.Signature), decl.Body
|
||||
body, closureVars := w.p.bodyIdx(sig, block, w.dict)
|
||||
if len(closureVars) > 0 {
|
||||
fmt.Fprintln(os.Stderr, "CLOSURE", closureVars)
|
||||
}
|
||||
assert(len(closureVars) == 0)
|
||||
|
||||
w.Sync(pkgbits.SyncFuncExt)
|
||||
@ -1266,6 +1270,9 @@ func (w *writer) stmt1(stmt syntax.Stmt) {
|
||||
w.pos(stmt)
|
||||
w.op(callOps[stmt.Tok])
|
||||
w.expr(stmt.Call)
|
||||
if stmt.Tok == syntax.Defer {
|
||||
w.optExpr(stmt.DeferAt)
|
||||
}
|
||||
|
||||
case *syntax.DeclStmt:
|
||||
for _, decl := range stmt.DeclList {
|
||||
@ -2300,6 +2307,10 @@ type posVar struct {
|
||||
var_ *types2.Var
|
||||
}
|
||||
|
||||
func (p posVar) String() string {
|
||||
return p.pos.String() + ":" + p.var_.String()
|
||||
}
|
||||
|
||||
func (w *writer) exprList(expr syntax.Expr) {
|
||||
w.Sync(pkgbits.SyncExprList)
|
||||
w.exprs(syntax.UnpackListExpr(expr))
|
||||
|
1142
src/cmd/compile/internal/rangefunc/rewrite.go
Normal file
1142
src/cmd/compile/internal/rangefunc/rewrite.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -103,6 +103,7 @@ func InitConfig() {
|
||||
ir.Syms.CgoCheckPtrWrite = typecheck.LookupRuntimeFunc("cgoCheckPtrWrite")
|
||||
ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment")
|
||||
ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc")
|
||||
ir.Syms.Deferprocat = typecheck.LookupRuntimeFunc("deferprocat")
|
||||
ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack")
|
||||
ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn")
|
||||
ir.Syms.Duffcopy = typecheck.LookupRuntimeFunc("duffcopy")
|
||||
@ -1491,10 +1492,10 @@ func (s *state) stmt(n ir.Node) {
|
||||
s.openDeferRecord(n.Call.(*ir.CallExpr))
|
||||
} else {
|
||||
d := callDefer
|
||||
if n.Esc() == ir.EscNever {
|
||||
if n.Esc() == ir.EscNever && n.DeferAt == nil {
|
||||
d = callDeferStack
|
||||
}
|
||||
s.callResult(n.Call.(*ir.CallExpr), d)
|
||||
s.call(n.Call.(*ir.CallExpr), d, false, n.DeferAt)
|
||||
}
|
||||
case ir.OGO:
|
||||
n := n.(*ir.GoDeferStmt)
|
||||
@ -5182,20 +5183,21 @@ func (s *state) openDeferExit() {
|
||||
}
|
||||
|
||||
func (s *state) callResult(n *ir.CallExpr, k callKind) *ssa.Value {
|
||||
return s.call(n, k, false)
|
||||
return s.call(n, k, false, nil)
|
||||
}
|
||||
|
||||
func (s *state) callAddr(n *ir.CallExpr, k callKind) *ssa.Value {
|
||||
return s.call(n, k, true)
|
||||
return s.call(n, k, true, nil)
|
||||
}
|
||||
|
||||
// Calls the function n using the specified call type.
|
||||
// Returns the address of the return value (or nil if none).
|
||||
func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Value {
|
||||
func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExtra ir.Expr) *ssa.Value {
|
||||
s.prevCall = nil
|
||||
var callee *ir.Name // target function (if static)
|
||||
var closure *ssa.Value // ptr to closure to run (if dynamic)
|
||||
var codeptr *ssa.Value // ptr to target code (if dynamic)
|
||||
var dextra *ssa.Value // defer extra arg
|
||||
var rcvr *ssa.Value // receiver to set
|
||||
fn := n.X
|
||||
var ACArgs []*types.Type // AuxCall args
|
||||
@ -5251,6 +5253,9 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
closure = iclosure
|
||||
}
|
||||
}
|
||||
if deferExtra != nil {
|
||||
dextra = s.expr(deferExtra)
|
||||
}
|
||||
|
||||
params := callABI.ABIAnalyze(n.X.Type(), false /* Do not set (register) nNames from caller side -- can cause races. */)
|
||||
types.CalcSize(fn.Type())
|
||||
@ -5293,6 +5298,13 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
callArgs = append(callArgs, closure)
|
||||
stksize += int64(types.PtrSize)
|
||||
argStart += int64(types.PtrSize)
|
||||
if dextra != nil {
|
||||
// Extra token of type any for deferproc
|
||||
ACArgs = append(ACArgs, types.Types[types.TINTER])
|
||||
callArgs = append(callArgs, dextra)
|
||||
stksize += 2 * int64(types.PtrSize)
|
||||
argStart += 2 * int64(types.PtrSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Set receiver (for interface calls).
|
||||
@ -5328,11 +5340,15 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
// call target
|
||||
switch {
|
||||
case k == callDefer:
|
||||
aux := ssa.StaticAuxCall(ir.Syms.Deferproc, s.f.ABIDefault.ABIAnalyzeTypes(ACArgs, ACResults)) // TODO paramResultInfo for DeferProc
|
||||
sym := ir.Syms.Deferproc
|
||||
if dextra != nil {
|
||||
sym = ir.Syms.Deferprocat
|
||||
}
|
||||
aux := ssa.StaticAuxCall(sym, s.f.ABIDefault.ABIAnalyzeTypes(ACArgs, ACResults)) // TODO paramResultInfo for Deferproc(at)
|
||||
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
|
||||
case k == callGo:
|
||||
aux := ssa.StaticAuxCall(ir.Syms.Newproc, s.f.ABIDefault.ABIAnalyzeTypes(ACArgs, ACResults))
|
||||
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) // TODO paramResultInfo for NewProc
|
||||
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) // TODO paramResultInfo for Newproc
|
||||
case closure != nil:
|
||||
// rawLoad because loading the code pointer from a
|
||||
// closure is always safe, but IsSanitizerSafeAddr
|
||||
|
@ -17,6 +17,7 @@ type Node interface {
|
||||
// associated with that production; usually the left-most one
|
||||
// ('[' for IndexExpr, 'if' for IfStmt, etc.)
|
||||
Pos() Pos
|
||||
SetPos(Pos)
|
||||
aNode()
|
||||
}
|
||||
|
||||
@ -26,8 +27,9 @@ type node struct {
|
||||
pos Pos
|
||||
}
|
||||
|
||||
func (n *node) Pos() Pos { return n.pos }
|
||||
func (*node) aNode() {}
|
||||
func (n *node) Pos() Pos { return n.pos }
|
||||
func (n *node) SetPos(pos Pos) { n.pos = pos }
|
||||
func (*node) aNode() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Files
|
||||
@ -389,8 +391,9 @@ type (
|
||||
}
|
||||
|
||||
CallStmt struct {
|
||||
Tok token // Go or Defer
|
||||
Call Expr
|
||||
Tok token // Go or Defer
|
||||
Call Expr
|
||||
DeferAt Expr // argument to runtime.deferprocat
|
||||
stmt
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
package syntax
|
||||
|
||||
type token uint
|
||||
type Token uint
|
||||
|
||||
type token = Token
|
||||
|
||||
//go:generate stringer -type token -linecomment tokens.go
|
||||
|
||||
|
@ -117,6 +117,9 @@ func panicnildottype(want *byte)
|
||||
func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
|
||||
func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
|
||||
|
||||
// defer in range over func
|
||||
func deferrangefunc() interface{}
|
||||
|
||||
func fastrand() uint32
|
||||
|
||||
// *byte is really *runtime.Type
|
||||
|
@ -103,139 +103,140 @@ var runtimeDecls = [...]struct {
|
||||
{"panicnildottype", funcTag, 72},
|
||||
{"ifaceeq", funcTag, 73},
|
||||
{"efaceeq", funcTag, 73},
|
||||
{"fastrand", 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},
|
||||
{"deferrangefunc", funcTag, 74},
|
||||
{"fastrand", 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, 30},
|
||||
{"writeBarrier", varTag, 105},
|
||||
{"typedmemmove", funcTag, 106},
|
||||
{"typedmemclr", funcTag, 107},
|
||||
{"typedslicecopy", funcTag, 108},
|
||||
{"selectnbsend", funcTag, 109},
|
||||
{"selectnbrecv", funcTag, 110},
|
||||
{"selectsetpc", funcTag, 111},
|
||||
{"selectgo", funcTag, 112},
|
||||
{"writeBarrier", varTag, 106},
|
||||
{"typedmemmove", funcTag, 107},
|
||||
{"typedmemclr", funcTag, 108},
|
||||
{"typedslicecopy", funcTag, 109},
|
||||
{"selectnbsend", funcTag, 110},
|
||||
{"selectnbrecv", funcTag, 111},
|
||||
{"selectsetpc", funcTag, 112},
|
||||
{"selectgo", funcTag, 113},
|
||||
{"block", funcTag, 9},
|
||||
{"makeslice", funcTag, 113},
|
||||
{"makeslice64", funcTag, 114},
|
||||
{"makeslicecopy", funcTag, 115},
|
||||
{"growslice", funcTag, 117},
|
||||
{"unsafeslicecheckptr", funcTag, 118},
|
||||
{"makeslice", funcTag, 114},
|
||||
{"makeslice64", funcTag, 115},
|
||||
{"makeslicecopy", funcTag, 116},
|
||||
{"growslice", funcTag, 118},
|
||||
{"unsafeslicecheckptr", funcTag, 119},
|
||||
{"panicunsafeslicelen", funcTag, 9},
|
||||
{"panicunsafeslicenilptr", funcTag, 9},
|
||||
{"unsafestringcheckptr", funcTag, 119},
|
||||
{"unsafestringcheckptr", funcTag, 120},
|
||||
{"panicunsafestringlen", funcTag, 9},
|
||||
{"panicunsafestringnilptr", funcTag, 9},
|
||||
{"mulUintptr", funcTag, 120},
|
||||
{"memmove", funcTag, 121},
|
||||
{"memclrNoHeapPointers", funcTag, 122},
|
||||
{"memclrHasPointers", funcTag, 122},
|
||||
{"memequal", funcTag, 123},
|
||||
{"memequal0", funcTag, 124},
|
||||
{"memequal8", funcTag, 124},
|
||||
{"memequal16", funcTag, 124},
|
||||
{"memequal32", funcTag, 124},
|
||||
{"memequal64", funcTag, 124},
|
||||
{"memequal128", funcTag, 124},
|
||||
{"f32equal", funcTag, 125},
|
||||
{"f64equal", funcTag, 125},
|
||||
{"c64equal", funcTag, 125},
|
||||
{"c128equal", funcTag, 125},
|
||||
{"strequal", funcTag, 125},
|
||||
{"interequal", funcTag, 125},
|
||||
{"nilinterequal", funcTag, 125},
|
||||
{"memhash", funcTag, 126},
|
||||
{"memhash0", funcTag, 127},
|
||||
{"memhash8", funcTag, 127},
|
||||
{"memhash16", funcTag, 127},
|
||||
{"memhash32", funcTag, 127},
|
||||
{"memhash64", funcTag, 127},
|
||||
{"memhash128", funcTag, 127},
|
||||
{"f32hash", funcTag, 128},
|
||||
{"f64hash", funcTag, 128},
|
||||
{"c64hash", funcTag, 128},
|
||||
{"c128hash", funcTag, 128},
|
||||
{"strhash", funcTag, 128},
|
||||
{"interhash", funcTag, 128},
|
||||
{"nilinterhash", funcTag, 128},
|
||||
{"int64div", funcTag, 129},
|
||||
{"uint64div", funcTag, 130},
|
||||
{"int64mod", funcTag, 129},
|
||||
{"uint64mod", funcTag, 130},
|
||||
{"float64toint64", funcTag, 131},
|
||||
{"float64touint64", funcTag, 132},
|
||||
{"float64touint32", funcTag, 133},
|
||||
{"int64tofloat64", funcTag, 134},
|
||||
{"int64tofloat32", funcTag, 136},
|
||||
{"uint64tofloat64", funcTag, 137},
|
||||
{"uint64tofloat32", funcTag, 138},
|
||||
{"uint32tofloat64", funcTag, 139},
|
||||
{"complex128div", funcTag, 140},
|
||||
{"getcallerpc", funcTag, 141},
|
||||
{"getcallersp", funcTag, 141},
|
||||
{"mulUintptr", funcTag, 121},
|
||||
{"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},
|
||||
{"racefuncenter", funcTag, 31},
|
||||
{"racefuncexit", funcTag, 9},
|
||||
{"raceread", funcTag, 31},
|
||||
{"racewrite", funcTag, 31},
|
||||
{"racereadrange", funcTag, 142},
|
||||
{"racewriterange", funcTag, 142},
|
||||
{"msanread", funcTag, 142},
|
||||
{"msanwrite", funcTag, 142},
|
||||
{"msanmove", funcTag, 143},
|
||||
{"asanread", funcTag, 142},
|
||||
{"asanwrite", funcTag, 142},
|
||||
{"checkptrAlignment", funcTag, 144},
|
||||
{"checkptrArithmetic", funcTag, 146},
|
||||
{"libfuzzerTraceCmp1", funcTag, 147},
|
||||
{"libfuzzerTraceCmp2", funcTag, 148},
|
||||
{"libfuzzerTraceCmp4", funcTag, 149},
|
||||
{"libfuzzerTraceCmp8", funcTag, 150},
|
||||
{"libfuzzerTraceConstCmp1", funcTag, 147},
|
||||
{"libfuzzerTraceConstCmp2", funcTag, 148},
|
||||
{"libfuzzerTraceConstCmp4", funcTag, 149},
|
||||
{"libfuzzerTraceConstCmp8", funcTag, 150},
|
||||
{"libfuzzerHookStrCmp", funcTag, 151},
|
||||
{"libfuzzerHookEqualFold", funcTag, 151},
|
||||
{"addCovMeta", funcTag, 153},
|
||||
{"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},
|
||||
{"x86HasPOPCNT", varTag, 6},
|
||||
{"x86HasSSE41", varTag, 6},
|
||||
{"x86HasFMA", varTag, 6},
|
||||
{"armHasVFPv4", varTag, 6},
|
||||
{"arm64HasATOMICS", varTag, 6},
|
||||
{"asanregisterglobals", funcTag, 122},
|
||||
{"asanregisterglobals", funcTag, 123},
|
||||
}
|
||||
|
||||
func runtimeTypes() []*types.Type {
|
||||
var typs [154]*types.Type
|
||||
var typs [155]*types.Type
|
||||
typs[0] = types.ByteType
|
||||
typs[1] = types.NewPtr(typs[0])
|
||||
typs[2] = types.Types[types.TANY]
|
||||
@ -310,86 +311,87 @@ func runtimeTypes() []*types.Type {
|
||||
typs[71] = newSig(params(typs[1], typs[1], typs[1]), nil)
|
||||
typs[72] = newSig(params(typs[1]), nil)
|
||||
typs[73] = newSig(params(typs[57], typs[7], typs[7]), params(typs[6]))
|
||||
typs[74] = newSig(nil, params(typs[62]))
|
||||
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[62]), 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[62]), 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[62]), 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] = types.NewArray(typs[0], 3)
|
||||
typs[105] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[104]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
|
||||
typs[106] = newSig(params(typs[1], typs[3], typs[3]), nil)
|
||||
typs[107] = newSig(params(typs[1], typs[3]), nil)
|
||||
typs[108] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
|
||||
typs[109] = newSig(params(typs[102], typs[3]), params(typs[6]))
|
||||
typs[110] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6]))
|
||||
typs[111] = newSig(params(typs[57]), nil)
|
||||
typs[112] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
|
||||
typs[113] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
|
||||
typs[114] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
|
||||
typs[115] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
|
||||
typs[116] = types.NewSlice(typs[2])
|
||||
typs[117] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[116]))
|
||||
typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
|
||||
typs[119] = newSig(params(typs[7], typs[22]), nil)
|
||||
typs[120] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6]))
|
||||
typs[121] = newSig(params(typs[3], typs[3], typs[5]), nil)
|
||||
typs[122] = newSig(params(typs[7], typs[5]), nil)
|
||||
typs[123] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
|
||||
typs[124] = newSig(params(typs[3], typs[3]), params(typs[6]))
|
||||
typs[125] = newSig(params(typs[7], typs[7]), params(typs[6]))
|
||||
typs[126] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
|
||||
typs[127] = newSig(params(typs[7], typs[5]), params(typs[5]))
|
||||
typs[128] = newSig(params(typs[3], typs[5]), params(typs[5]))
|
||||
typs[129] = newSig(params(typs[22], typs[22]), params(typs[22]))
|
||||
typs[130] = newSig(params(typs[24], typs[24]), params(typs[24]))
|
||||
typs[131] = newSig(params(typs[20]), params(typs[22]))
|
||||
typs[132] = newSig(params(typs[20]), params(typs[24]))
|
||||
typs[133] = newSig(params(typs[20]), params(typs[62]))
|
||||
typs[134] = newSig(params(typs[22]), params(typs[20]))
|
||||
typs[135] = types.Types[types.TFLOAT32]
|
||||
typs[136] = newSig(params(typs[22]), params(typs[135]))
|
||||
typs[137] = newSig(params(typs[24]), params(typs[20]))
|
||||
typs[138] = newSig(params(typs[24]), params(typs[135]))
|
||||
typs[139] = newSig(params(typs[62]), params(typs[20]))
|
||||
typs[140] = newSig(params(typs[26], typs[26]), params(typs[26]))
|
||||
typs[141] = newSig(nil, params(typs[5]))
|
||||
typs[142] = newSig(params(typs[5], typs[5]), nil)
|
||||
typs[143] = newSig(params(typs[5], typs[5], typs[5]), nil)
|
||||
typs[144] = newSig(params(typs[7], typs[1], typs[5]), nil)
|
||||
typs[145] = types.NewSlice(typs[7])
|
||||
typs[146] = newSig(params(typs[7], typs[145]), nil)
|
||||
typs[147] = newSig(params(typs[66], typs[66], typs[17]), nil)
|
||||
typs[148] = newSig(params(typs[60], typs[60], typs[17]), nil)
|
||||
typs[149] = newSig(params(typs[62], typs[62], typs[17]), nil)
|
||||
typs[150] = newSig(params(typs[24], typs[24], typs[17]), nil)
|
||||
typs[151] = newSig(params(typs[28], typs[28], typs[17]), nil)
|
||||
typs[152] = types.NewArray(typs[0], 16)
|
||||
typs[153] = newSig(params(typs[7], typs[62], typs[152], typs[28], typs[15], typs[66], typs[66]), params(typs[62]))
|
||||
typs[74] = newSig(nil, params(typs[10]))
|
||||
typs[75] = newSig(nil, params(typs[62]))
|
||||
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[62]), 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[62]), 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[62]), 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] = types.NewArray(typs[0], 3)
|
||||
typs[106] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[105]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
|
||||
typs[107] = newSig(params(typs[1], typs[3], typs[3]), nil)
|
||||
typs[108] = newSig(params(typs[1], typs[3]), nil)
|
||||
typs[109] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
|
||||
typs[110] = newSig(params(typs[103], typs[3]), params(typs[6]))
|
||||
typs[111] = newSig(params(typs[3], typs[100]), params(typs[6], typs[6]))
|
||||
typs[112] = newSig(params(typs[57]), nil)
|
||||
typs[113] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
|
||||
typs[114] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
|
||||
typs[115] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
|
||||
typs[116] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
|
||||
typs[117] = types.NewSlice(typs[2])
|
||||
typs[118] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[117]))
|
||||
typs[119] = newSig(params(typs[1], typs[7], typs[22]), nil)
|
||||
typs[120] = newSig(params(typs[7], typs[22]), nil)
|
||||
typs[121] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6]))
|
||||
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[62]))
|
||||
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[62]), 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[66], typs[66], typs[17]), nil)
|
||||
typs[149] = newSig(params(typs[60], typs[60], typs[17]), nil)
|
||||
typs[150] = newSig(params(typs[62], typs[62], 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[62], typs[153], typs[28], typs[15], typs[66], typs[66]), params(typs[62]))
|
||||
return typs[:]
|
||||
}
|
||||
|
||||
|
@ -583,6 +583,17 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
|
||||
return e
|
||||
}
|
||||
|
||||
if name, ok := n.X.(*ir.Name); ok {
|
||||
sym := name.Sym()
|
||||
if sym.Pkg.Path == "go.runtime" && sym.Name == "deferrangefunc" {
|
||||
// Call to runtime.deferrangefunc is being shared with a range-over-func
|
||||
// body that might add defers to this frame, so we cannot use open-coded defers
|
||||
// and we need to call deferreturn even if we don't see any other explicit defers.
|
||||
ir.CurFunc.SetHasDefer(true)
|
||||
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
|
||||
}
|
||||
}
|
||||
|
||||
walkCall1(n, init)
|
||||
return n
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ func walkStmt(n ir.Node) ir.Node {
|
||||
if n.Typecheck() == 0 {
|
||||
base.Fatalf("missing typecheck: %+v", n)
|
||||
}
|
||||
|
||||
init := ir.TakeInit(n)
|
||||
n = walkExpr(n, &init)
|
||||
if n.Op() == ir.ONAME {
|
||||
@ -104,10 +105,11 @@ func walkStmt(n ir.Node) ir.Node {
|
||||
n := n.(*ir.GoDeferStmt)
|
||||
ir.CurFunc.SetHasDefer(true)
|
||||
ir.CurFunc.NumDefers++
|
||||
if ir.CurFunc.NumDefers > maxOpenDefers {
|
||||
if ir.CurFunc.NumDefers > maxOpenDefers || n.DeferAt != nil {
|
||||
// Don't allow open-coded defers if there are more than
|
||||
// 8 defers in the function, since we use a single
|
||||
// byte to record active defers.
|
||||
// Also don't allow if we need to use deferprocat.
|
||||
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
|
||||
}
|
||||
if n.Esc() != ir.EscNever {
|
||||
|
335
test/range3.go
335
test/range3.go
@ -11,43 +11,365 @@ package main
|
||||
// test range over integers
|
||||
|
||||
func testint1() {
|
||||
bad := false
|
||||
j := 0
|
||||
for i := range int(4) {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j != 4 {
|
||||
println("wrong count ranging over 4:", j)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testint1")
|
||||
}
|
||||
}
|
||||
|
||||
func testint2() {
|
||||
bad := false
|
||||
j := 0
|
||||
for i := range 4 {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j != 4 {
|
||||
println("wrong count ranging over 4:", j)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testint2")
|
||||
}
|
||||
}
|
||||
|
||||
func testint3() {
|
||||
bad := false
|
||||
type MyInt int
|
||||
|
||||
j := MyInt(0)
|
||||
for i := range MyInt(4) {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j != 4 {
|
||||
println("wrong count ranging over 4:", j)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testint3")
|
||||
}
|
||||
}
|
||||
|
||||
// test range over functions
|
||||
|
||||
var gj int
|
||||
|
||||
func yield4x(yield func() bool) {
|
||||
_ = yield() && yield() && yield() && yield()
|
||||
}
|
||||
|
||||
func yield4(yield func(int) bool) {
|
||||
_ = yield(1) && yield(2) && yield(3) && yield(4)
|
||||
}
|
||||
|
||||
func yield3(yield func(int) bool) {
|
||||
_ = yield(1) && yield(2) && yield(3)
|
||||
}
|
||||
|
||||
func yield2(yield func(int) bool) {
|
||||
_ = yield(1) && yield(2)
|
||||
}
|
||||
|
||||
func testfunc0() {
|
||||
j := 0
|
||||
for range yield4x {
|
||||
j++
|
||||
}
|
||||
if j != 4 {
|
||||
println("wrong count ranging over yield4x:", j)
|
||||
panic("testfunc0")
|
||||
}
|
||||
|
||||
j = 0
|
||||
for _ = range yield4 {
|
||||
j++
|
||||
}
|
||||
if j != 4 {
|
||||
println("wrong count ranging over yield4:", j)
|
||||
panic("testfunc0")
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc1() {
|
||||
bad := false
|
||||
j := 1
|
||||
for i := range yield4 {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j != 5 {
|
||||
println("wrong count ranging over f:", j)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testfunc1")
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc2() {
|
||||
bad := false
|
||||
j := 1
|
||||
var i int
|
||||
for i = range yield4 {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j != 5 {
|
||||
println("wrong count ranging over f:", j)
|
||||
bad = true
|
||||
}
|
||||
if i != 4 {
|
||||
println("wrong final i ranging over f:", i)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testfunc2")
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc3() {
|
||||
bad := false
|
||||
j := 1
|
||||
var i int
|
||||
for i = range yield4 {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
if i == 2 {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if j != 3 {
|
||||
println("wrong count ranging over f:", j)
|
||||
bad = true
|
||||
}
|
||||
if i != 2 {
|
||||
println("wrong final i ranging over f:", i)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testfunc3")
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc4() {
|
||||
bad := false
|
||||
j := 1
|
||||
var i int
|
||||
func() {
|
||||
for i = range yield4 {
|
||||
if i != j {
|
||||
println("range var", i, "want", j)
|
||||
bad = true
|
||||
}
|
||||
j++
|
||||
if i == 2 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
if j != 3 {
|
||||
println("wrong count ranging over f:", j)
|
||||
bad = true
|
||||
}
|
||||
if i != 2 {
|
||||
println("wrong final i ranging over f:", i)
|
||||
bad = true
|
||||
}
|
||||
if bad {
|
||||
panic("testfunc3")
|
||||
}
|
||||
}
|
||||
|
||||
func func5() (int, int) {
|
||||
for i := range yield4 {
|
||||
return 10, i
|
||||
}
|
||||
panic("still here")
|
||||
}
|
||||
|
||||
func testfunc5() {
|
||||
x, y := func5()
|
||||
if x != 10 || y != 1 {
|
||||
println("wrong results", x, y, "want", 10, 1)
|
||||
panic("testfunc5")
|
||||
}
|
||||
}
|
||||
|
||||
func func6() (z, w int) {
|
||||
for i := range yield4 {
|
||||
z = 10
|
||||
w = i
|
||||
return
|
||||
}
|
||||
panic("still here")
|
||||
}
|
||||
|
||||
func testfunc6() {
|
||||
x, y := func6()
|
||||
if x != 10 || y != 1 {
|
||||
println("wrong results", x, y, "want", 10, 1)
|
||||
panic("testfunc6")
|
||||
}
|
||||
}
|
||||
|
||||
var saved []int
|
||||
|
||||
func save(x int) {
|
||||
saved = append(saved, x)
|
||||
}
|
||||
|
||||
func printslice(s []int) {
|
||||
print("[")
|
||||
for i, x := range s {
|
||||
if i > 0 {
|
||||
print(", ")
|
||||
}
|
||||
print(x)
|
||||
}
|
||||
print("]")
|
||||
}
|
||||
|
||||
func eqslice(s, t []int) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, x := range s {
|
||||
if x != t[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func func7() {
|
||||
defer save(-1)
|
||||
for i := range yield4 {
|
||||
defer save(i)
|
||||
}
|
||||
defer save(5)
|
||||
}
|
||||
|
||||
func checkslice(name string, saved, want []int) {
|
||||
if !eqslice(saved, want) {
|
||||
print("wrong results ")
|
||||
printslice(saved)
|
||||
print(" want ")
|
||||
printslice(want)
|
||||
print("\n")
|
||||
panic(name)
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc7() {
|
||||
saved = nil
|
||||
func7()
|
||||
want := []int{5, 4, 3, 2, 1, -1}
|
||||
checkslice("testfunc7", saved, want)
|
||||
}
|
||||
|
||||
func func8() {
|
||||
defer save(-1)
|
||||
for i := range yield2 {
|
||||
for j := range yield3 {
|
||||
defer save(i*10 + j)
|
||||
}
|
||||
defer save(i)
|
||||
}
|
||||
defer save(-2)
|
||||
for i := range yield4 {
|
||||
defer save(i)
|
||||
}
|
||||
defer save(-3)
|
||||
}
|
||||
|
||||
func testfunc8() {
|
||||
saved = nil
|
||||
func8()
|
||||
want := []int{-3, 4, 3, 2, 1, -2, 2, 23, 22, 21, 1, 13, 12, 11, -1}
|
||||
checkslice("testfunc8", saved, want)
|
||||
}
|
||||
|
||||
func func9() {
|
||||
n := 0
|
||||
for _ = range yield2 {
|
||||
for _ = range yield3 {
|
||||
n++
|
||||
defer save(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testfunc9() {
|
||||
saved = nil
|
||||
func9()
|
||||
want := []int{6, 5, 4, 3, 2, 1}
|
||||
checkslice("testfunc9", saved, want)
|
||||
}
|
||||
|
||||
// test that range evaluates the index and value expressions
|
||||
// exactly once per iteration.
|
||||
|
||||
var ncalls = 0
|
||||
|
||||
func getvar(p *int) *int {
|
||||
ncalls++
|
||||
return p
|
||||
}
|
||||
|
||||
func iter2(list ...int) func(func(int, int) bool) {
|
||||
return func(yield func(int, int) bool) {
|
||||
for i, x := range list {
|
||||
if !yield(i, x) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testcalls() {
|
||||
var i, v int
|
||||
ncalls = 0
|
||||
si := 0
|
||||
sv := 0
|
||||
for *getvar(&i), *getvar(&v) = range iter2(1, 2) {
|
||||
si += i
|
||||
sv += v
|
||||
}
|
||||
if ncalls != 4 {
|
||||
println("wrong number of calls:", ncalls, "!= 4")
|
||||
panic("fail")
|
||||
}
|
||||
if si != 1 || sv != 3 {
|
||||
println("wrong sum in testcalls", si, sv)
|
||||
panic("fail")
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,4 +377,15 @@ func main() {
|
||||
testint1()
|
||||
testint2()
|
||||
testint3()
|
||||
testfunc0()
|
||||
testfunc1()
|
||||
testfunc2()
|
||||
testfunc3()
|
||||
testfunc4()
|
||||
testfunc5()
|
||||
testfunc6()
|
||||
testfunc7()
|
||||
testfunc8()
|
||||
testfunc9()
|
||||
testcalls()
|
||||
}
|
||||
|
333
test/rangegen.go
Normal file
333
test/rangegen.go
Normal file
@ -0,0 +1,333 @@
|
||||
// runoutput -goexperiment range
|
||||
|
||||
// Copyright 2023 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.
|
||||
|
||||
// Torture test for range-over-func.
|
||||
//
|
||||
// cmd/internal/testdir runs this like
|
||||
//
|
||||
// go run rangegen.go >x.go
|
||||
// go run x.go
|
||||
//
|
||||
// but a longer version can be run using
|
||||
//
|
||||
// go run rangegen.go long
|
||||
//
|
||||
// In that second form, rangegen takes care of compiling
|
||||
// and running the code it generates, in batches.
|
||||
// That form takes 10-20 minutes to run.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const verbose = false
|
||||
|
||||
func main() {
|
||||
long := len(os.Args) > 1 && os.Args[1] == "long"
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("rangegen: ")
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
tests := ""
|
||||
flush := func(force bool) {
|
||||
if !long || (strings.Count(tests, "\n") < 1000 && !force) {
|
||||
return
|
||||
}
|
||||
p(b, mainCode, tests)
|
||||
err := os.WriteFile("tmp.go", b.Bytes(), 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
out, err := exec.Command("go", "run", "tmp.go").CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("go run tmp.go: %v\n%s", err, out)
|
||||
}
|
||||
print(".")
|
||||
if force {
|
||||
print("\nPASS\n")
|
||||
}
|
||||
b.Reset()
|
||||
tests = ""
|
||||
p(b, "package main\n\n")
|
||||
p(b, "const verbose = %v\n\n", verbose)
|
||||
}
|
||||
|
||||
p(b, "package main\n\n")
|
||||
p(b, "const verbose = %v\n\n", verbose)
|
||||
max := 2
|
||||
if !long {
|
||||
max = 5
|
||||
}
|
||||
for i := 1; i <= max; i++ {
|
||||
maxDouble := -1
|
||||
if long {
|
||||
maxDouble = i
|
||||
}
|
||||
for double := -1; double <= maxDouble; double++ {
|
||||
code := gen(new(bytes.Buffer), "", "", "", i, double, func(c int) bool { return true })
|
||||
for j := 0; j < code; j++ {
|
||||
hi := j + 1
|
||||
if long {
|
||||
hi = code
|
||||
}
|
||||
for k := j; k < hi && k < code; k++ {
|
||||
s := fmt.Sprintf("%d_%d_%d_%d", i, double+1, j, k)
|
||||
code0 := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return c == j || c == k })
|
||||
code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return c == j || c == k })
|
||||
if code0 != code1 {
|
||||
panic("bad generator")
|
||||
}
|
||||
tests += "test" + s + "()\n"
|
||||
p(b, testCode, "test"+s, []int{j, k}, "testFunc"+s, "testSlice"+s)
|
||||
flush(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 1; i <= max; i++ {
|
||||
maxDouble := -1
|
||||
if long {
|
||||
maxDouble = i
|
||||
}
|
||||
for double := -1; double <= maxDouble; double++ {
|
||||
s := fmt.Sprintf("%d_%d", i, double+1)
|
||||
code := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return true })
|
||||
code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return true })
|
||||
if code != code1 {
|
||||
panic("bad generator")
|
||||
}
|
||||
tests += "test" + s + "()\n"
|
||||
var all []int
|
||||
for j := 0; j < code; j++ {
|
||||
all = append(all, j)
|
||||
}
|
||||
p(b, testCode, "test"+s, all, "testFunc"+s, "testSlice"+s)
|
||||
flush(false)
|
||||
}
|
||||
}
|
||||
if long {
|
||||
flush(true)
|
||||
os.Remove("tmp.go")
|
||||
return
|
||||
}
|
||||
|
||||
p(b, mainCode, tests)
|
||||
|
||||
os.Stdout.Write(b.Bytes())
|
||||
}
|
||||
|
||||
func p(b *bytes.Buffer, format string, args ...any) {
|
||||
fmt.Fprintf(b, format, args...)
|
||||
}
|
||||
|
||||
func gen(b *bytes.Buffer, name, prefix, rangeExpr string, depth, double int, allowed func(int) bool) int {
|
||||
p(b, "func %s(o *output, code int) int {\n", name)
|
||||
p(b, " dfr := 0; _ = dfr\n")
|
||||
code := genLoop(b, 0, prefix, rangeExpr, depth, double, 0, "", allowed)
|
||||
p(b, " return 0\n")
|
||||
p(b, "}\n\n")
|
||||
return code
|
||||
}
|
||||
|
||||
func genLoop(b *bytes.Buffer, d int, prefix, rangeExpr string, depth, double, code int, labelSuffix string, allowed func(int) bool) int {
|
||||
limit := 1
|
||||
if d == double {
|
||||
limit = 2
|
||||
}
|
||||
for rep := 0; rep < limit; rep++ {
|
||||
if rep == 1 {
|
||||
labelSuffix = "R"
|
||||
}
|
||||
s := fmt.Sprintf("%d%s", d, labelSuffix)
|
||||
p(b, " o.log(`top%s`)\n", s)
|
||||
p(b, " l%sa := 0\n", s)
|
||||
p(b, "goto L%sa; L%sa: o.log(`L%sa`)\n", s, s, s)
|
||||
p(b, " if l%sa++; l%sa >= 2 { o.log(`loop L%sa`); return -1 }\n", s, s, s)
|
||||
p(b, " l%sfor := 0\n", s)
|
||||
p(b, "goto L%sfor; L%sfor: for f := 0; f < 1; f++ { o.log(`L%sfor`)\n", s, s, s)
|
||||
p(b, " if l%sfor++; l%sfor >= 2 { o.log(`loop L%sfor`); return -1 }\n", s, s, s)
|
||||
p(b, " l%ssw := 0\n", s)
|
||||
p(b, "goto L%ssw; L%ssw: switch { default: o.log(`L%ssw`)\n", s, s, s)
|
||||
p(b, " if l%ssw++; l%ssw >= 2 { o.log(`loop L%ssw`); return -1 }\n", s, s, s)
|
||||
p(b, " l%ssel := 0\n", s)
|
||||
p(b, "goto L%ssel; L%ssel: select { default: o.log(`L%ssel`)\n", s, s, s)
|
||||
p(b, " if l%ssel++; l%ssel >= 2 { o.log(`loop L%ssel`); return -1 }\n", s, s, s)
|
||||
p(b, " l%s := 0\n", s)
|
||||
p(b, "goto L%s; L%s: for %s i%s := range %s {\n", s, s, prefix, s, rangeExpr)
|
||||
p(b, " o.log1(`L%s top`, i%s)\n", s, s)
|
||||
p(b, " if l%s++; l%s >= 4 { o.log(`loop L%s`); return -1 }\n", s, s, s)
|
||||
printTests := func() {
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { continue }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " switch { case code == %v: continue }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { return %[1]v }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { select { default: break } }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { switch { default: break } }\n", code)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { dfr++; defer o.log1(`defer %d`, dfr) }\n", code, code)
|
||||
}
|
||||
for i := d; i > 0; i-- {
|
||||
suffix := labelSuffix
|
||||
if i < double {
|
||||
suffix = ""
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break L%d%s }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { select { default: break L%d%s } }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break L%d%s }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break L%d%ssw }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break L%d%ssel }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { break L%d%sfor }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { continue L%d%sfor }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { goto L%d%sa }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { goto L%d%s }\n", code, i, suffix)
|
||||
}
|
||||
if code++; allowed(code) {
|
||||
p(b, " if code == %v { goto L%d%sb }\n", code, i, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
printTests()
|
||||
if d < depth {
|
||||
if rep == 1 {
|
||||
double = d // signal to children to use the rep=1 labels
|
||||
}
|
||||
code = genLoop(b, d+1, prefix, rangeExpr, depth, double, code, labelSuffix, allowed)
|
||||
printTests()
|
||||
}
|
||||
p(b, " o.log(`L%s bot`)\n", s)
|
||||
p(b, " }\n")
|
||||
p(b, " o.log(`L%ssel bot`)\n", s)
|
||||
p(b, " }\n")
|
||||
p(b, " o.log(`L%ssw bot`)\n", s)
|
||||
p(b, " }\n")
|
||||
p(b, " o.log(`L%sfor bot`)\n", s)
|
||||
p(b, " }\n")
|
||||
p(b, " o.log(`done%s`)\n", s)
|
||||
p(b, "goto L%sb; L%sb: o.log(`L%sb`)\n", s, s, s)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
var testCode = `
|
||||
func %s() {
|
||||
all := %#v
|
||||
for i := 0; i < len(all); i++ {
|
||||
c := all[i]
|
||||
outFunc := run(%s, c)
|
||||
outSlice := run(%s, c)
|
||||
if !outFunc.eq(outSlice) {
|
||||
println("mismatch", "%[3]s", "%[4]s", c)
|
||||
println()
|
||||
println("func:")
|
||||
outFunc.print()
|
||||
println()
|
||||
println("slice:")
|
||||
outSlice.print()
|
||||
panic("mismatch")
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
println("did", "%[3]s", "%[4]s", len(all))
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var mainCode = `
|
||||
|
||||
func main() {
|
||||
if verbose {
|
||||
println("main")
|
||||
}
|
||||
%s
|
||||
}
|
||||
|
||||
func yield2(yield func(int)bool) { _ = yield(1) && yield(2) }
|
||||
var slice2 = []int{1,2}
|
||||
|
||||
type output struct {
|
||||
ret int
|
||||
trace []any
|
||||
}
|
||||
|
||||
func (o *output) log(x any) {
|
||||
o.trace = append(o.trace, x)
|
||||
}
|
||||
|
||||
func (o *output) log1(x, y any) {
|
||||
o.trace = append(o.trace, x, y)
|
||||
}
|
||||
|
||||
func (o *output) eq(p *output) bool{
|
||||
if o.ret != p.ret || len(o.trace) != len(p.trace) {
|
||||
return false
|
||||
}
|
||||
for i ,x := range o.trace {
|
||||
if x != p.trace[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *output) print() {
|
||||
println("ret", o.ret, "trace-len", len(o.trace))
|
||||
for i := 0; i < len(o.trace); i++ {
|
||||
print("#", i, " ")
|
||||
switch x := o.trace[i].(type) {
|
||||
case int:
|
||||
print(x)
|
||||
case string:
|
||||
print(x)
|
||||
default:
|
||||
print(x)
|
||||
}
|
||||
print("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func run(f func(*output, int)int, i int) *output {
|
||||
o := &output{}
|
||||
o.ret = f(o, i)
|
||||
return o
|
||||
}
|
||||
|
||||
`
|
Loading…
Reference in New Issue
Block a user