[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:
Matthew Dempsky 2021-06-11 03:09:26 -07:00
parent 8f00eb0099
commit 0132b91127
10 changed files with 200 additions and 196 deletions

View File

@ -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 does the necessary substitions for a ClosureExpr n and returns the new
// closure node. // closure node.
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.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 // Prior to the subst edit, set a flag in the inlsubst to
// indicated that we don't want to update the source positions in // 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 // 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. // issue #46234 for more details.
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate) defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
subst.noPosUpdate = true subst.noPosUpdate = true
ir.EditChildren(m, subst.edit)
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc)) //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 oldfn := n.Func
newfn := ir.NewFunc(oldfn.Pos()) newfn := ir.NewClosureFunc(oldfn.Pos(), outerfunc)
// 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
// Ntype can be nil for -G=3 mode. // Ntype can be nil for -G=3 mode.
if oldfn.Nname.Ntype != nil { if oldfn.Nname.Ntype != nil {
newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype) 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 { if subst.newclofn != nil {
//fmt.Printf("Inlining a closure with a nested closure\n") //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 // Actually create the named function for the closure, now that
// the closure is inlined in a specific function. // the closure is inlined in a specific function.
m.SetTypecheck(0) newclo := newfn.OClosure
newclo.SetInit(subst.list(n.Init()))
if oldfn.ClosureCalled() { if oldfn.ClosureCalled() {
typecheck.Callee(m) return typecheck.Callee(newclo)
} else { } else {
typecheck.Expr(m) return typecheck.Expr(newclo)
} }
return m
} }
// node recursively copies a node from the saved pristine body of the // node recursively copies a node from the saved pristine body of the

View File

@ -195,6 +195,7 @@ type ClosureExpr struct {
IsGoWrap bool // whether this is wrapper closure of a go statement IsGoWrap bool // whether this is wrapper closure of a go statement
} }
// Deprecated: Use NewClosureFunc instead.
func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr { func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr {
n := &ClosureExpr{Func: fn} n := &ClosureExpr{Func: fn}
n.op = OCLOSURE n.op = OCLOSURE

View File

@ -9,6 +9,7 @@ import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"fmt"
) )
// A Func corresponds to a single function in a Go program // A Func corresponds to a single function in a Go program
@ -311,3 +312,102 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
func IsTrivialClosure(clo *ClosureExpr) bool { func IsTrivialClosure(clo *ClosureExpr) bool {
return len(clo.Func.ClosureVars) == 0 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
}

View File

@ -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 { func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
fn := ir.NewFunc(g.pos(expr)) fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc)
fn.SetIsHiddenClosure(ir.CurFunc != nil) 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) typ := g.typ(typ2)
fn.Nname.Func = fn
fn.Nname.Defn = fn
typed(typ, fn.Nname) typed(typ, fn.Nname)
fn.SetTypecheck(1)
fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn)
typed(typ, fn.OClosure) typed(typ, fn.OClosure)
fn.SetTypecheck(1)
g.funcBody(fn, nil, expr.Type, expr.Body) 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) cv.SetWalkdef(1)
} }
g.target.Decls = append(g.target.Decls, fn) return ir.UseClosure(fn.OClosure, g.target)
return fn.OClosure
} }
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type { func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {

View File

@ -110,25 +110,35 @@ func LoadPackage(filenames []string) {
// We also defer type alias declarations until phase 2 // We also defer type alias declarations until phase 2
// to avoid cycles like #18640. // to avoid cycles like #18640.
// TODO(gri) Remove this again once we have a fix for #25838. // 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. // Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1. // To check interface assignments, depends on phase 1.
// Don't use range--typecheck can add closures to Target.Decls. // Don't use range--typecheck can add closures to Target.Decls.
base.Timer.Start("fe", "typecheck", "top2") for phase, name := range []string{"top1", "top2"} {
for i := 0; i < len(typecheck.Target.Decls); i++ { base.Timer.Start("fe", "typecheck", name)
n := typecheck.Target.Decls[i] for i := 0; i < len(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() { n := typecheck.Target.Decls[i]
typecheck.Target.Decls[i] = typecheck.Stmt(n) 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") base.Timer.Start("fe", "typecheck", "func")
var fcount int64 var fcount int64
for i := 0; i < len(typecheck.Target.Decls); i++ { for i := 0; i < len(typecheck.Target.Decls); i++ {
n := typecheck.Target.Decls[i] if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
if n.Op() == ir.ODCLFUNC {
if base.Flag.W > 1 { if base.Flag.W > 1 {
s := fmt.Sprintf("\nbefore typecheck %v", n) s := fmt.Sprintf("\nbefore typecheck %v", fn)
ir.Dump(s, n) ir.Dump(s, fn)
} }
typecheck.FuncBody(n.(*ir.Func)) typecheck.FuncBody(fn)
if base.Flag.W > 1 { if base.Flag.W > 1 {
s := fmt.Sprintf("\nafter typecheck %v", n) s := fmt.Sprintf("\nafter typecheck %v", fn)
ir.Dump(s, n) ir.Dump(s, fn)
} }
fcount++ fcount++
} }
@ -1794,24 +1803,14 @@ func fakeRecv() *ir.Field {
} }
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node { func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
xtype := p.typeExpr(expr.Type) fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc)
fn.Nname.Ntype = 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
p.funcBody(fn, expr.Body) p.funcBody(fn, expr.Body)
ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn) ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
return clo return fn.OClosure
} }
// A function named init is a special case. // A function named init is a special case.

View File

@ -280,8 +280,8 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// } // }
// Make a new internal function. // Make a new internal function.
fn := ir.NewFunc(pos) fn := ir.NewClosureFunc(pos, outer)
fn.SetIsHiddenClosure(true) ir.NameClosure(fn.OClosure, outer)
// This is the dictionary we want to use. // 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. // 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. // Build an internal function with the right signature.
closureType := types.NewSignature(x.Type().Pkg(), nil, nil, formalParams, formalResults) 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(closureType, fn.Nname)
typed(x.Type(), fn.OClosure)
fn.SetTypecheck(1) fn.SetTypecheck(1)
// Build body of closure. This involves just calling the wrapped function directly // 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) typecheck.Stmt(innerCall)
ir.CurFunc = nil ir.CurFunc = nil
fn.Body = []ir.Node{innerCall} 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). // We're all done with the captured dictionary (and receiver, for method values).
ir.FinishCaptureNames(pos, outer, fn) ir.FinishCaptureNames(pos, outer, fn)
// Make a closure referencing our new internal function. // Make a closure referencing our new internal function.
c := ir.NewClosureExpr(pos, fn) c := ir.UseClosure(fn.OClosure, g.target)
var init []ir.Node var init []ir.Node
if outer != nil { if outer != nil {
init = append(init, dictAssign) init = append(init, dictAssign)
@ -417,9 +409,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
if rcvrValue != nil { if rcvrValue != nil {
init = append(init, rcvrAssign) init = append(init, rcvrAssign)
} }
c.SetInit(init) return ir.InitExpr(init, c)
typed(x.Type(), c)
return c
} }
// instantiateMethods instantiates all the methods of all fully-instantiated // 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: 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) x := x.(*ir.ClosureExpr)
// Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and // Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
// x.Func.Body. // x.Func.Body.
oldfn := x.Func oldfn := x.Func
newfn := ir.NewFunc(oldfn.Pos()) newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf)
if oldfn.ClosureCalled() { ir.NameClosure(newfn.OClosure, subst.newf)
newfn.SetClosureCalled(true)
} newfn.SetClosureCalled(oldfn.ClosureCalled())
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)
saveNewf := subst.newf saveNewf := subst.newf
ir.CurFunc = newfn ir.CurFunc = newfn
@ -885,7 +869,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
newfn.ClosureVars = subst.namelist(oldfn.ClosureVars) newfn.ClosureVars = subst.namelist(oldfn.ClosureVars)
typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname) typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
typed(newfn.Nname.Type(), m) typed(newfn.Nname.Type(), newfn.OClosure)
newfn.SetTypecheck(1) newfn.SetTypecheck(1)
// Make sure type of closure function is set before doing body. // 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 subst.newf = saveNewf
ir.CurFunc = 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: case ir.OCONVIFACE:
x := x.(*ir.ConvExpr) x := x.(*ir.ConvExpr)

