]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: always construct typechecked closures
authorMatthew Dempsky <mdempsky@google.com>
Thu, 17 Aug 2023 01:56:41 +0000 (18:56 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 17 Aug 2023 19:36:26 +0000 (19:36 +0000)
This CL extends ir.NewClosureFunc to take the signature type argument,
and to handle naming the closure and adding it to typecheck.Target.

It also removes the code for typechecking OCLOSURE and ODCLFUNC nodes,
by having them always constructed as typechecked. ODCLFUNC node
construction will be further simplified in the followup CL.

Change-Id: Iabde4557d33051ee470a3bc4fd49599490024cba
Reviewed-on: https://go-review.googlesource.com/c/go/+/520337
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
12 files changed:
src/cmd/compile/internal/escape/call.go
src/cmd/compile/internal/ir/func.go
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/noder/unified.go
src/cmd/compile/internal/pkginit/init.go
src/cmd/compile/internal/reflectdata/alg.go
src/cmd/compile/internal/ssagen/abi.go
src/cmd/compile/internal/staticinit/sched.go
src/cmd/compile/internal/typecheck/dcl.go
src/cmd/compile/internal/typecheck/func.go
src/cmd/compile/internal/typecheck/stmt.go
src/cmd/compile/internal/typecheck/typecheck.go

index fe0c542ed6737048ed7ed9737fc9db5588d6117c..d87dca23e12a4dafc99c6cb60b2fc57774c1d877 100644 (file)
@@ -256,9 +256,8 @@ func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
        }
 
        // Create a new no-argument function that we'll hand off to defer.
-       fn := ir.NewClosureFunc(n.Pos(), true)
+       fn := ir.NewClosureFunc(n.Pos(), n.Pos(), types.NewSignature(nil, nil, nil), e.curfn, typecheck.Target)
        fn.SetWrapper(true)
-       fn.Nname.SetType(types.NewSignature(nil, nil, nil))
        fn.SetEsc(escFuncTagged) // no params; effectively tagged already
        fn.Body = []ir.Node{call}
        if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC {
@@ -272,6 +271,7 @@ func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
        }
 
        clo := fn.OClosure
+
        if n.Op() == ir.OGO {
                clo.IsGoWrap = true
        }
index fa45ccb2dfd19aaf3f08cf1f52b0121d1d98fa8f..406c614d19880ec239df60473e2e71b4a1305034 100644 (file)
@@ -390,80 +390,37 @@ func closureName(outerfn *Func, pos src.XPos) *types.Sym {
        return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
 }
 
-// NewClosureFunc creates a new Func to represent a function literal.
-// If hidden is true, then the closure is marked hidden (i.e., as a
-// function literal contained within another function, rather than a
-// package-scope variable initialization expression).
-func NewClosureFunc(pos src.XPos, hidden bool) *Func {
-       fn := NewFunc(pos)
-       fn.SetIsHiddenClosure(hidden)
-
-       fn.Nname = NewNameAt(pos, BlankNode.Sym(), nil)
-       fn.Nname.Func = fn
-       fn.Nname.Defn = fn
-
-       fn.OClosure = &ClosureExpr{Func: fn}
-       fn.OClosure.op = OCLOSURE
-       fn.OClosure.pos = pos
-
-       return fn
-}
-
-// NameClosure generates a unique for the given function literal,
-// which must have appeared within outerfn.
-func NameClosure(clo *ClosureExpr, outerfn *Func) {
-       fn := clo.Func
-       if fn.IsHiddenClosure() != (outerfn != nil) {
-               base.FatalfAt(clo.Pos(), "closure naming inconsistency: hidden %v, but outer %v", fn.IsHiddenClosure(), outerfn)
-       }
-
-       name := fn.Nname
-       if !IsBlank(name) {
-               base.FatalfAt(clo.Pos(), "closure already named: %v", name)
-       }
+// NewClosureFunc creates a new Func to represent a function literal
+// with the given type.
+//
+// fpos the position used for the underlying ODCLFUNC and ONAME,
+// whereas cpos is the position used for the OCLOSURE. They're
+// separate because in the presence of inlining, the OCLOSURE node
+// should have an inline-adjusted position, whereas the ODCLFUNC and
+// ONAME must not.
+//
+// outerfn is the enclosing function, if any. The returned function is
+// appending to pkg.Funcs.
+func NewClosureFunc(fpos, cpos src.XPos, typ *types.Type, outerfn *Func, pkg *Package) *Func {
+       fn := NewFunc(fpos)
+       fn.SetIsHiddenClosure(outerfn != nil)
 
-       name.SetSym(closureName(outerfn, clo.Pos()))
+       name := NewNameAt(fpos, closureName(outerfn, cpos), typ)
        MarkFunc(name)
-}
+       name.Func = fn
+       name.Defn = fn
+       fn.Nname = name
 
-// UseClosure checks that the given 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
+       clo := &ClosureExpr{Func: fn}
+       clo.op = OCLOSURE
+       clo.pos = cpos
+       fn.OClosure = clo
 
-       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)
-       }
+       fn.SetTypecheck(1)
+       clo.SetType(typ)
+       clo.SetTypecheck(1)
 
