aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2021-06-11 03:09:26 -0700
committerMatthew Dempsky <mdempsky@google.com>2021-06-12 13:53:19 +0000
commit0132b91127c3cf6e19e0e1db014a04219427171e (patch)
tree577fb27f75e4b57916072c31f343bde8f8a6aefc /src/cmd/compile/internal/typecheck
parent8f00eb009978e2e1bb681e698a0b51e20333eb05 (diff)
downloadgo-0132b91127c3cf6e19e0e1db014a04219427171e.tar.gz
go-0132b91127c3cf6e19e0e1db014a04219427171e.zip
[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>
Diffstat (limited to 'src/cmd/compile/internal/typecheck')
-rw-r--r--src/cmd/compile/internal/typecheck/func.go80
-rw-r--r--src/cmd/compile/internal/typecheck/iimport.go17
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go6
3 files changed, 28 insertions, 75 deletions
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index 15756a47e4..bd21977f26 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -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
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 9054a83e6e..b1b3c27898 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -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:
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 9868c2d9a9..a6b21f948a 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -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)