View File

@ -199,35 +199,6 @@ func fnpkg(fn *ir.Name) *types.Pkg {
return fn.Sym().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 // MethodValueWrapper returns the DCLFUNC node representing the
// wrapper function (*-fm) needed for the given method value. If the // wrapper function (*-fm) needed for the given method value. If the
// wrapper function hasn't already been created yet, it's created and // 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. // function associated with the closure.
// TODO: This creation of the named function should probably really be done in a // TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking. // separate pass from type-checking.
func tcClosure(clo *ir.ClosureExpr, top int) { func tcClosure(clo *ir.ClosureExpr, top int) ir.Node {
fn := clo.Func 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 // Set current associated iota value, so iota can be used inside
// function in ConstSpec, see issue #22344 // function in ConstSpec, see issue #22344
if x := getIotaValue(); x >= 0 { if x := getIotaValue(); x >= 0 {
@ -322,30 +305,14 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
fn.SetClosureCalled(top&ctxCallee != 0) fn.SetClosureCalled(top&ctxCallee != 0)
// Do not typecheck fn twice, otherwise, we will end up pushing ir.NameClosure(clo, ir.CurFunc)
// 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)
}
Func(fn) Func(fn)
clo.SetType(fn.Type())
// Type check the body now, but only if we're inside a function. // 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 // 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 // ready to type check code yet; we'll check it later, because the
// underlying closure function we create is added to Target.Decls. // underlying closure function we create is added to Target.Decls.
if ir.CurFunc != nil && clo.Type() != nil { if ir.CurFunc != nil {
oldfn := ir.CurFunc oldfn := ir.CurFunc
ir.CurFunc = fn ir.CurFunc = fn
Stmts(fn.Body) Stmts(fn.Body)
@ -371,14 +338,17 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
} }
fn.ClosureVars = fn.ClosureVars[:out] fn.ClosureVars = fn.ClosureVars[:out]
if base.Flag.W > 1 { clo.SetType(fn.Type())
s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn))
ir.Dump(s, fn) target := Target
} if inTypeCheckInl {
if !inTypeCheckInl { // We're typechecking an imported function, so it's not actually
// Add function to Target.Decls once only when we give it a name // part of Target. Skip adding it to Target.Decls so we don't
Target.Decls = append(Target.Decls, fn) // compile it again.
target = nil
} }
return ir.UseClosure(clo, target)
} }
// type check function definition // type check function definition

