// 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
        // 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")
 
        // 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
 
        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
 
        "cmd/compile/internal/types"
        "cmd/internal/obj"
        "cmd/internal/src"
+       "fmt"
 )
 
 // A Func corresponds to a single function in a Go program
 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
+}
 
 }
 
 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)
 
                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 {
 
        //   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)
+                       }
                }
        }
 
        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++
                }
 }
 
 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.
 
        //   }
 
        // 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.
 
        // 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
        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)
        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
                        }
 
                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
                        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.
                        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)
 
        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
 // 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 {
 
        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)
        }
        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
 
 
                // 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())
 
                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:
 
 
        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)
 
        }
 
        // 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.
        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 &&
        }
        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 {
        }
 
        // 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.