mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Modify rangefunc #next protocol to make it more robust Extra-terrible nests of rangefunc iterators caused the prior implementation to misbehave non-locally (in outer loops). Add more rangefunc exit flag tests, parallel and tricky This tests the assertion that a rangefunc iterator running in parallel can trigger the race detector if any of the parallel goroutines attempts an early exit. It also verifies that if everything else is carefully written, that it does NOT trigger the race detector if all the parts run time completion. Another test tries to rerun a yield function within a loop, so that any per-line shared checking would be fooled. Added all the use-of-body/yield-function checking. These checks handle pathological cases that would cause rangefunc for loops to behave in surprising ways (compared to "regular" for loops). For example, a rangefunc iterator might defer-recover a panic thrown in the syntactic body of a loop; this notices the fault and panics with an explanation Modified closure naming to ID rangefunc bodies Add a "-range<N>" suffix to the name of any closure generated for a rangefunc loop body, as provided in Alessandro Arzilli's CL (which is merged into this one). Fix return values for panicky range functions This removes the delayed implementation of "return x" by ensuring that return values (in rangefunc-return-containing functions) always have names and translating the "return x" into "#rv1 = x" where #rv1 is the synthesized name of the first result. Updates #61405. Change-Id: I933299ecce04ceabcf1c0c2de8e610b2ecd1cfd8 Reviewed-on: https://go-review.googlesource.com/c/go/+/584596 Reviewed-by: Matthew Dempsky <mdempsky@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Tim King <taking@google.com>
619 lines
21 KiB
Go
619 lines
21 KiB
Go
// Copyright 2020 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ir
|
|
|
|
import (
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
"fmt"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// A Func corresponds to a single function in a Go program
|
|
// (and vice versa: each function is denoted by exactly one *Func).
|
|
//
|
|
// There are multiple nodes that represent a Func in the IR.
|
|
//
|
|
// The ONAME node (Func.Nname) is used for plain references to it.
|
|
// The ODCLFUNC node (the Func itself) is used for its declaration code.
|
|
// The OCLOSURE node (Func.OClosure) is used for a reference to a
|
|
// function literal.
|
|
//
|
|
// An imported function will have an ONAME node which points to a Func
|
|
// with an empty body.
|
|
// A declared function or method has an ODCLFUNC (the Func itself) and an ONAME.
|
|
// A function literal is represented directly by an OCLOSURE, but it also
|
|
// has an ODCLFUNC (and a matching ONAME) representing the compiled
|
|
// underlying form of the closure, which accesses the captured variables
|
|
// using a special data structure passed in a register.
|
|
//
|
|
// A method declaration is represented like functions, except f.Sym
|
|
// will be the qualified method name (e.g., "T.m").
|
|
//
|
|
// A method expression (T.M) is represented as an OMETHEXPR node,
|
|
// in which n.Left and n.Right point to the type and method, respectively.
|
|
// Each distinct mention of a method expression in the source code
|
|
// constructs a fresh node.
|
|
//
|
|
// A method value (t.M) is represented by ODOTMETH/ODOTINTER
|
|
// when it is called directly and by OMETHVALUE otherwise.
|
|
// These are like method expressions, except that for ODOTMETH/ODOTINTER,
|
|
// the method name is stored in Sym instead of Right.
|
|
// Each OMETHVALUE ends up being implemented as a new
|
|
// function, a bit like a closure, with its own ODCLFUNC.
|
|
// The OMETHVALUE uses n.Func to record the linkage to
|
|
// the generated ODCLFUNC, but there is no
|
|
// pointer from the Func back to the OMETHVALUE.
|
|
type Func struct {
|
|
miniNode
|
|
Body Nodes
|
|
|
|
Nname *Name // ONAME node
|
|
OClosure *ClosureExpr // OCLOSURE node
|
|
|
|
// ONAME nodes for all params/locals for this func/closure, does NOT
|
|
// include closurevars until transforming closures during walk.
|
|
// Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
|
|
// with PPARAMs and PPARAMOUTs in order corresponding to the function signature.
|
|
// Anonymous and blank params are declared as ~pNN (for PPARAMs) and ~rNN (for PPARAMOUTs).
|
|
Dcl []*Name
|
|
|
|
// ClosureVars lists the free variables that are used within a
|
|
// function literal, but formally declared in an enclosing
|
|
// function. The variables in this slice are the closure function's
|
|
// own copy of the variables, which are used within its function
|
|
// body. They will also each have IsClosureVar set, and will have
|
|
// Byval set if they're captured by value.
|
|
ClosureVars []*Name
|
|
|
|
// Enclosed functions that need to be compiled.
|
|
// Populated during walk.
|
|
Closures []*Func
|
|
|
|
// Parents records the parent scope of each scope within a
|
|
// function. The root scope (0) has no parent, so the i'th
|
|
// scope's parent is stored at Parents[i-1].
|
|
Parents []ScopeID
|
|
|
|
// Marks records scope boundary changes.
|
|
Marks []Mark
|
|
|
|
FieldTrack map[*obj.LSym]struct{}
|
|
DebugInfo interface{}
|
|
LSym *obj.LSym // Linker object in this function's native ABI (Func.ABI)
|
|
|
|
Inl *Inline
|
|
|
|
// RangeParent, if non-nil, is the first non-range body function containing
|
|
// the closure for the body of a range function.
|
|
RangeParent *Func
|
|
|
|
// funcLitGen, rangeLitGen and goDeferGen track how many closures have been
|
|
// created in this function for function literals, range-over-func loops,
|
|
// and go/defer wrappers, respectively. Used by closureName for creating
|
|
// unique function names.
|
|
// Tracking goDeferGen separately avoids wrappers throwing off
|
|
// function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11).
|
|
funcLitGen int32
|
|
rangeLitGen int32
|
|
goDeferGen int32
|
|
|
|
Label int32 // largest auto-generated label in this function
|
|
|
|
Endlineno src.XPos
|
|
WBPos src.XPos // position of first write barrier; see SetWBPos
|
|
|
|
Pragma PragmaFlag // go:xxx function annotations
|
|
|
|
flags bitset16
|
|
|
|
// ABI is a function's "definition" ABI. This is the ABI that
|
|
// this function's generated code is expecting to be called by.
|
|
//
|
|
// For most functions, this will be obj.ABIInternal. It may be
|
|
// a different ABI for functions defined in assembly or ABI wrappers.
|
|
//
|
|
// This is included in the export data and tracked across packages.
|
|
ABI obj.ABI
|
|
// ABIRefs is the set of ABIs by which this function is referenced.
|
|
// For ABIs other than this function's definition ABI, the
|
|
// compiler generates ABI wrapper functions. This is only tracked
|
|
// within a package.
|
|
ABIRefs obj.ABISet
|
|
|
|
NumDefers int32 // number of defer calls in the function
|
|
NumReturns int32 // number of explicit returns in the function
|
|
|
|
// NWBRCalls records the LSyms of functions called by this
|
|
// function for go:nowritebarrierrec analysis. Only filled in
|
|
// if nowritebarrierrecCheck != nil.
|
|
NWBRCalls *[]SymAndPos
|
|
|
|
// For wrapper functions, WrappedFunc point to the original Func.
|
|
// Currently only used for go/defer wrappers.
|
|
WrappedFunc *Func
|
|
|
|
// WasmImport is used by the //go:wasmimport directive to store info about
|
|
// a WebAssembly function import.
|
|
WasmImport *WasmImport
|
|
}
|
|
|
|
// WasmImport stores metadata associated with the //go:wasmimport pragma.
|
|
type WasmImport struct {
|
|
Module string
|
|
Name string
|
|
}
|
|
|
|
// NewFunc returns a new Func with the given name and type.
|
|
//
|
|
// fpos is the position of the "func" token, and npos is the position
|
|
// of the name identifier.
|
|
//
|
|
// TODO(mdempsky): I suspect there's no need for separate fpos and
|
|
// npos.
|
|
func NewFunc(fpos, npos src.XPos, sym *types.Sym, typ *types.Type) *Func {
|
|
name := NewNameAt(npos, sym, typ)
|
|
name.Class = PFUNC
|
|
sym.SetFunc(true)
|
|
|
|
fn := &Func{Nname: name}
|
|
fn.pos = fpos
|
|
fn.op = ODCLFUNC
|
|
// Most functions are ABIInternal. The importer or symabis
|
|
// pass may override this.
|
|
fn.ABI = obj.ABIInternal
|
|
fn.SetTypecheck(1)
|
|
|
|
name.Func = fn
|
|
|
|
return fn
|
|
}
|
|
|
|
func (f *Func) isStmt() {}
|
|
|
|
func (n *Func) copy() Node { panic(n.no("copy")) }
|
|
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
|
|
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
|
|
func (n *Func) editChildrenWithHidden(edit func(Node) Node) { editNodes(n.Body, edit) }
|
|
|
|
func (f *Func) Type() *types.Type { return f.Nname.Type() }
|
|
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
|
|
func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }
|
|
func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }
|
|
|
|
// An Inline holds fields used for function bodies that can be inlined.
|
|
type Inline struct {
|
|
Cost int32 // heuristic cost of inlining this function
|
|
|
|
// Copy of Func.Dcl for use during inlining. This copy is needed
|
|
// because the function's Dcl may change from later compiler
|
|
// transformations. This field is also populated when a function
|
|
// from another package is imported and inlined.
|
|
Dcl []*Name
|
|
HaveDcl bool // whether we've loaded Dcl
|
|
|
|
// Function properties, encoded as a string (these are used for
|
|
// making inlining decisions). See cmd/compile/internal/inline/inlheur.
|
|
Properties string
|
|
|
|
// CanDelayResults reports whether it's safe for the inliner to delay
|
|
// initializing the result parameters until immediately before the
|
|
// "return" statement.
|
|
CanDelayResults bool
|
|
}
|
|
|
|
// A Mark represents a scope boundary.
|
|
type Mark struct {
|
|
// Pos is the position of the token that marks the scope
|
|
// change.
|
|
Pos src.XPos
|
|
|
|
// Scope identifies the innermost scope to the right of Pos.
|
|
Scope ScopeID
|
|
}
|
|
|
|
// A ScopeID represents a lexical scope within a function.
|
|
type ScopeID int32
|
|
|
|
const (
|
|
funcDupok = 1 << iota // duplicate definitions ok
|
|
funcWrapper // hide frame from users (elide in tracebacks, don't count as a frame for recover())
|
|
funcABIWrapper // is an ABI wrapper (also set flagWrapper)
|
|
funcNeedctxt // function uses context register (has closure variables)
|
|
// true if closure inside a function; false if a simple function or a
|
|
// closure in a global variable initialization
|
|
funcIsHiddenClosure
|
|
funcIsDeadcodeClosure // true if closure is deadcode
|
|
funcHasDefer // contains a defer statement
|
|
funcNilCheckDisabled // disable nil checks when compiling this function
|
|
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
|
|
funcNeverReturns // function never returns (in most cases calls panic(), os.Exit(), or equivalent)
|
|
funcOpenCodedDeferDisallowed // can't do open-coded defers
|
|
funcClosureResultsLost // closure is called indirectly and we lost track of its results; used by escape analysis
|
|
funcPackageInit // compiler emitted .init func for package
|
|
)
|
|
|
|
type SymAndPos struct {
|
|
Sym *obj.LSym // LSym of callee
|
|
Pos src.XPos // line of call
|
|
}
|
|
|
|
func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
|
|
func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
|
|
func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper != 0 }
|
|
func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
|
|
func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
|
|
func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 }
|
|
func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
|
|
func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
|
|
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
|
|
func (f *Func) NeverReturns() bool { return f.flags&funcNeverReturns != 0 }
|
|
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
|
|
func (f *Func) ClosureResultsLost() bool { return f.flags&funcClosureResultsLost != 0 }
|
|
func (f *Func) IsPackageInit() bool { return f.flags&funcPackageInit != 0 }
|
|
|
|
func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
|
|
func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
|
|
func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, b) }
|
|
func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
|
|
func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
|
|
func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) }
|
|
func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
|
|
func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
|
|
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
|
|
func (f *Func) SetNeverReturns(b bool) { f.flags.set(funcNeverReturns, b) }
|
|
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
|
|
func (f *Func) SetClosureResultsLost(b bool) { f.flags.set(funcClosureResultsLost, b) }
|
|
func (f *Func) SetIsPackageInit(b bool) { f.flags.set(funcPackageInit, b) }
|
|
|
|
func (f *Func) SetWBPos(pos src.XPos) {
|
|
if base.Debug.WB != 0 {
|
|
base.WarnfAt(pos, "write barrier")
|
|
}
|
|
if !f.WBPos.IsKnown() {
|
|
f.WBPos = pos
|
|
}
|
|
}
|
|
|
|
// FuncName returns the name (without the package) of the function f.
|
|
func FuncName(f *Func) string {
|
|
if f == nil || f.Nname == nil {
|
|
return "<nil>"
|
|
}
|
|
return f.Sym().Name
|
|
}
|
|
|
|
// PkgFuncName returns the name of the function referenced by f, with package
|
|
// prepended.
|
|
//
|
|
// This differs from the compiler's internal convention where local functions
|
|
// lack a package. This is primarily useful when the ultimate consumer of this
|
|
// is a human looking at message.
|
|
func PkgFuncName(f *Func) string {
|
|
if f == nil || f.Nname == nil {
|
|
return "<nil>"
|
|
}
|
|
s := f.Sym()
|
|
pkg := s.Pkg
|
|
|
|
return pkg.Path + "." + s.Name
|
|
}
|
|
|
|
// LinkFuncName returns the name of the function f, as it will appear in the
|
|
// symbol table of the final linked binary.
|
|
func LinkFuncName(f *Func) string {
|
|
if f == nil || f.Nname == nil {
|
|
return "<nil>"
|
|
}
|
|
s := f.Sym()
|
|
pkg := s.Pkg
|
|
|
|
return objabi.PathToPrefix(pkg.Path) + "." + s.Name
|
|
}
|
|
|
|
// ParseLinkFuncName parsers a symbol name (as returned from LinkFuncName) back
|
|
// to the package path and local symbol name.
|
|
func ParseLinkFuncName(name string) (pkg, sym string, err error) {
|
|
pkg, sym = splitPkg(name)
|
|
if pkg == "" {
|
|
return "", "", fmt.Errorf("no package path in name")
|
|
}
|
|
|
|
pkg, err = objabi.PrefixToPath(pkg) // unescape
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("malformed package path: %v", err)
|
|
}
|
|
|
|
return pkg, sym, nil
|
|
}
|
|
|
|
// Borrowed from x/mod.
|
|
func modPathOK(r rune) bool {
|
|
if r < utf8.RuneSelf {
|
|
return r == '-' || r == '.' || r == '_' || r == '~' ||
|
|
'0' <= r && r <= '9' ||
|
|
'A' <= r && r <= 'Z' ||
|
|
'a' <= r && r <= 'z'
|
|
}
|
|
return false
|
|
}
|
|
|
|
func escapedImportPathOK(r rune) bool {
|
|
return modPathOK(r) || r == '+' || r == '/' || r == '%'
|
|
}
|
|
|
|
// splitPkg splits the full linker symbol name into package and local symbol
|
|
// name.
|
|
func splitPkg(name string) (pkgpath, sym string) {
|
|
// package-sym split is at first dot after last the / that comes before
|
|
// any characters illegal in a package path.
|
|
|
|
lastSlashIdx := 0
|
|
for i, r := range name {
|
|
// Catches cases like:
|
|
// * example.foo[sync/atomic.Uint64].
|
|
// * example%2ecom.foo[sync/atomic.Uint64].
|
|
//
|
|
// Note that name is still escaped; unescape occurs after splitPkg.
|
|
if !escapedImportPathOK(r) {
|
|
break
|
|
}
|
|
if r == '/' {
|
|
lastSlashIdx = i
|
|
}
|
|
}
|
|
for i := lastSlashIdx; i < len(name); i++ {
|
|
r := name[i]
|
|
if r == '.' {
|
|
return name[:i], name[i+1:]
|
|
}
|
|
}
|
|
|
|
return "", name
|
|
}
|
|
|
|
var CurFunc *Func
|
|
|
|
// WithFunc invokes do with CurFunc and base.Pos set to curfn and
|
|
// curfn.Pos(), respectively, and then restores their previous values
|
|
// before returning.
|
|
func WithFunc(curfn *Func, do func()) {
|
|
oldfn, oldpos := CurFunc, base.Pos
|
|
defer func() { CurFunc, base.Pos = oldfn, oldpos }()
|
|
|
|
CurFunc, base.Pos = curfn, curfn.Pos()
|
|
do()
|
|
}
|
|
|
|
func FuncSymName(s *types.Sym) string {
|
|
return s.Name + "·f"
|
|
}
|
|
|
|
// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags
|
|
// and compiling runtime.
|
|
func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
|
|
if base.Debug.Closure > 0 {
|
|
if clo.Esc() == EscHeap {
|
|
base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
|
|
} else {
|
|
base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
|
|
}
|
|
}
|
|
if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap {
|
|
base.ErrorfAt(clo.Pos(), 0, "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func))
|
|
}
|
|
}
|
|
|
|
// IsTrivialClosure reports whether closure clo has an
|
|
// empty list of captured vars.
|
|
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 at pos.
|
|
func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
|
if outerfn != nil && outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
|
|
outerfn = outerfn.OClosure.Func.RangeParent
|
|
}
|
|
pkg := types.LocalPkg
|
|
outer := "glob."
|
|
var prefix string = "."
|
|
switch why {
|
|
default:
|
|
base.FatalfAt(pos, "closureName: bad Op: %v", why)
|
|
case OCLOSURE:
|
|
if outerfn == nil || outerfn.OClosure == nil {
|
|
prefix = ".func"
|
|
}
|
|
case ORANGE:
|
|
prefix = "-range"
|
|
case OGO:
|
|
prefix = ".gowrap"
|
|
case ODEFER:
|
|
prefix = ".deferwrap"
|
|
}
|
|
gen := &globClosgen
|
|
|
|
// There may be multiple functions named "_". In those
|
|
// cases, we can't use their individual Closgens as it
|
|
// would lead to name clashes.
|
|
if outerfn != nil && !IsBlank(outerfn.Nname) {
|
|
pkg = outerfn.Sym().Pkg
|
|
outer = FuncName(outerfn)
|
|
|
|
switch why {
|
|
case OCLOSURE:
|
|
gen = &outerfn.funcLitGen
|
|
case ORANGE:
|
|
gen = &outerfn.rangeLitGen
|
|
default:
|
|
gen = &outerfn.goDeferGen
|
|
}
|
|
}
|
|
|
|
// If this closure was created due to inlining, then incorporate any
|
|
// inlined functions' names into the closure's linker symbol name
|
|
// too (#60324).
|
|
if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 {
|
|
names := []string{outer}
|
|
base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) {
|
|
names = append(names, call.Name)
|
|
})
|
|
outer = strings.Join(names, ".")
|
|
}
|
|
|
|
*gen++
|
|
return pkg.Lookup(fmt.Sprintf("%s%s%d", outer, prefix, *gen))
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// why is the reason we're generating this Func. It can be OCLOSURE
|
|
// (for a normal function literal) or OGO or ODEFER (for wrapping a
|
|
// call expression that has parameters or results).
|
|
func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package) *Func {
|
|
fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why), typ)
|
|
fn.SetIsHiddenClosure(outerfn != nil)
|
|
if outerfn != nil {
|
|
fn.SetDupok(outerfn.Dupok()) // if the outer function is dupok, so is the closure
|
|
}
|
|
|
|
clo := &ClosureExpr{Func: fn}
|
|
clo.op = OCLOSURE
|
|
clo.pos = cpos
|
|
clo.SetType(typ)
|
|
clo.SetTypecheck(1)
|
|
if why == ORANGE {
|
|
clo.Func.RangeParent = outerfn
|
|
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
|
|
clo.Func.RangeParent = outerfn.OClosure.Func.RangeParent
|
|
}
|
|
}
|
|
fn.OClosure = clo
|
|
|
|
fn.Nname.Defn = fn
|
|
pkg.Funcs = append(pkg.Funcs, fn)
|
|
|
|
return fn
|
|
}
|
|
|
|
// IsFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
|
|
func IsFuncPCIntrinsic(n *CallExpr) bool {
|
|
if n.Op() != OCALLFUNC || n.Fun.Op() != ONAME {
|
|
return false
|
|
}
|
|
fn := n.Fun.(*Name).Sym()
|
|
return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
|
|
fn.Pkg.Path == "internal/abi"
|
|
}
|
|
|
|
// IsIfaceOfFunc inspects whether n is an interface conversion from a direct
|
|
// reference of a func. If so, it returns referenced Func; otherwise nil.
|
|
//
|
|
// This is only usable before walk.walkConvertInterface, which converts to an
|
|
// OMAKEFACE.
|
|
func IsIfaceOfFunc(n Node) *Func {
|
|
if n, ok := n.(*ConvExpr); ok && n.Op() == OCONVIFACE {
|
|
if name, ok := n.X.(*Name); ok && name.Op() == ONAME && name.Class == PFUNC {
|
|
return name.Func
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FuncPC returns a uintptr-typed expression that evaluates to the PC of a
|
|
// function as uintptr, as returned by internal/abi.FuncPC{ABI0,ABIInternal}.
|
|
//
|
|
// n should be a Node of an interface type, as is passed to
|
|
// internal/abi.FuncPC{ABI0,ABIInternal}.
|
|
//
|
|
// TODO(prattmic): Since n is simply an interface{} there is no assertion that
|
|
// it is actually a function at all. Perhaps we should emit a runtime type
|
|
// assertion?
|
|
func FuncPC(pos src.XPos, n Node, wantABI obj.ABI) Node {
|
|
if !n.Type().IsInterface() {
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an interface value, got %v", wantABI, n.Type())
|
|
}
|
|
|
|
if fn := IsIfaceOfFunc(n); fn != nil {
|
|
name := fn.Nname
|
|
abi := fn.ABI
|
|
if abi != wantABI {
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an %v function, %s is defined as %v", wantABI, wantABI, name.Sym().Name, abi)
|
|
}
|
|
var e Node = NewLinksymExpr(pos, name.LinksymABI(abi), types.Types[types.TUINTPTR])
|
|
e = NewAddrExpr(pos, e)
|
|
e.SetType(types.Types[types.TUINTPTR].PtrTo())
|
|
e = NewConvExpr(pos, OCONVNOP, types.Types[types.TUINTPTR], e)
|
|
e.SetTypecheck(1)
|
|
return e
|
|
}
|
|
// fn is not a defined function. It must be ABIInternal.
|
|
// Read the address from func value, i.e. *(*uintptr)(idata(fn)).
|
|
if wantABI != obj.ABIInternal {
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s does not accept func expression, which is ABIInternal", wantABI)
|
|
}
|
|
var e Node = NewUnaryExpr(pos, OIDATA, n)
|
|
e.SetType(types.Types[types.TUINTPTR].PtrTo())
|
|
e.SetTypecheck(1)
|
|
e = NewStarExpr(pos, e)
|
|
e.SetType(types.Types[types.TUINTPTR])
|
|
e.SetTypecheck(1)
|
|
return e
|
|
}
|
|
|
|
// DeclareParams creates Names for all of the parameters in fn's
|
|
// signature and adds them to fn.Dcl.
|
|
//
|
|
// If setNname is true, then it also sets types.Field.Nname for each
|
|
// parameter.
|
|
func (fn *Func) DeclareParams(setNname bool) {
|
|
if fn.Dcl != nil {
|
|
base.FatalfAt(fn.Pos(), "%v already has Dcl", fn)
|
|
}
|
|
|
|
declareParams := func(params []*types.Field, ctxt Class, prefix string, offset int) {
|
|
for i, param := range params {
|
|
sym := param.Sym
|
|
if sym == nil || sym.IsBlank() {
|
|
sym = fn.Sym().Pkg.LookupNum(prefix, i)
|
|
}
|
|
|
|
name := NewNameAt(param.Pos, sym, param.Type)
|
|
name.Class = ctxt
|
|
name.Curfn = fn
|
|
fn.Dcl[offset+i] = name
|
|
|
|
if setNname {
|
|
param.Nname = name
|
|
}
|
|
}
|
|
}
|
|
|
|
sig := fn.Type()
|
|
params := sig.RecvParams()
|
|
results := sig.Results()
|
|
|
|
fn.Dcl = make([]*Name, len(params)+len(results))
|
|
declareParams(params, PPARAM, "~p", 0)
|
|
declareParams(results, PPARAMOUT, "~r", len(params))
|
|
}
|