-       if pkg != nil {
-               pkg.Funcs = append(pkg.Funcs, fn)
-       }
+       pkg.Funcs = append(pkg.Funcs, 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
+       return fn
 }
index c51963e1c270f97ae4bb9147c6ea577d92c2a626..d71a1fc5fa73f5f6259caa99bd2c373b92c653ef 100644 (file)
@@ -755,6 +755,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
 
                name.Func = ir.NewFunc(r.pos())
                name.Func.Nname = name
+               name.Func.SetTypecheck(1)
 
                if r.hasTypeParams() {
                        name.Func.SetDupok(true)
@@ -999,6 +1000,7 @@ func (r *reader) method(rext *reader) *types.Field {
 
        name.Func = ir.NewFunc(r.pos())
        name.Func.Nname = name
+       name.Func.SetTypecheck(1)
 
        if r.hasTypeParams() {
                name.Func.SetDupok(true)
@@ -1096,8 +1098,6 @@ func (r *reader) funcExt(name *ir.Name, method *types.Sym) {
                }
        }
 
-       typecheck.Func(fn)
-
        if r.Bool() {
                assert(name.Defn == nil)
 
@@ -2722,15 +2722,9 @@ func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack b
        // position instead. See also the explanation in reader.funcLit.
        inlPos := r.inlPos(origPos)
 
-       fn := ir.NewClosureFunc(origPos, r.curfn != nil)
+       // TODO(mdempsky): Remove hard-coding of typecheck.Target.
+       fn := ir.NewClosureFunc(origPos, inlPos, typ, r.curfn, typecheck.Target)
        fn.SetWrapper(true)
-       clo := fn.OClosure
-       clo.SetPos(inlPos)
-       ir.NameClosure(clo, r.curfn)
-
-       setType(fn.Nname, typ)
-       typecheck.Func(fn)
-       setType(clo, fn.Type())
 
        var init ir.Nodes
        for i, n := range captures {
@@ -2767,8 +2761,7 @@ func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack b
        bodyReader[fn] = pri
        pri.funcBody(fn)
 
-       // TODO(mdempsky): Remove hard-coding of typecheck.Target.
-       return ir.InitExpr(init, ir.UseClosure(clo, typecheck.Target))
+       return ir.InitExpr(init, fn.OClosure)
 }
 
 // syntheticSig duplicates and returns the params and results lists
@@ -3120,14 +3113,8 @@ func (r *reader) funcLit() ir.Node {
        xtype2 := r.signature(nil)
        r.suppressInlPos--
 
-       fn := ir.NewClosureFunc(pos, r.curfn != nil)
-       clo := fn.OClosure
-       clo.SetPos(r.inlPos(pos)) // see comment above
-       ir.NameClosure(clo, r.curfn)
-
-       setType(fn.Nname, xtype2)
-       typecheck.Func(fn)
-       setType(clo, fn.Type())
+       // TODO(mdempsky): Remove hard-coding of typecheck.Target.
+       fn := ir.NewClosureFunc(pos, r.inlPos(pos), xtype2, r.curfn, typecheck.Target)
 
        fn.ClosureVars = make([]*ir.Name, 0, r.Len())
        for len(fn.ClosureVars) < cap(fn.ClosureVars) {
@@ -3141,8 +3128,7 @@ func (r *reader) funcLit() ir.Node {
 
        r.addBody(fn, nil)
 
-       // TODO(mdempsky): Remove hard-coding of typecheck.Target.
-       return ir.UseClosure(clo, typecheck.Target)
+       return fn.OClosure
 }
 
 func (r *reader) exprList() []ir.Node {
@@ -3463,6 +3449,7 @@ func unifiedInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.Inlined
        // TODO(mdempsky): This still feels clumsy. Can we do better?
        tmpfn := ir.NewFunc(fn.Pos())
        tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym(), fn.Type())
+       tmpfn.SetTypecheck(1)
        tmpfn.Closgen = callerfn.Closgen
        defer func() { callerfn.Closgen = tmpfn.Closgen }()
 
@@ -3638,6 +3625,7 @@ func expandInline(fn *ir.Func, pri pkgReaderIndex) {
 
        tmpfn := ir.NewFunc(fn.Pos())
        tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym(), fn.Type())
+       tmpfn.SetTypecheck(1)
        tmpfn.ClosureVars = fn.ClosureVars
 
        {
@@ -3861,7 +3849,6 @@ func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Packa
        recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
 
        if !needed {
-               typecheck.Func(fn)
                return
        }
 
@@ -3883,6 +3870,7 @@ func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *t
        fn.Nname = name
 
        setType(name, sig)
+       fn.SetTypecheck(1)
 
        // TODO(mdempsky): De-duplicate with similar logic in funcargs.
        defParams := func(class ir.Class, params *types.Type) {
@@ -3899,8 +3887,6 @@ func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *t
 }
 
 func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
-       typecheck.Func(fn)
-
        ir.WithFunc(fn, func() {
                typecheck.Stmts(fn.Body)
        })
index 25c7b77831f08b7f0772cb603e4f109768ccec42..58d4e029373ae68e86e12524e7221e368b1c5965 100644 (file)
@@ -83,8 +83,6 @@ func unified(m posMap, noders []*noder) {
 
        target := typecheck.Target
 
-       typecheck.TypecheckAllowed = true
-
        localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
        readPackage(localPkgReader, types.LocalPkg, true)
 
index 48c6b03527891400214f2623e15c917b2dcdde23..4a4bc1f39932296a4dadbb8d5fdda425335fe4f0 100644 (file)
@@ -52,7 +52,6 @@ func MakeInit() {
        fn.Body = nf
        typecheck.FinishFuncBody()
 
-       typecheck.Func(fn)
        ir.WithFunc(fn, func() {
                typecheck.Stmts(nf)
        })
@@ -145,7 +144,6 @@ func MakeTask() {
 
                        fnInit.Body.Append(asancall)
                        typecheck.FinishFuncBody()
-                       typecheck.Func(fnInit)
                        ir.CurFunc = fnInit
                        typecheck.Stmts(fnInit.Body)
                        ir.CurFunc = nil
index 20b5b76265359da2af792894ae692792c46f8d1c..a561c1e8b535eb97f63163c93f9eb42727c66b4d 100644 (file)
@@ -237,7 +237,6 @@ func hashFunc(t *types.Type) *ir.Func {
        typecheck.FinishFuncBody()
 
        fn.SetDupok(true)
-       typecheck.Func(fn)
 
        ir.WithFunc(fn, func() {
                typecheck.Stmts(fn.Body)
@@ -623,7 +622,6 @@ func eqFunc(t *types.Type) *ir.Func {
        typecheck.FinishFuncBody()
 
        fn.SetDupok(true)
-       typecheck.Func(fn)
 
        ir.WithFunc(fn, func() {
                typecheck.Stmts(fn.Body)
index 2b08d5a63b4f01724c07147612be6c3645deff53..f01563e776ab9e06dae5c3e1f43aa2aea9640982 100644 (file)
@@ -324,7 +324,6 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
 
        typecheck.FinishFuncBody()
 
-       typecheck.Func(fn)
        ir.CurFunc = fn
        typecheck.Stmts(fn.Body)
 
index 016d0692ed5197abbde77861705f207cb96b526b..cc97d2fcf7a7ebc7caec74c12271717ef80889fe 100644 (file)
@@ -1055,8 +1055,6 @@ func tryWrapGlobalMapInit(n ir.Node) (mapvar *ir.Name, genfn *ir.Func, call ir.N
        newfn.Body = append(newfn.Body, as)
        typecheck.FinishFuncBody()
 
-       typecheck.Func(newfn)
-
        const no = `
        // Register new function with decls.
        typecheck.Target.Decls = append(typecheck.Target.Decls, newfn)
index c0b7c76176f4b6c6d30956b0cabebba3be61a84b..9da2c8f324706cf58ea5285499929e6be09ca83c 100644 (file)
@@ -34,6 +34,7 @@ func DeclFunc(sym *types.Sym, recv *ir.Field, params, results []*ir.Field) *ir.F
        checkdupfields("argument", typ.Recvs().FieldSlice(), typ.Params().FieldSlice(), typ.Results().FieldSlice())
        fn.Nname.SetType(typ)
        fn.Nname.SetTypecheck(1)
+       fn.SetTypecheck(1)
 
        return fn
 }
index eb17e63d9a0114623ce97d7fc04d2061675129d2..3084ac8f343734d53d0feb5c66e011ed25057c49 100644 (file)
@@ -146,62 +146,6 @@ func MethodValueType(n *ir.SelectorExpr) *types.Type {
        return t
 }
 
-// tcClosure typechecks an OCLOSURE node. It also creates the named
-// 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) 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)
-       }
-
-       ir.NameClosure(clo, ir.CurFunc)
-       Func(fn)
-
-       // 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 {
-               oldfn := ir.CurFunc
-               ir.CurFunc = fn
-               Stmts(fn.Body)
-               ir.CurFunc = oldfn
-       }
-
-       out := 0
-       for _, v := range fn.ClosureVars {
-               if v.Type() == nil {
-                       // If v.Type is nil, it means v looked like it was going to be
-                       // used in the closure, but isn't. This happens in struct
-                       // literals like s{f: x} where we can't distinguish whether f is
-                       // a field identifier or expression until resolving s.
-                       continue
-               }
-
-               // type check closed variables outside the closure, so that the
-               // outer frame also captures them.
-               Expr(v.Outer)
-
-               fn.ClosureVars[out] = v
-               out++
-       }
-       fn.ClosureVars = fn.ClosureVars[:out]
-
-       clo.SetType(fn.Type())
-
-       return ir.UseClosure(clo, Target)
-}
-
 // type check function definition
 // To be called by typecheck, not directly.
 // (Call typecheck.Func instead.)
index 274a3e3bbdde9d612ddfceece9aef362c9119a5d..b902fd9a583de1474c12d91692331bf75fa47938 100644 (file)
@@ -63,7 +63,6 @@ func assign(stmt ir.Node, lhs, rhs []ir.Node) {
        // so that the conversion below happens).
 
        checkLHS := func(i int, typ *types.Type) {
-               lhs[i] = Resolve(lhs[i])
                if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Type() == nil {
                        base.Assertf(typ.Kind() == types.TNIL, "unexpected untyped nil")
                        n.SetType(defaultType(typ))
index 8790eac28a76c05fa86d08eba7045ed469996060..a36acb93009ac1a68d0eb244f30a0f34dc710ab7 100644 (file)
@@ -21,10 +21,6 @@ import (
 // to be included in the package-level init function.
 var InitTodoFunc = ir.NewFunc(base.Pos)
 
-var inimport bool // set during import
-
-var TypecheckAllowed bool
-
 var (
        NeedRuntimeType = func(*types.Type) {}
 )
@@ -105,20 +101,6 @@ const (
 // marks variables that escape the local frame.
 // rewrites n.Op to be more specific in some cases.
 
-// Resolve resolves an ONONAME node to a definition, if any. If n is not an ONONAME node,
-// Resolve returns n unchanged. If n is an ONONAME node and not in the same package,
-// then n.Sym() is resolved using import data. Otherwise, Resolve returns
-// n.Sym().Def. An ONONAME node can be created using ir.NewIdent(), so an imported
-// symbol can be resolved via Resolve(ir.NewIdent(src.NoXPos, sym)).
-func Resolve(n ir.Node) (res ir.Node) {
-       if n == nil || n.Op() != ir.ONONAME {
-               return n
-       }
-
-       base.Fatalf("unexpected NONAME node: %+v", n)
-       panic("unreachable")
-}
-
 func typecheckslice(l []ir.Node, top int) {
        for i := range l {
                l[i] = typecheck(l[i], top)
@@ -203,23 +185,11 @@ func cycleTrace(cycle []ir.Node) string {
 
 var typecheck_tcstack []ir.Node
 
-func Func(fn *ir.Func) {
-       new := Stmt(fn)
-       if new != fn {
-               base.Fatalf("typecheck changed func")
-       }
-}
-
 // typecheck type checks node n.
 // The result of typecheck MUST be assigned back to n, e.g.
 //
 //     n.Left = typecheck(n.Left, top)
 func typecheck(n ir.Node, top int) (res ir.Node) {
-       // cannot type check until all the source has been parsed
-       if !TypecheckAllowed {
-               base.Fatalf("early typecheck")
-       }
-
        if n == nil {
                return nil
        }
@@ -236,9 +206,6 @@ func typecheck(n ir.Node, top int) (res ir.Node) {
                n = n.(*ir.ParenExpr).X
        }
 
-       // Resolve definition of name and value of iota lazily.
-       n = Resolve(n)
-
        // Skip typecheck if already done.
        // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
        if n.Typecheck() == 1 || n.Typecheck() == 3 {
@@ -681,10 +648,6 @@ func typecheck1(n ir.Node, top int) ir.Node {
                n := n.(*ir.UnaryExpr)
                return tcUnsafeData(n)
 
-       case ir.OCLOSURE:
-               n := n.(*ir.ClosureExpr)
-               return tcClosure(n, top)
-
        case ir.OITAB:
                n := n.(*ir.UnaryExpr)
                return tcITab(n)