diff options
Diffstat (limited to 'src/cmd/compile/internal/ir/func.go')
-rw-r--r-- | src/cmd/compile/internal/ir/func.go | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go index 1d76813a4c..3d4f8c4486 100644 --- a/src/cmd/compile/internal/ir/func.go +++ b/src/cmd/compile/internal/ir/func.go @@ -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 +} |