View File

@ -1283,12 +1283,7 @@ func (r *importReader) node() ir.Node {
// All the remaining code below is similar to (*noder).funcLit(), but // All the remaining code below is similar to (*noder).funcLit(), but
// with Dcls and ClosureVars lists already set up // with Dcls and ClosureVars lists already set up
fn := ir.NewFunc(pos) fn := ir.NewClosureFunc(pos, r.curfn)
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.Nname.SetType(typ) fn.Nname.SetType(typ)
cvars := make([]*ir.Name, r.int64()) cvars := make([]*ir.Name, r.int64())
@ -1321,18 +1316,10 @@ func (r *importReader) node() ir.Node {
ir.FinishCaptureNames(pos, r.curfn, fn) ir.FinishCaptureNames(pos, r.curfn, fn)
clo := ir.NewClosureExpr(pos, fn) clo := fn.OClosure
fn.OClosure = clo
if go117ExportTypes { if go117ExportTypes {
clo.SetType(typ) 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 return clo
case ir.OSTRUCTLIT: case ir.OSTRUCTLIT:

View File

@ -787,11 +787,7 @@ func typecheck1(n ir.Node, top int) ir.Node {
case ir.OCLOSURE: case ir.OCLOSURE:
n := n.(*ir.ClosureExpr) n := n.(*ir.ClosureExpr)
tcClosure(n, top) return tcClosure(n, top)
if n.Type() == nil {
return n
}
return n
case ir.OITAB: case ir.OITAB:
n := n.(*ir.UnaryExpr) n := n.(*ir.UnaryExpr)

View File

@ -1704,14 +1704,10 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
} }
// Create a new no-argument function that we'll hand off to defer. // 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 outerfn := ir.CurFunc
wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
sym := types.LocalPkg.Lookup(wrapname) fn := ir.NewClosureFunc(base.Pos, outerfn)
fn := typecheck.DeclFunc(sym, noargst) fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil))
fn.SetIsHiddenClosure(true)
fn.SetWrapper(true) fn.SetWrapper(true)
// helper for capturing reference to a var declared in an outer scope. // 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 { if methSelectorExpr != nil {
methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name)) 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. // This flags a builtin as opposed to a regular call.
irregular := (call.Op() != ir.OCALLFUNC && irregular := (call.Op() != ir.OCALLFUNC &&
@ -1755,23 +1750,12 @@ func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
} }
newcall := mkNewCall(call.Pos(), op, callX, newCallArgs) 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. // Finalize body, register function on the main decls list.
fn.Body = []ir.Node{newcall} fn.Body = []ir.Node{newcall}
typecheck.FinishFuncBody() ir.FinishCaptureNames(n.Pos(), outerfn, fn)
typecheck.Func(fn)
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
// Create closure expr // Create closure expr
clo := ir.NewClosureExpr(n.Pos(), fn) clo := typecheck.Expr(fn.OClosure).(*ir.ClosureExpr)
fn.OClosure = clo
clo.SetType(fn.Type())
// Set escape properties for closure. // Set escape properties for closure.
if n.Op() == ir.OGO { 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. // 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) typecheck.Call(topcall)
// Tag the call to insure that directClosureCall doesn't undo our work. // Tag the call to insure that directClosureCall doesn't undo our work.