mirror of
https://github.com/golang/go
synced 2024-10-06 08:00:07 +00:00
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of its callers setup the closure funcs in slightly different ways. E.g., it's non-obvious who's responsible for setting the underlying func's Sym or adding it to target.Decls, or how to write new code that constructs a closure without interfering with existing code. This CL refactors everything to use three common functions in package ir: NewClosureFunc (which handle creating the Func, Name, and ClosureExpr and wiring them together), NameClosure (which generates and assigns its unique Sym), and UseClosure (which handles adding the Func to target.Decls). Most IR builders can actually name the closure right away, but the legacy noder+typecheck path may not yet know the name of the enclosing function. In particular, for methods declared with aliased receiver parameters, we need to wait until after typechecking top-level declarations to know the method's true name. So they're left anonymous until typecheck. UseClosure does relatively little work today, but it serves as a useful spot to check that the code setting up closures got it right. It may also eventually serve as an optimization point for early lifting of trivial closures, which may or may not ultimately be beneficial. Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1 Reviewed-on: https://go-review.googlesource.com/c/go/+/327051 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
8f00eb0099
commit
0132b91127
|
@ -1143,8 +1143,6 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
|
|||
// closure does the necessary substitions for a ClosureExpr n and returns the new
|
||||
// closure node.
|
||||
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
||||
m := ir.Copy(n)
|
||||
|
||||
// Prior to the subst edit, set a flag in the inlsubst to
|
||||
// indicated that we don't want to update the source positions in
|
||||
// the new closure. If we do this, it will appear that the closure
|
||||
|
@ -1152,29 +1150,21 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
|||
// issue #46234 for more details.
|
||||
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
|
||||
subst.noPosUpdate = true
|
||||
ir.EditChildren(m, subst.edit)
|
||||
|
||||
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
|
||||
|
||||
// The following is similar to funcLit
|
||||
outerfunc := subst.newclofn
|
||||
if outerfunc == nil {
|
||||
outerfunc = ir.CurFunc
|
||||
}
|
||||
|
||||
oldfn := n.Func
|
||||
newfn := ir.NewFunc(oldfn.Pos())
|
||||
// These three lines are not strictly necessary, but just to be clear
|
||||
// that new function needs to redo typechecking and inlinability.
|
||||
newfn.SetTypecheck(0)
|
||||
newfn.SetInlinabilityChecked(false)
|
||||
newfn.Inl = nil
|
||||
newfn.SetIsHiddenClosure(true)
|
||||
newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
|
||||
newfn.Nname.Func = newfn
|
||||
newfn := ir.NewClosureFunc(oldfn.Pos(), outerfunc)
|
||||
|
||||
// Ntype can be nil for -G=3 mode.
|
||||
if oldfn.Nname.Ntype != nil {
|
||||
newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
|
||||
}
|
||||
newfn.Nname.Defn = newfn
|
||||
|
||||
m.(*ir.ClosureExpr).Func = newfn
|
||||
newfn.OClosure = m.(*ir.ClosureExpr)
|
||||
|
||||
if subst.newclofn != nil {
|
||||
//fmt.Printf("Inlining a closure with a nested closure\n")
|
||||
|
@ -1224,13 +1214,13 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
|||
|
||||
// Actually create the named function for the closure, now that
|
||||
// the closure is inlined in a specific function.
|
||||
m.SetTypecheck(0)
|
||||
newclo := newfn.OClosure
|
||||
newclo.SetInit(subst.list(n.Init()))
|
||||
if oldfn.ClosureCalled() {
|
||||
typecheck.Callee(m)
|
||||
return typecheck.Callee(newclo)
|
||||
} else {
|
||||
typecheck.Expr(m)
|
||||
return typecheck.Expr(newclo)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// node recursively copies a node from the saved pristine body of the
|
||||
|
|
|
@ -195,6 +195,7 @@ type ClosureExpr struct {
|
|||
IsGoWrap bool // whether this is wrapper closure of a go statement
|
||||
}
|
||||
|
||||
// Deprecated: Use NewClosureFunc instead.
|
||||
func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr {
|
||||
n := &ClosureExpr{Func: fn}
|
||||
n.op = OCLOSURE
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A Func corresponds to a single function in a Go program
|
||||
|
@ -311,3 +312,102 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
|
|||
func IsTrivialClosure(clo *ClosureExpr) bool {
|
||||
return len(clo.Func.ClosureVars) == 0
|
||||
}
|
||||
|
||||
// globClosgen is like Func.Closgen, but for the global scope.
|
||||
var globClosgen int32
|
||||
|
||||
// closureName generates a new unique name for a closure within outerfn.
|
||||
func closureName(outerfn *Func) *types.Sym {
|
||||
pkg := types.LocalPkg
|
||||
outer := "glob."
|
||||
prefix := "func"
|
||||
gen := &globClosgen
|
||||
|
||||
if outerfn != nil {
|
||||
if outerfn.OClosure != nil {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
pkg = outerfn.Sym().Pkg
|
||||
outer = FuncName(outerfn)
|
||||
|
||||
// There may be multiple functions named "_". In those
|
||||
// cases, we can't use their individual Closgens as it
|
||||
// would lead to name clashes.
|
||||
if !IsBlank(outerfn.Nname) {
|
||||
gen = &outerfn.Closgen
|
||||
}
|
||||
}
|
||||
|
||||
*gen++
|
||||
return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
|
||||
}
|
||||
|
||||
// NewClosureFunc creates a new Func to represent a function literal
|
||||
// within outerfn.
|
||||
func NewClosureFunc(pos src.XPos, outerfn *Func) *Func {
|
||||
fn := NewFunc(pos)
|
||||
fn.SetIsHiddenClosure(outerfn != nil)
|
||||
|
||||
fn.Nname = NewNameAt(pos, BlankNode.Sym())
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Defn = fn
|
||||
|
||||
fn.OClosure = NewClosureExpr(pos, fn)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// NameClosure generates a unique for the given function literal,
|
||||
// which must have appeared within outerfn.
|
||||
func NameClosure(clo *ClosureExpr, outerfn *Func) {
|
||||
name := clo.Func.Nname
|
||||
if !IsBlank(name) {
|
||||
base.FatalfAt(clo.Pos(), "closure already named: %v", name)
|
||||
}
|
||||
|
||||
name.SetSym(closureName(outerfn))
|
||||
MarkFunc(name)
|
||||
}
|
||||
|
||||
// UseClosure checks that the ginen function literal has been setup
|
||||
// correctly, and then returns it as an expression.
|
||||
// It must be called after clo.Func.ClosureVars has been set.
|
||||
func UseClosure(clo *ClosureExpr, pkg *Package) Node {
|
||||
fn := clo.Func
|
||||
name := fn.Nname
|
||||
|
||||
if IsBlank(name) {
|
||||
base.FatalfAt(fn.Pos(), "unnamed closure func: %v", fn)
|
||||
}
|
||||
// Caution: clo.Typecheck() is still 0 when UseClosure is called by
|
||||
// tcClosure.
|
||||
if fn.Typecheck() != 1 || name.Typecheck() != 1 {
|
||||
base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn)
|
||||
}
|
||||
if clo.Type() == nil || name.Type() == nil {
|
||||
base.FatalfAt(fn.Pos(), "missing types: %v", fn)
|
||||
}
|
||||
if !types.Identical(clo.Type(), name.Type()) {
|
||||
base.FatalfAt(fn.Pos(), "mismatched types: %v", fn)
|
||||
}
|
||||
|
||||
if base.Flag.W > 1 {
|
||||
s := fmt.Sprintf("new closure func: %v", fn)
|
||||
Dump(s, fn)
|
||||
}
|
||||
|
||||
if pkg != nil {
|
||||
pkg.Decls = append(pkg.Decls, fn)
|
||||
}
|
||||
|
||||
if false && IsTrivialClosure(clo) {
|
||||
// TODO(mdempsky): Investigate if we can/should optimize this
|
||||
// case. walkClosure already handles it later, but it could be
|
||||
// useful to recognize earlier (e.g., it might allow multiple
|
||||
// inlined calls to a function to share a common trivial closure
|
||||
// func, rather than cloning it for each inlined call).
|
||||
}
|
||||
|
||||
return clo
|
||||
}
|
||||
|
|
|
@ -373,19 +373,13 @@ func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
|
|||
}
|
||||
|
||||
func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
|
||||
fn := ir.NewFunc(g.pos(expr))
|
||||
fn.SetIsHiddenClosure(ir.CurFunc != nil)
|
||||
fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc)
|
||||
ir.NameClosure(fn.OClosure, ir.CurFunc)
|
||||
|
||||
fn.Nname = ir.NewNameAt(g.pos(expr), typecheck.ClosureName(ir.CurFunc))
|
||||
ir.MarkFunc(fn.Nname)
|
||||
typ := g.typ(typ2)
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Defn = fn
|
||||
typed(typ, fn.Nname)
|
||||
fn.SetTypecheck(1)
|
||||
|
||||
fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn)
|
||||
typed(typ, fn.OClosure)
|
||||
fn.SetTypecheck(1)
|
||||
|
||||
g.funcBody(fn, nil, expr.Type, expr.Body)
|
||||
|
||||
|
@ -399,9 +393,7 @@ func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
|
|||
cv.SetWalkdef(1)
|
||||
}
|
||||
|
||||
g.target.Decls = append(g.target.Decls, fn)
|
||||
|
||||
return fn.OClosure
|
||||
return ir.UseClosure(fn.OClosure, g.target)
|
||||
}
|
||||
|
||||
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
|
||||
|
|
|
@ -110,25 +110,35 @@ func LoadPackage(filenames []string) {
|
|||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
// TODO(gri) Remove this again once we have a fix for #25838.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
base.Timer.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
||||
n := typecheck.Target.Decls[i]
|
||||
if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).X.Alias()) {
|
||||
typecheck.Target.Decls[i] = typecheck.Stmt(n)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Phase 2: Variable assignments.
|
||||
// To check interface assignments, depends on phase 1.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
base.Timer.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
||||
n := typecheck.Target.Decls[i]
|
||||
if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() {
|
||||
typecheck.Target.Decls[i] = typecheck.Stmt(n)
|
||||
for phase, name := range []string{"top1", "top2"} {
|
||||
base.Timer.Start("fe", "typecheck", name)
|
||||
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
||||
n := typecheck.Target.Decls[i]
|
||||
op := n.Op()
|
||||
|
||||
// Closure function declarations are typechecked as part of the
|
||||
// closure expression.
|
||||
if fn, ok := n.(*ir.Func); ok && fn.OClosure != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't actually add ir.ODCL nodes to Target.Decls. Make sure of that.
|
||||
if op == ir.ODCL {
|
||||
base.FatalfAt(n.Pos(), "unexpected top declaration: %v", op)
|
||||
}
|
||||
|
||||
// Identify declarations that should be deferred to the second
|
||||
// iteration.
|
||||
late := op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias()
|
||||
|
||||
if late == (phase == 1) {
|
||||
typecheck.Target.Decls[i] = typecheck.Stmt(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,16 +147,15 @@ func LoadPackage(filenames []string) {
|
|||
base.Timer.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
||||
n := typecheck.Target.Decls[i]
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
|
||||
if base.Flag.W > 1 {
|
||||
s := fmt.Sprintf("\nbefore typecheck %v", n)
|
||||
ir.Dump(s, n)
|
||||
s := fmt.Sprintf("\nbefore typecheck %v", fn)
|
||||
ir.Dump(s, fn)
|
||||
}
|
||||
typecheck.FuncBody(n.(*ir.Func))
|
||||
typecheck.FuncBody(fn)
|
||||
if base.Flag.W > 1 {
|
||||
s := fmt.Sprintf("\nafter typecheck %v", n)
|
||||
ir.Dump(s, n)
|
||||
s := fmt.Sprintf("\nafter typecheck %v", fn)
|
||||
ir.Dump(s, fn)
|
||||
}
|
||||
fcount++
|
||||
}
|
||||
|
@ -1794,24 +1803,14 @@ func fakeRecv() *ir.Field {
|
|||
}
|
||||
|
||||
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
|
||||
xtype := p.typeExpr(expr.Type)
|
||||
|
||||
fn := ir.NewFunc(p.pos(expr))
|
||||
fn.SetIsHiddenClosure(ir.CurFunc != nil)
|
||||
|
||||
fn.Nname = ir.NewNameAt(p.pos(expr), ir.BlankNode.Sym()) // filled in by tcClosure
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Ntype = xtype
|
||||
fn.Nname.Defn = fn
|
||||
|
||||
clo := ir.NewClosureExpr(p.pos(expr), fn)
|
||||
fn.OClosure = clo
|
||||
fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc)
|
||||
fn.Nname.Ntype = p.typeExpr(expr.Type)
|
||||
|
||||
p.funcBody(fn, expr.Body)
|
||||
|
||||
ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
|
||||
|
||||
return clo
|
||||
return fn.OClosure
|
||||
}
|
||||
|
||||
// A function named init is a special case.
|
||||
|
|
|
@ -280,8 +280,8 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
|||
// }
|
||||
|
||||
// Make a new internal function.
|
||||
fn := ir.NewFunc(pos)
|
||||
fn.SetIsHiddenClosure(true)
|
||||
fn := ir.NewClosureFunc(pos, outer)
|
||||
ir.NameClosure(fn.OClosure, outer)
|
||||
|
||||
// This is the dictionary we want to use.
|
||||
// It may be a constant, or it may be a dictionary acquired from the outer function's dictionary.
|
||||
|
@ -346,13 +346,8 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
|||
|
||||
// Build an internal function with the right signature.
|
||||
closureType := types.NewSignature(x.Type().Pkg(), nil, nil, formalParams, formalResults)
|
||||
sym := typecheck.ClosureName(outer)
|
||||
sym.SetFunc(true)
|
||||
fn.Nname = ir.NewNameAt(pos, sym)
|
||||
fn.Nname.Class = ir.PFUNC
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Defn = fn
|
||||
typed(closureType, fn.Nname)
|
||||
typed(x.Type(), fn.OClosure)
|
||||
fn.SetTypecheck(1)
|
||||
|
||||
// Build body of closure. This involves just calling the wrapped function directly
|
||||
|
@ -401,15 +396,12 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
|||
typecheck.Stmt(innerCall)
|
||||
ir.CurFunc = nil
|
||||
fn.Body = []ir.Node{innerCall}
|
||||
if outer == nil {
|
||||
g.target.Decls = append(g.target.Decls, fn)
|
||||
}
|
||||
|
||||
// We're all done with the captured dictionary (and receiver, for method values).
|
||||
ir.FinishCaptureNames(pos, outer, fn)
|
||||
|
||||
// Make a closure referencing our new internal function.
|
||||
c := ir.NewClosureExpr(pos, fn)
|
||||
c := ir.UseClosure(fn.OClosure, g.target)
|
||||
var init []ir.Node
|
||||
if outer != nil {
|
||||
init = append(init, dictAssign)
|
||||
|
@ -417,9 +409,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
|||
if rcvrValue != nil {
|
||||
init = append(init, rcvrAssign)
|
||||
}
|
||||
c.SetInit(init)
|
||||
typed(x.Type(), c)
|
||||
return c
|
||||
return ir.InitExpr(init, c)
|
||||
}
|
||||
|
||||
// instantiateMethods instantiates all the methods of all fully-instantiated
|
||||
|
@ -859,24 +849,18 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
|||
}
|
||||
|
||||
case ir.OCLOSURE:
|
||||
// We're going to create a new closure from scratch, so clear m
|
||||
// to avoid using the ir.Copy by accident until we reassign it.
|
||||
m = nil
|
||||
|
||||
x := x.(*ir.ClosureExpr)
|
||||
// Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
|
||||
// x.Func.Body.
|
||||
oldfn := x.Func
|
||||
newfn := ir.NewFunc(oldfn.Pos())
|
||||
if oldfn.ClosureCalled() {
|
||||
newfn.SetClosureCalled(true)
|
||||
}
|
||||
newfn.SetIsHiddenClosure(true)
|
||||
m.(*ir.ClosureExpr).Func = newfn
|
||||
// Closure name can already have brackets, if it derives
|
||||
// from a generic method
|
||||
newsym := typecheck.MakeInstName(oldfn.Nname.Sym(), subst.ts.Targs, subst.isMethod)
|
||||
newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), newsym)
|
||||
newfn.Nname.Func = newfn
|
||||
newfn.Nname.Defn = newfn
|
||||
ir.MarkFunc(newfn.Nname)
|
||||
newfn.OClosure = m.(*ir.ClosureExpr)
|
||||
newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf)
|
||||
ir.NameClosure(newfn.OClosure, subst.newf)
|
||||
|
||||
newfn.SetClosureCalled(oldfn.ClosureCalled())
|
||||
|
||||
saveNewf := subst.newf
|
||||
ir.CurFunc = newfn
|
||||
|
@ -885,7 +869,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
|||
newfn.ClosureVars = subst.namelist(oldfn.ClosureVars)
|
||||
|
||||
typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
|
||||
typed(newfn.Nname.Type(), m)
|
||||
typed(newfn.Nname.Type(), newfn.OClosure)
|
||||
newfn.SetTypecheck(1)
|
||||
|
||||
// Make sure type of closure function is set before doing body.
|
||||
|
@ -893,7 +877,8 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
|||
subst.newf = saveNewf
|
||||
ir.CurFunc = saveNewf
|
||||
|
||||
subst.g.target.Decls = append(subst.g.target.Decls, newfn)
|
||||
m = ir.UseClosure(newfn.OClosure, subst.g.target)
|
||||
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
|
||||
|
||||
case ir.OCONVIFACE:
|
||||
x := x.(*ir.ConvExpr)
|
||||
|
|
|
@ -199,35 +199,6 @@ func fnpkg(fn *ir.Name) *types.Pkg {
|
|||
return fn.Sym().Pkg
|
||||
}
|
||||
|
||||
// ClosureName generates a new unique name for a closure within
|
||||
// outerfunc.
|
||||
func ClosureName(outerfunc *ir.Func) *types.Sym {
|
||||
outer := "glob."
|
||||
prefix := "func"
|
||||
gen := &globClosgen
|
||||
|
||||
if outerfunc != nil {
|
||||
if outerfunc.OClosure != nil {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
outer = ir.FuncName(outerfunc)
|
||||
|
||||
// There may be multiple functions named "_". In those
|
||||
// cases, we can't use their individual Closgens as it
|
||||
// would lead to name clashes.
|
||||
if !ir.IsBlank(outerfunc.Nname) {
|
||||
gen = &outerfunc.Closgen
|
||||
}
|
||||
}
|
||||
|
||||
*gen++
|
||||
return Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
|
||||
}
|
||||
|
||||
// globClosgen is like Func.Closgen, but for the global scope.
|
||||
var globClosgen int32
|
||||
|
||||
// MethodValueWrapper returns the DCLFUNC node representing the
|
||||
// wrapper function (*-fm) needed for the given method value. If the
|
||||
// wrapper function hasn't already been created yet, it's created and
|
||||
|
@ -312,8 +283,20 @@ func MethodValueWrapper(dot *ir.SelectorExpr) *ir.Func {
|
|||
// function associated with the closure.
|
||||
// TODO: This creation of the named function should probably really be done in a
|
||||
// separate pass from type-checking.
|
||||
func tcClosure(clo *ir.ClosureExpr, top int) {
|
||||
func tcClosure(clo *ir.ClosureExpr, top int) ir.Node {
|
||||
fn := clo.Func
|
||||
|
||||
// We used to allow IR builders to typecheck the underlying Func
|
||||
// themselves, but that led to too much variety and inconsistency
|
||||
// around who's responsible for naming the function, typechecking
|
||||
// it, or adding it to Target.Decls.
|
||||
//
|
||||
// It's now all or nothing. Callers are still allowed to do these
|
||||
// themselves, but then they assume responsibility for all of them.
|
||||
if fn.Typecheck() == 1 {
|
||||
base.FatalfAt(fn.Pos(), "underlying closure func already typechecked: %v", fn)
|
||||
}
|
||||
|
||||
// Set current associated iota value, so iota can be used inside
|
||||
// function in ConstSpec, see issue #22344
|
||||
if x := getIotaValue(); x >= 0 {
|
||||
|
@ -322,30 +305,14 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
|
|||
|
||||
fn.SetClosureCalled(top&ctxCallee != 0)
|
||||
|
||||
// Do not typecheck fn twice, otherwise, we will end up pushing
|
||||
// fn to Target.Decls multiple times, causing InitLSym called twice.
|
||||
// See #30709
|
||||
if fn.Typecheck() == 1 {
|
||||
clo.SetType(fn.Type())
|
||||
return
|
||||
}
|
||||
|
||||
// Don't give a name and add to Target.Decls if we are typechecking an inlined
|
||||
// body in ImportedBody(), since we only want to create the named function
|
||||
// when the closure is actually inlined (and then we force a typecheck
|
||||
// explicitly in (*inlsubst).node()).
|
||||
if !inTypeCheckInl {
|
||||
fn.Nname.SetSym(ClosureName(ir.CurFunc))
|
||||
ir.MarkFunc(fn.Nname)
|
||||
}
|
||||
ir.NameClosure(clo, ir.CurFunc)
|
||||
Func(fn)
|
||||
clo.SetType(fn.Type())
|
||||
|
||||
// Type check the body now, but only if we're inside a function.
|
||||
// At top level (in a variable initialization: curfn==nil) we're not
|
||||
// ready to type check code yet; we'll check it later, because the
|
||||
// underlying closure function we create is added to Target.Decls.
|
||||
if ir.CurFunc != nil && clo.Type() != nil {
|
||||
if ir.CurFunc != nil {
|
||||
oldfn := ir.CurFunc
|
||||
ir.CurFunc = fn
|
||||
Stmts(fn.Body)
|
||||
|
@ -371,14 +338,17 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
|
|||
}
|
||||
fn.ClosureVars = fn.ClosureVars[:out]
|
||||
|
||||
if base.Flag.W > 1 {
|
||||
s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn))
|
||||
ir.Dump(s, fn)
|
||||
}
|
||||
if !inTypeCheckInl {
|
||||
// Add function to Target.Decls once only when we give it a name
|
||||
Target.Decls = append(Target.Decls, fn)
|
||||
clo.SetType(fn.Type())
|
||||
|
||||
target := Target
|
||||
if inTypeCheckInl {
|
||||
// We're typechecking an imported function, so it's not actually
|
||||
// part of Target. Skip adding it to Target.Decls so we don't
|
||||
// compile it again.
|
||||
target = nil
|
||||
}
|
||||
|
||||
return ir.UseClosure(clo, target)
|
||||
}
|
||||
|
||||
// type check function definition
|
||||
|
|
|
@ -1283,12 +1283,7 @@ func (r *importReader) node() ir.Node {
|
|||
|
||||
// All the remaining code below is similar to (*noder).funcLit(), but
|
||||
// with Dcls and ClosureVars lists already set up
|
||||
fn := ir.NewFunc(pos)
|
||||
fn.SetIsHiddenClosure(true)
|
||||
fn.Nname = ir.NewNameAt(pos, ir.BlankNode.Sym())
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Ntype = ir.TypeNode(typ)
|
||||
fn.Nname.Defn = fn
|
||||
fn := ir.NewClosureFunc(pos, r.curfn)
|
||||
fn.Nname.SetType(typ)
|
||||
|
||||
cvars := make([]*ir.Name, r.int64())
|
||||
|
@ -1321,18 +1316,10 @@ func (r *importReader) node() ir.Node {
|
|||
|
||||
ir.FinishCaptureNames(pos, r.curfn, fn)
|
||||
|
||||
clo := ir.NewClosureExpr(pos, fn)
|
||||
fn.OClosure = clo
|
||||
clo := fn.OClosure
|
||||
if go117ExportTypes {
|
||||
clo.SetType(typ)
|
||||
}
|
||||
if r.curfn.Type().HasTParam() {
|
||||
// Generic functions aren't inlined, so give the closure a
|
||||
// function name now, which is then available for use
|
||||
// (after appending the type args) for each stenciling.
|
||||
fn.Nname.SetSym(ClosureName(r.curfn))
|
||||
}
|
||||
|
||||
return clo
|
||||
|
||||
case ir.OSTRUCTLIT:
|
||||
|
|
|
@ -787,11 +787,7 @@ func typecheck1(n ir.Node, top int) ir.Node {
|
|||
|
||||
case ir.OCLOSURE:
|
||||
n := n.(*ir.ClosureExpr)
|
||||
tcClosure(n, top)
|
||||
if n.Type() == nil {
|
||||
return n
|
||||
}
|
||||
return n
|
||||
return tcClosure(n, top)
|
||||
|
||||
case ir.OITAB:
|
||||
n := n.(*ir.UnaryExpr)
|
||||
|
|
|
@ -1704,14 +1704,10 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
|
|||
}
|
||||
|
||||
// Create a new no-argument function that we'll hand off to defer.
|
||||
var noFuncArgs []*ir.Field
|
||||
noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
|
||||
wrapGoDefer_prgen++
|
||||
outerfn := ir.CurFunc
|
||||
wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
|
||||
sym := types.LocalPkg.Lookup(wrapname)
|
||||
fn := typecheck.DeclFunc(sym, noargst)
|
||||
fn.SetIsHiddenClosure(true)
|
||||
|
||||
fn := ir.NewClosureFunc(base.Pos, outerfn)
|
||||
fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil))
|
||||
fn.SetWrapper(true)
|
||||
|
||||
// helper for capturing reference to a var declared in an outer scope.
|
||||
|
@ -1741,7 +1737,6 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
|
|||
if methSelectorExpr != nil {
|
||||
methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
|
||||
}
|
||||
ir.FinishCaptureNames(n.Pos(), outerfn, fn)
|
||||
|
||||
// This flags a builtin as opposed to a regular call.
|
||||
irregular := (call.Op() != ir.OCALLFUNC &&
|
||||
|
@ -1755,23 +1750,12 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
|
|||
}
|
||||
newcall := mkNewCall(call.Pos(), op, callX, newCallArgs)
|
||||
|
||||
// Type-check the result.
|
||||
if !irregular {
|
||||
typecheck.Call(newcall.(*ir.CallExpr))
|
||||
} else {
|
||||
typecheck.Stmt(newcall)
|
||||
}
|
||||
|
||||
// Finalize body, register function on the main decls list.
|
||||
fn.Body = []ir.Node{newcall}
|
||||
typecheck.FinishFuncBody()
|
||||
typecheck.Func(fn)
|
||||
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
|
||||
ir.FinishCaptureNames(n.Pos(), outerfn, fn)
|
||||
|
||||
// Create closure expr
|
||||
clo := ir.NewClosureExpr(n.Pos(), fn)
|
||||
fn.OClosure = clo
|
||||
clo.SetType(fn.Type())
|
||||
clo := typecheck.Expr(fn.OClosure).(*ir.ClosureExpr)
|
||||
|
||||
// Set escape properties for closure.
|
||||
if n.Op() == ir.OGO {
|
||||
|
@ -1788,7 +1772,7 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
|
|||
}
|
||||
|
||||
// Create new top level call to closure over argless function.
|
||||
topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
|
||||
topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, nil)
|
||||
typecheck.Call(topcall)
|
||||
|
||||
// Tag the call to insure that directClosureCall doesn't undo our work.
|
||||
|
|
Loading…
Reference in a new issue