mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: remove GOEXPERIMENT=nounified frontend
This CL removes most of the code specific to the nounified frontend. To simplify review, it's a strict remove-only CL. Updates #57410. Change-Id: Ic3196570aa4286618c1d5e7fd0d0e6601a18195b Reviewed-on: https://go-review.googlesource.com/c/go/+/458620 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
a7de684e1b
commit
da9761303c
23 changed files with 0 additions and 11575 deletions
|
|
@ -31,7 +31,6 @@ import (
|
|||
"go/constant"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
|
|
@ -40,7 +39,6 @@ import (
|
|||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// Inlining budget parameters, gathered in one place
|
||||
|
|
@ -879,23 +877,6 @@ func inlCallee(fn ir.Node, profile *pgo.Profile) *ir.Func {
|
|||
return nil
|
||||
}
|
||||
|
||||
func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
|
||||
if t.Nname == nil {
|
||||
return ir.BlankNode
|
||||
}
|
||||
n := t.Nname.(*ir.Name)
|
||||
if ir.IsBlank(n) {
|
||||
return ir.BlankNode
|
||||
}
|
||||
inlvar := inlvars[n]
|
||||
if inlvar == nil {
|
||||
base.Fatalf("missing inlvar for %v", n)
|
||||
}
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
|
||||
inlvar.Name().Defn = as
|
||||
return inlvar
|
||||
}
|
||||
|
||||
var inlgen int
|
||||
|
||||
// SSADumpInline gives the SSA back end a chance to dump the function
|
||||
|
|
@ -1094,576 +1075,6 @@ func CalleeEffects(init *ir.Nodes, callee ir.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// oldInlineCall creates an InlinedCallExpr to replace the given call
|
||||
// expression. fn is the callee function to be inlined. inlIndex is
|
||||
// the inlining tree position index, for use with src.NewInliningBase
|
||||
// when rewriting positions.
|
||||
func oldInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
|
||||
SSADumpInline(fn)
|
||||
|
||||
ninit := call.Init()
|
||||
|
||||
// For normal function calls, the function callee expression
|
||||
// may contain side effects. Make sure to preserve these,
|
||||
// if necessary (#42703).
|
||||
if call.Op() == ir.OCALLFUNC {
|
||||
CalleeEffects(&ninit, call.X)
|
||||
}
|
||||
|
||||
// Make temp names to use instead of the originals.
|
||||
inlvars := make(map[*ir.Name]*ir.Name)
|
||||
|
||||
// record formals/locals for later post-processing
|
||||
var inlfvars []*ir.Name
|
||||
|
||||
for _, ln := range fn.Inl.Dcl {
|
||||
if ln.Op() != ir.ONAME {
|
||||
continue
|
||||
}
|
||||
if ln.Class == ir.PPARAMOUT { // return values handled below.
|
||||
continue
|
||||
}
|
||||
inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
|
||||
inlvars[ln] = inlf
|
||||
if base.Flag.GenDwarfInl > 0 {
|
||||
if ln.Class == ir.PPARAM {
|
||||
inlf.Name().SetInlFormal(true)
|
||||
} else {
|
||||
inlf.Name().SetInlLocal(true)
|
||||
}
|
||||
inlf.SetPos(ln.Pos())
|
||||
inlfvars = append(inlfvars, inlf)
|
||||
}
|
||||
}
|
||||
|
||||
// We can delay declaring+initializing result parameters if:
|
||||
// temporaries for return values.
|
||||
var retvars []ir.Node
|
||||
for i, t := range fn.Type().Results().Fields().Slice() {
|
||||
var m *ir.Name
|
||||
if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
|
||||
n := nn.(*ir.Name)
|
||||
m = inlvar(n)
|
||||
m = typecheck.Expr(m).(*ir.Name)
|
||||
inlvars[n] = m
|
||||
} else {
|
||||
// anonymous return values, synthesize names for use in assignment that replaces return
|
||||
m = retvar(t, i)
|
||||
}
|
||||
|
||||
if base.Flag.GenDwarfInl > 0 {
|
||||
// Don't update the src.Pos on a return variable if it
|
||||
// was manufactured by the inliner (e.g. "~R2"); such vars
|
||||
// were not part of the original callee.
|
||||
if !strings.HasPrefix(m.Sym().Name, "~R") {
|
||||
m.Name().SetInlFormal(true)
|
||||
m.SetPos(t.Pos)
|
||||
inlfvars = append(inlfvars, m)
|
||||
}
|
||||
}
|
||||
|
||||
retvars = append(retvars, m)
|
||||
}
|
||||
|
||||
// Assign arguments to the parameters' temp names.
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
|
||||
as.Def = true
|
||||
if call.Op() == ir.OCALLMETH {
|
||||
base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
|
||||
}
|
||||
as.Rhs.Append(call.Args...)
|
||||
|
||||
if recv := fn.Type().Recv(); recv != nil {
|
||||
as.Lhs.Append(inlParam(recv, as, inlvars))
|
||||
}
|
||||
for _, param := range fn.Type().Params().Fields().Slice() {
|
||||
as.Lhs.Append(inlParam(param, as, inlvars))
|
||||
}
|
||||
|
||||
if len(as.Rhs) != 0 {
|
||||
ninit.Append(typecheck.Stmt(as))
|
||||
}
|
||||
|
||||
if !fn.Inl.CanDelayResults {
|
||||
// Zero the return parameters.
|
||||
for _, n := range retvars {
|
||||
ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
|
||||
ras := ir.NewAssignStmt(base.Pos, n, nil)
|
||||
ninit.Append(typecheck.Stmt(ras))
|
||||
}
|
||||
}
|
||||
|
||||
retlabel := typecheck.AutoLabel(".i")
|
||||
|
||||
inlgen++
|
||||
|
||||
// Add an inline mark just before the inlined body.
|
||||
// This mark is inline in the code so that it's a reasonable spot
|
||||
// to put a breakpoint. Not sure if that's really necessary or not
|
||||
// (in which case it could go at the end of the function instead).
|
||||
// Note issue 28603.
|
||||
ninit.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(inlIndex)))
|
||||
|
||||
subst := inlsubst{
|
||||
retlabel: retlabel,
|
||||
retvars: retvars,
|
||||
inlvars: inlvars,
|
||||
defnMarker: ir.NilExpr{},
|
||||
bases: make(map[*src.PosBase]*src.PosBase),
|
||||
newInlIndex: inlIndex,
|
||||
fn: fn,
|
||||
}
|
||||
subst.edit = subst.node
|
||||
|
||||
body := subst.list(ir.Nodes(fn.Inl.Body))
|
||||
|
||||
lab := ir.NewLabelStmt(base.Pos, retlabel)
|
||||
body = append(body, lab)
|
||||
|
||||
if base.Flag.GenDwarfInl > 0 {
|
||||
for _, v := range inlfvars {
|
||||
v.SetPos(subst.updatedPos(v.Pos()))
|
||||
}
|
||||
}
|
||||
|
||||
//dumplist("ninit post", ninit);
|
||||
|
||||
res := ir.NewInlinedCallExpr(base.Pos, body, retvars)
|
||||
res.SetInit(ninit)
|
||||
res.SetType(call.Type())
|
||||
res.SetTypecheck(1)
|
||||
return res
|
||||
}
|
||||
|
||||
// Every time we expand a function we generate a new set of tmpnames,
|
||||
// PAUTO's in the calling functions, and link them off of the
|
||||
// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
|
||||
func inlvar(var_ *ir.Name) *ir.Name {
|
||||
if base.Flag.LowerM > 3 {
|
||||
fmt.Printf("inlvar %+v\n", var_)
|
||||
}
|
||||
|
||||
n := typecheck.NewName(var_.Sym())
|
||||
n.SetType(var_.Type())
|
||||
n.SetTypecheck(1)
|
||||
n.Class = ir.PAUTO
|
||||
n.SetUsed(true)
|
||||
n.SetAutoTemp(var_.AutoTemp())
|
||||
n.Curfn = ir.CurFunc // the calling function, not the called one
|
||||
n.SetAddrtaken(var_.Addrtaken())
|
||||
|
||||
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
|
||||
return n
|
||||
}
|
||||
|
||||
// Synthesize a variable to store the inlined function's results in.
|
||||
func retvar(t *types.Field, i int) *ir.Name {
|
||||
n := typecheck.NewName(typecheck.LookupNum("~R", i))
|
||||
n.SetType(t.Type)
|
||||
n.SetTypecheck(1)
|
||||
n.Class = ir.PAUTO
|
||||
n.SetUsed(true)
|
||||
n.Curfn = ir.CurFunc // the calling function, not the called one
|
||||
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
|
||||
return n
|
||||
}
|
||||
|
||||
// The inlsubst type implements the actual inlining of a single
|
||||
// function call.
|
||||
type inlsubst struct {
|
||||
// Target of the goto substituted in place of a return.
|
||||
retlabel *types.Sym
|
||||
|
||||
// Temporary result variables.
|
||||
retvars []ir.Node
|
||||
|
||||
inlvars map[*ir.Name]*ir.Name
|
||||
// defnMarker is used to mark a Node for reassignment.
|
||||
// inlsubst.clovar set this during creating new ONAME.
|
||||
// inlsubst.node will set the correct Defn for inlvar.
|
||||
defnMarker ir.NilExpr
|
||||
|
||||
// bases maps from original PosBase to PosBase with an extra
|
||||
// inlined call frame.
|
||||
bases map[*src.PosBase]*src.PosBase
|
||||
|
||||
// newInlIndex is the index of the inlined call frame to
|
||||
// insert for inlined nodes.
|
||||
newInlIndex int
|
||||
|
||||
edit func(ir.Node) ir.Node // cached copy of subst.node method value closure
|
||||
|
||||
// If non-nil, we are inside a closure inside the inlined function, and
|
||||
// newclofn is the Func of the new inlined closure.
|
||||
newclofn *ir.Func
|
||||
|
||||
fn *ir.Func // For debug -- the func that is being inlined
|
||||
|
||||
// If true, then don't update source positions during substitution
|
||||
// (retain old source positions).
|
||||
noPosUpdate bool
|
||||
}
|
||||
|
||||
// list inlines a list of nodes.
|
||||
func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
|
||||
s := make([]ir.Node, 0, len(ll))
|
||||
for _, n := range ll {
|
||||
s = append(s, subst.node(n))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// fields returns a list of the fields of a struct type representing receiver,
|
||||
// params, or results, after duplicating the field nodes and substituting the
|
||||
// Nname nodes inside the field nodes.
|
||||
func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
|
||||
oldfields := oldt.FieldSlice()
|
||||
newfields := make([]*types.Field, len(oldfields))
|
||||
for i := range oldfields {
|
||||
newfields[i] = oldfields[i].Copy()
|
||||
if oldfields[i].Nname != nil {
|
||||
newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name))
|
||||
}
|
||||
}
|
||||
return newfields
|
||||
}
|
||||
|
||||
// clovar creates a new ONAME node for a local variable or param of a closure
|
||||
// inside a function being inlined.
|
||||
func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
|
||||
m := ir.NewNameAt(n.Pos(), n.Sym())
|
||||
m.Class = n.Class
|
||||
m.SetType(n.Type())
|
||||
m.SetTypecheck(1)
|
||||
if n.IsClosureVar() {
|
||||
m.SetIsClosureVar(true)
|
||||
}
|
||||
if n.Addrtaken() {
|
||||
m.SetAddrtaken(true)
|
||||
}
|
||||
if n.Used() {
|
||||
m.SetUsed(true)
|
||||
}
|
||||
m.Defn = n.Defn
|
||||
|
||||
m.Curfn = subst.newclofn
|
||||
|
||||
switch defn := n.Defn.(type) {
|
||||
case nil:
|
||||
// ok
|
||||
case *ir.Name:
|
||||
if !n.IsClosureVar() {
|
||||
base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n)
|
||||
}
|
||||
if n.Sym().Pkg != types.LocalPkg {
|
||||
// If the closure came from inlining a function from
|
||||
// another package, must change package of captured
|
||||
// variable to localpkg, so that the fields of the closure
|
||||
// struct are local package and can be accessed even if
|
||||
// name is not exported. If you disable this code, you can
|
||||
// reproduce the problem by running 'go test
|
||||
// go/internal/srcimporter'. TODO(mdempsky) - maybe change
|
||||
// how we create closure structs?
|
||||
m.SetSym(types.LocalPkg.Lookup(n.Sym().Name))
|
||||
}
|
||||
// Make sure any inlvar which is the Defn
|
||||
// of an ONAME closure var is rewritten
|
||||
// during inlining. Don't substitute
|
||||
// if Defn node is outside inlined function.
|
||||
if subst.inlvars[n.Defn.(*ir.Name)] != nil {
|
||||
m.Defn = subst.node(n.Defn)
|
||||
}
|
||||
case *ir.AssignStmt, *ir.AssignListStmt:
|
||||
// Mark node for reassignment at the end of inlsubst.node.
|
||||
m.Defn = &subst.defnMarker
|
||||
case *ir.TypeSwitchGuard:
|
||||
// TODO(mdempsky): Set m.Defn properly. See discussion on #45743.
|
||||
case *ir.RangeStmt:
|
||||
// TODO: Set m.Defn properly if we support inlining range statement in the future.
|
||||
default:
|
||||
base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn)
|
||||
}
|
||||
|
||||
if n.Outer != nil {
|
||||
// Either the outer variable is defined in function being inlined,
|
||||
// and we will replace it with the substituted variable, or it is
|
||||
// defined outside the function being inlined, and we should just
|
||||
// skip the outer variable (the closure variable of the function
|
||||
// being inlined).
|
||||
s := subst.node(n.Outer).(*ir.Name)
|
||||
if s == n.Outer {
|
||||
s = n.Outer.Outer
|
||||
}
|
||||
m.Outer = s
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// closure does the necessary substitutions for a ClosureExpr n and returns the new
|
||||
// closure node.
|
||||
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
||||
// Prior to the subst edit, set a flag in the inlsubst to indicate
|
||||
// that we don't want to update the source positions in the new
|
||||
// closure function. If we do this, it will appear that the
|
||||
// closure itself has things inlined into it, which is not the
|
||||
// case. See issue #46234 for more details. At the same time, we
|
||||
// do want to update the position in the new ClosureExpr (which is
|
||||
// part of the function we're working on). See #49171 for an
|
||||
// example of what happens if we miss that update.
|
||||
newClosurePos := subst.updatedPos(n.Pos())
|
||||
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
|
||||
subst.noPosUpdate = true
|
||||
|
||||
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
|
||||
|
||||
oldfn := n.Func
|
||||
newfn := ir.NewClosureFunc(oldfn.Pos(), true)
|
||||
|
||||
if subst.newclofn != nil {
|
||||
//fmt.Printf("Inlining a closure with a nested closure\n")
|
||||
}
|
||||
prevxfunc := subst.newclofn
|
||||
|
||||
// Mark that we are now substituting within a closure (within the
|
||||
// inlined function), and create new nodes for all the local
|
||||
// vars/params inside this closure.
|
||||
subst.newclofn = newfn
|
||||
newfn.Dcl = nil
|
||||
newfn.ClosureVars = nil
|
||||
for _, oldv := range oldfn.Dcl {
|
||||
newv := subst.clovar(oldv)
|
||||
subst.inlvars[oldv] = newv
|
||||
newfn.Dcl = append(newfn.Dcl, newv)
|
||||
}
|
||||
for _, oldv := range oldfn.ClosureVars {
|
||||
newv := subst.clovar(oldv)
|
||||
subst.inlvars[oldv] = newv
|
||||
newfn.ClosureVars = append(newfn.ClosureVars, newv)
|
||||
}
|
||||
|
||||
// Need to replace ONAME nodes in
|
||||
// newfn.Type().FuncType().Receiver/Params/Results.FieldSlice().Nname
|
||||
oldt := oldfn.Type()
|
||||
newrecvs := subst.fields(oldt.Recvs())
|
||||
var newrecv *types.Field
|
||||
if len(newrecvs) > 0 {
|
||||
newrecv = newrecvs[0]
|
||||
}
|
||||
newt := types.NewSignature(oldt.Pkg(), newrecv,
|
||||
nil, subst.fields(oldt.Params()), subst.fields(oldt.Results()))
|
||||
|
||||
newfn.Nname.SetType(newt)
|
||||
newfn.Body = subst.list(oldfn.Body)
|
||||
|
||||
// Remove the nodes for the current closure from subst.inlvars
|
||||
for _, oldv := range oldfn.Dcl {
|
||||
delete(subst.inlvars, oldv)
|
||||
}
|
||||
for _, oldv := range oldfn.ClosureVars {
|
||||
delete(subst.inlvars, oldv)
|
||||
}
|
||||
// Go back to previous closure func
|
||||
subst.newclofn = prevxfunc
|
||||
|
||||
// Actually create the named function for the closure, now that
|
||||
// the closure is inlined in a specific function.
|
||||
newclo := newfn.OClosure
|
||||
newclo.SetPos(newClosurePos)
|
||||
newclo.SetInit(subst.list(n.Init()))
|
||||
return typecheck.Expr(newclo)
|
||||
}
|
||||
|
||||
// node recursively copies a node from the saved pristine body of the
|
||||
// inlined function, substituting references to input/output
|
||||
// parameters with ones to the tmpnames, and substituting returns with
|
||||
// assignments to the output.
|
||||
func (subst *inlsubst) node(n ir.Node) ir.Node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch n.Op() {
|
||||
case ir.ONAME:
|
||||
n := n.(*ir.Name)
|
||||
|
||||
// Handle captured variables when inlining closures.
|
||||
if n.IsClosureVar() && subst.newclofn == nil {
|
||||
o := n.Outer
|
||||
|
||||
// Deal with case where sequence of closures are inlined.
|
||||
// TODO(danscales) - write test case to see if we need to
|
||||
// go up multiple levels.
|
||||
if o.Curfn != ir.CurFunc {
|
||||
o = o.Outer
|
||||
}
|
||||
|
||||
// make sure the outer param matches the inlining location
|
||||
if o == nil || o.Curfn != ir.CurFunc {
|
||||
base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
|
||||
}
|
||||
|
||||
if base.Flag.LowerM > 2 {
|
||||
fmt.Printf("substituting captured name %+v -> %+v\n", n, o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
|
||||
if base.Flag.LowerM > 2 {
|
||||
fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
|
||||
}
|
||||
return inlvar
|
||||
}
|
||||
|
||||
if base.Flag.LowerM > 2 {
|
||||
fmt.Printf("not substituting name %+v\n", n)
|
||||
}
|
||||
return n
|
||||
|
||||
case ir.OMETHEXPR:
|
||||
n := n.(*ir.SelectorExpr)
|
||||
return n
|
||||
|
||||
case ir.OLITERAL, ir.ONIL, ir.OTYPE:
|
||||
// If n is a named constant or type, we can continue
|
||||
// using it in the inline copy. Otherwise, make a copy
|
||||
// so we can update the line number.
|
||||
if n.Sym() != nil {
|
||||
return n
|
||||
}
|
||||
|
||||
case ir.ORETURN:
|
||||
if subst.newclofn != nil {
|
||||
// Don't do special substitutions if inside a closure
|
||||
break
|
||||
}
|
||||
// Because of the above test for subst.newclofn,
|
||||
// this return is guaranteed to belong to the current inlined function.
|
||||
n := n.(*ir.ReturnStmt)
|
||||
init := subst.list(n.Init())
|
||||
if len(subst.retvars) != 0 && len(n.Results) != 0 {
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
|
||||
|
||||
// Make a shallow copy of retvars.
|
||||
// Otherwise OINLCALL.Rlist will be the same list,
|
||||
// and later walk and typecheck may clobber it.
|
||||
for _, n := range subst.retvars {
|
||||
as.Lhs.Append(n)
|
||||
}
|
||||
as.Rhs = subst.list(n.Results)
|
||||
|
||||
if subst.fn.Inl.CanDelayResults {
|
||||
for _, n := range as.Lhs {
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
|
||||
n.Name().Defn = as
|
||||
}
|
||||
}
|
||||
|
||||
init = append(init, typecheck.Stmt(as))
|
||||
}
|
||||
init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
|
||||
typecheck.Stmts(init)
|
||||
return ir.NewBlockStmt(base.Pos, init)
|
||||
|
||||
case ir.OGOTO, ir.OBREAK, ir.OCONTINUE:
|
||||
if subst.newclofn != nil {
|
||||
// Don't do special substitutions if inside a closure
|
||||
break
|
||||
}
|
||||
n := n.(*ir.BranchStmt)
|
||||
m := ir.Copy(n).(*ir.BranchStmt)
|
||||
m.SetPos(subst.updatedPos(m.Pos()))
|
||||
m.SetInit(nil)
|
||||
m.Label = translateLabel(n.Label)
|
||||
return m
|
||||
|
||||
case ir.OLABEL:
|
||||
if subst.newclofn != nil {
|
||||
// Don't do special substitutions if inside a closure
|
||||
break
|
||||
}
|
||||
n := n.(*ir.LabelStmt)
|
||||
m := ir.Copy(n).(*ir.LabelStmt)
|
||||
m.SetPos(subst.updatedPos(m.Pos()))
|
||||
m.SetInit(nil)
|
||||
m.Label = translateLabel(n.Label)
|
||||
return m
|
||||
|
||||
case ir.OCLOSURE:
|
||||
return subst.closure(n.(*ir.ClosureExpr))
|
||||
|
||||
}
|
||||
|
||||
m := ir.Copy(n)
|
||||
m.SetPos(subst.updatedPos(m.Pos()))
|
||||
ir.EditChildren(m, subst.edit)
|
||||
|
||||
if subst.newclofn == nil {
|
||||
// Translate any label on FOR, RANGE loops, SWITCH or SELECT
|
||||
switch m.Op() {
|
||||
case ir.OFOR:
|
||||
m := m.(*ir.ForStmt)
|
||||
m.Label = translateLabel(m.Label)
|
||||
return m
|
||||
|
||||
case ir.ORANGE:
|
||||
m := m.(*ir.RangeStmt)
|
||||
m.Label = translateLabel(m.Label)
|
||||
return m
|
||||
|
||||
case ir.OSWITCH:
|
||||
m := m.(*ir.SwitchStmt)
|
||||
m.Label = translateLabel(m.Label)
|
||||
return m
|
||||
|
||||
case ir.OSELECT:
|
||||
m := m.(*ir.SelectStmt)
|
||||
m.Label = translateLabel(m.Label)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
switch m := m.(type) {
|
||||
case *ir.AssignStmt:
|
||||
if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
|
||||
lhs.Defn = m
|
||||
}
|
||||
case *ir.AssignListStmt:
|
||||
for _, lhs := range m.Lhs {
|
||||
if lhs, ok := lhs.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
|
||||
lhs.Defn = m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// translateLabel makes a label from an inlined function (if non-nil) be unique by
|
||||
// adding "·inlgen".
|
||||
func translateLabel(l *types.Sym) *types.Sym {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
p := fmt.Sprintf("%s·%d", l.Name, inlgen)
|
||||
return typecheck.Lookup(p)
|
||||
}
|
||||
|
||||
func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
|
||||
if subst.noPosUpdate {
|
||||
return xpos
|
||||
}
|
||||
pos := base.Ctxt.PosTable.Pos(xpos)
|
||||
oldbase := pos.Base() // can be nil
|
||||
newbase := subst.bases[oldbase]
|
||||
if newbase == nil {
|
||||
newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
|
||||
subst.bases[oldbase] = newbase
|
||||
}
|
||||
pos.SetBase(newbase)
|
||||
return base.Ctxt.PosTable.XPos(pos)
|
||||
}
|
||||
|
||||
func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
|
||||
s := make([]*ir.Name, 0, len(ll))
|
||||
for _, n := range ll {
|
||||
|
|
|
|||
|
|
@ -5,53 +5,10 @@
|
|||
package noder
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
)
|
||||
|
||||
// TODO(mdempsky): Skip blank declarations? Probably only safe
|
||||
// for declarations without pragmas.
|
||||
|
||||
func (g *irgen) decls(res *ir.Nodes, decls []syntax.Decl) {
|
||||
for _, decl := range decls {
|
||||
switch decl := decl.(type) {
|
||||
case *syntax.ConstDecl:
|
||||
g.constDecl(res, decl)
|
||||
case *syntax.FuncDecl:
|
||||
g.funcDecl(res, decl)
|
||||
case *syntax.TypeDecl:
|
||||
if ir.CurFunc == nil {
|
||||
continue // already handled in irgen.generate
|
||||
}
|
||||
g.typeDecl(res, decl)
|
||||
case *syntax.VarDecl:
|
||||
g.varDecl(res, decl)
|
||||
default:
|
||||
g.unhandled("declaration", decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
|
||||
g.pragmaFlags(decl.Pragma, 0)
|
||||
|
||||
// Get the imported package's path, as resolved already by types2
|
||||
// and gcimporter. This is the same path as would be computed by
|
||||
// parseImportPath.
|
||||
switch pkgNameOf(g.info, decl).Imported().Path() {
|
||||
case "unsafe":
|
||||
p.importedUnsafe = true
|
||||
case "embed":
|
||||
p.importedEmbed = true
|
||||
}
|
||||
}
|
||||
|
||||
// pkgNameOf returns the PkgName associated with the given ImportDecl.
|
||||
func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName {
|
||||
if name := decl.LocalPkgName; name != nil {
|
||||
|
|
@ -59,294 +16,3 @@ func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName {
|
|||
}
|
||||
return info.Implicits[decl].(*types2.PkgName)
|
||||
}
|
||||
|
||||
func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
|
||||
g.pragmaFlags(decl.Pragma, 0)
|
||||
|
||||
for _, name := range decl.NameList {
|
||||
name, obj := g.def(name)
|
||||
|
||||
// For untyped numeric constants, make sure the value
|
||||
// representation matches what the rest of the
|
||||
// compiler (really just iexport) expects.
|
||||
// TODO(mdempsky): Revisit after #43891 is resolved.
|
||||
val := obj.(*types2.Const).Val()
|
||||
switch name.Type() {
|
||||
case types.UntypedInt, types.UntypedRune:
|
||||
val = constant.ToInt(val)
|
||||
case types.UntypedFloat:
|
||||
val = constant.ToFloat(val)
|
||||
case types.UntypedComplex:
|
||||
val = constant.ToComplex(val)
|
||||
}
|
||||
name.SetVal(val)
|
||||
|
||||
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name))
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
|
||||
assert(g.curDecl == "")
|
||||
// Set g.curDecl to the function name, as context for the type params declared
|
||||
// during types2-to-types1 translation if this is a generic function.
|
||||
g.curDecl = decl.Name.Value
|
||||
obj2 := g.info.Defs[decl.Name]
|
||||
recv := types2.AsSignature(obj2.Type()).Recv()
|
||||
if recv != nil {
|
||||
t2 := deref2(recv.Type())
|
||||
// This is a method, so set g.curDecl to recvTypeName.methName instead.
|
||||
g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl
|
||||
}
|
||||
|
||||
fn := ir.NewFunc(g.pos(decl))
|
||||
fn.Nname, _ = g.def(decl.Name)
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Defn = fn
|
||||
|
||||
fn.Pragma = g.pragmaFlags(decl.Pragma, funcPragmas)
|
||||
if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 {
|
||||
base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined")
|
||||
}
|
||||
if fn.Pragma&ir.Nointerface != 0 {
|
||||
// Propagate //go:nointerface from Func.Pragma to Field.Nointerface.
|
||||
// This is a bit roundabout, but this is the earliest point where we've
|
||||
// processed the function's pragma flags, and we've also already created
|
||||
// the Fields to represent the receiver's method set.
|
||||
if recv := fn.Type().Recv(); recv != nil {
|
||||
typ := types.ReceiverBaseType(recv.Type)
|
||||
if orig := typ.OrigType(); orig != nil {
|
||||
// For a generic method, we mark the methods on the
|
||||
// base generic type, since those are the methods
|
||||
// that will be stenciled.
|
||||
typ = orig
|
||||
}
|
||||
meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0)
|
||||
meth.SetNointerface(true)
|
||||
}
|
||||
}
|
||||
|
||||
if decl.Body != nil {
|
||||
if fn.Pragma&ir.Noescape != 0 {
|
||||
base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations")
|
||||
}
|
||||
if (fn.Pragma&ir.UintptrKeepAlive != 0 && fn.Pragma&ir.UintptrEscapes == 0) && fn.Pragma&ir.Nosplit == 0 {
|
||||
// Stack growth can't handle uintptr arguments that may
|
||||
// be pointers (as we don't know which are pointers
|
||||
// when creating the stack map). Thus uintptrkeepalive
|
||||
// functions (and all transitive callees) must be
|
||||
// nosplit.
|
||||
//
|
||||
// N.B. uintptrescapes implies uintptrkeepalive but it
|
||||
// is OK since the arguments must escape to the heap.
|
||||
//
|
||||
// TODO(prattmic): Add recursive nosplit check of callees.
|
||||
// TODO(prattmic): Functions with no body (i.e.,
|
||||
// assembly) must also be nosplit, but we can't check
|
||||
// that here.
|
||||
base.ErrorfAt(fn.Pos(), "go:uintptrkeepalive requires go:nosplit")
|
||||
}
|
||||
}
|
||||
|
||||
if decl.Name.Value == "init" && decl.Recv == nil {
|
||||
g.target.Inits = append(g.target.Inits, fn)
|
||||
}
|
||||
|
||||
saveHaveEmbed := g.haveEmbed
|
||||
saveCurDecl := g.curDecl
|
||||
g.curDecl = ""
|
||||
g.later(func() {
|
||||
defer func(b bool, s string) {
|
||||
// Revert haveEmbed and curDecl back to what they were before
|
||||
// the "later" function.
|
||||
g.haveEmbed = b
|
||||
g.curDecl = s
|
||||
}(g.haveEmbed, g.curDecl)
|
||||
|
||||
// Set haveEmbed and curDecl to what they were for this funcDecl.
|
||||
g.haveEmbed = saveHaveEmbed
|
||||
g.curDecl = saveCurDecl
|
||||
if fn.Type().HasTParam() {
|
||||
g.topFuncIsGeneric = true
|
||||
}
|
||||
g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
|
||||
g.topFuncIsGeneric = false
|
||||
if fn.Type().HasTParam() && fn.Body != nil {
|
||||
// Set pointers to the dcls/body of a generic function/method in
|
||||
// the Inl struct, so it is marked for export, is available for
|
||||
// stenciling, and works with Inline_Flood().
|
||||
fn.Inl = &ir.Inline{
|
||||
Cost: 1,
|
||||
Dcl: fn.Dcl,
|
||||
Body: fn.Body,
|
||||
}
|
||||
}
|
||||
|
||||
out.Append(fn)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
|
||||
// Set the position for any error messages we might print (e.g. too large types).
|
||||
base.Pos = g.pos(decl)
|
||||
assert(ir.CurFunc != nil || g.curDecl == "")
|
||||
// Set g.curDecl to the type name, as context for the type params declared
|
||||
// during types2-to-types1 translation if this is a generic type.
|
||||
saveCurDecl := g.curDecl
|
||||
g.curDecl = decl.Name.Value
|
||||
if decl.Alias {
|
||||
name, _ := g.def(decl.Name)
|
||||
g.pragmaFlags(decl.Pragma, 0)
|
||||
assert(name.Alias()) // should be set by irgen.obj
|
||||
|
||||
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
||||
g.curDecl = ""
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent size calculations until we set the underlying type.
|
||||
types.DeferCheckSize()
|
||||
|
||||
name, obj := g.def(decl.Name)
|
||||
ntyp, otyp := name.Type(), obj.Type()
|
||||
if ir.CurFunc != nil {
|
||||
ntyp.SetVargen()
|
||||
}
|
||||
|
||||
pragmas := g.pragmaFlags(decl.Pragma, 0)
|
||||
name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed?
|
||||
|
||||
ntyp.SetUnderlying(g.typeExpr(decl.Type))
|
||||
|
||||
tparams := otyp.(*types2.Named).TypeParams()
|
||||
if n := tparams.Len(); n > 0 {
|
||||
rparams := make([]*types.Type, n)
|
||||
for i := range rparams {
|
||||
rparams[i] = g.typ(tparams.At(i))
|
||||
}
|
||||
// This will set hasTParam flag if any rparams are not concrete types.
|
||||
ntyp.SetRParams(rparams)
|
||||
}
|
||||
types.ResumeCheckSize()
|
||||
|
||||
g.curDecl = saveCurDecl
|
||||
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
|
||||
methods := make([]*types.Field, otyp.NumMethods())
|
||||
for i := range methods {
|
||||
m := otyp.Method(i)
|
||||
// Set g.curDecl to recvTypeName.methName, as context for the
|
||||
// method-specific type params in the receiver.
|
||||
g.curDecl = decl.Name.Value + "." + m.Name()
|
||||
meth := g.obj(m)
|
||||
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
|
||||
methods[i].Nname = meth
|
||||
g.curDecl = ""
|
||||
}
|
||||
ntyp.Methods().Set(methods)
|
||||
}
|
||||
|
||||
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
||||
}
|
||||
|
||||
func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
|
||||
pos := g.pos(decl)
|
||||
// Set the position for any error messages we might print (e.g. too large types).
|
||||
base.Pos = pos
|
||||
names := make([]*ir.Name, len(decl.NameList))
|
||||
for i, name := range decl.NameList {
|
||||
names[i], _ = g.def(name)
|
||||
}
|
||||
|
||||
if decl.Pragma != nil {
|
||||
pragma := decl.Pragma.(*pragmas)
|
||||
varEmbed(g.makeXPos, names[0], decl, pragma, g.haveEmbed)
|
||||
g.reportUnused(pragma)
|
||||
}
|
||||
|
||||
haveEmbed := g.haveEmbed
|
||||
do := func() {
|
||||
defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
|
||||
|
||||
g.haveEmbed = haveEmbed
|
||||
values := g.exprList(decl.Values)
|
||||
|
||||
var as2 *ir.AssignListStmt
|
||||
if len(values) != 0 && len(names) != len(values) {
|
||||
as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values)
|
||||
}
|
||||
|
||||
for i, name := range names {
|
||||
if ir.CurFunc != nil {
|
||||
out.Append(ir.NewDecl(pos, ir.ODCL, name))
|
||||
}
|
||||
if as2 != nil {
|
||||
as2.Lhs[i] = name
|
||||
name.Defn = as2
|
||||
} else {
|
||||
as := ir.NewAssignStmt(pos, name, nil)
|
||||
if len(values) != 0 {
|
||||
as.Y = values[i]
|
||||
name.Defn = as
|
||||
} else if ir.CurFunc == nil {
|
||||
name.Defn = as
|
||||
}
|
||||
if !g.delayTransform() {
|
||||
lhs := []ir.Node{as.X}
|
||||
rhs := []ir.Node{}
|
||||
if as.Y != nil {
|
||||
rhs = []ir.Node{as.Y}
|
||||
}
|
||||
transformAssign(as, lhs, rhs)
|
||||
as.X = lhs[0]
|
||||
if as.Y != nil {
|
||||
as.Y = rhs[0]
|
||||
}
|
||||
}
|
||||
as.SetTypecheck(1)
|
||||
out.Append(as)
|
||||
}
|
||||
}
|
||||
if as2 != nil {
|
||||
if !g.delayTransform() {
|
||||
transformAssign(as2, as2.Lhs, as2.Rhs)
|
||||
}
|
||||
as2.SetTypecheck(1)
|
||||
out.Append(as2)
|
||||
}
|
||||
}
|
||||
|
||||
// If we're within a function, we need to process the assignment
|
||||
// part of the variable declaration right away. Otherwise, we leave
|
||||
// it to be handled after all top-level declarations are processed.
|
||||
if ir.CurFunc != nil {
|
||||
do()
|
||||
} else {
|
||||
g.later(do)
|
||||
}
|
||||
}
|
||||
|
||||
// pragmaFlags returns any specified pragma flags included in allowed,
|
||||
// and reports errors about any other, unexpected pragmas.
|
||||
func (g *irgen) pragmaFlags(pragma syntax.Pragma, allowed ir.PragmaFlag) ir.PragmaFlag {
|
||||
if pragma == nil {
|
||||
return 0
|
||||
}
|
||||
p := pragma.(*pragmas)
|
||||
present := p.Flag & allowed
|
||||
p.Flag &^= allowed
|
||||
g.reportUnused(p)
|
||||
return present
|
||||
}
|
||||
|
||||
// reportUnused reports errors about any unused pragmas.
|
||||
func (g *irgen) reportUnused(pragma *pragmas) {
|
||||
for _, pos := range pragma.Pos {
|
||||
if pos.Flag&pragma.Flag != 0 {
|
||||
base.ErrorfAt(g.makeXPos(pos.Pos), "misplaced compiler directive")
|
||||
}
|
||||
}
|
||||
if len(pragma.Embeds) > 0 {
|
||||
for _, e := range pragma.Embeds {
|
||||
base.ErrorfAt(g.makeXPos(e.Pos), "misplaced go:embed directive")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,340 +7,10 @@ package noder
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
func (g *irgen) expr(expr syntax.Expr) ir.Node {
|
||||
expr = unparen(expr) // skip parens; unneeded after parse+typecheck
|
||||
|
||||
if expr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" {
|
||||
return ir.BlankNode
|
||||
}
|
||||
|
||||
tv := g.typeAndValue(expr)
|
||||
switch {
|
||||
case tv.IsBuiltin():
|
||||
// Qualified builtins, such as unsafe.Add and unsafe.Slice.
|
||||
if expr, ok := expr.(*syntax.SelectorExpr); ok {
|
||||
if name, ok := expr.X.(*syntax.Name); ok {
|
||||
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
|
||||
return g.use(expr.Sel)
|
||||
}
|
||||
}
|
||||
}
|
||||
return g.use(expr.(*syntax.Name))
|
||||
case tv.IsType():
|
||||
return ir.TypeNode(g.typ(tv.Type))
|
||||
case tv.IsValue(), tv.IsVoid():
|
||||
// ok
|
||||
default:
|
||||
base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
|
||||
}
|
||||
|
||||
base.Assert(g.exprStmtOK)
|
||||
|
||||
typ := idealType(tv)
|
||||
if typ == nil {
|
||||
base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", tv.Type)
|
||||
}
|
||||
|
||||
// Constant expression.
|
||||
if tv.Value != nil {
|
||||
typ := g.typ(typ)
|
||||
value := FixValue(typ, tv.Value)
|
||||
return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr))
|
||||
}
|
||||
|
||||
n := g.expr0(typ, expr)
|
||||
if n.Typecheck() != 1 && n.Typecheck() != 3 {
|
||||
base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n)
|
||||
}
|
||||
if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) {
|
||||
base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
|
||||
pos := g.pos(expr)
|
||||
assert(pos.IsKnown())
|
||||
|
||||
// Set base.Pos for transformation code that still uses base.Pos, rather than
|
||||
// the pos of the node being converted.
|
||||
base.Pos = pos
|
||||
|
||||
switch expr := expr.(type) {
|
||||
case *syntax.Name:
|
||||
if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
|
||||
return Nil(pos, g.typ(typ))
|
||||
}
|
||||
return g.use(expr)
|
||||
|
||||
case *syntax.CompositeLit:
|
||||
return g.compLit(typ, expr)
|
||||
|
||||
case *syntax.FuncLit:
|
||||
return g.funcLit(typ, expr)
|
||||
|
||||
case *syntax.AssertExpr:
|
||||
return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type))
|
||||
|
||||
case *syntax.CallExpr:
|
||||
fun := g.expr(expr.Fun)
|
||||
return g.callExpr(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots)
|
||||
|
||||
case *syntax.IndexExpr:
|
||||
args := unpackListExpr(expr.Index)
|
||||
if len(args) == 1 {
|
||||
tv := g.typeAndValue(args[0])
|
||||
if tv.IsValue() {
|
||||
// This is just a normal index expression
|
||||
n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0]))
|
||||
if !g.delayTransform() {
|
||||
// transformIndex will modify n.Type() for OINDEXMAP.
|
||||
transformIndex(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// expr.Index is a list of type args, so we ignore it, since types2 has
|
||||
// already provided this info with the Info.Instances map.
|
||||
return g.expr(expr.X)
|
||||
|
||||
case *syntax.SelectorExpr:
|
||||
// Qualified identifier.
|
||||
if name, ok := expr.X.(*syntax.Name); ok {
|
||||
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
|
||||
return g.use(expr.Sel)
|
||||
}
|
||||
}
|
||||
return g.selectorExpr(pos, typ, expr)
|
||||
|
||||
case *syntax.SliceExpr:
|
||||
n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
|
||||
if !g.delayTransform() {
|
||||
transformSlice(n)
|
||||
}
|
||||
return n
|
||||
|
||||
case *syntax.Operation:
|
||||
if expr.Y == nil {
|
||||
n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X))
|
||||
if n.Op() == ir.OADDR && !g.delayTransform() {
|
||||
transformAddr(n.(*ir.AddrExpr))
|
||||
}
|
||||
return n
|
||||
}
|
||||
switch op := g.op(expr.Op, binOps[:]); op {
|
||||
case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
|
||||
n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
|
||||
if !g.delayTransform() {
|
||||
transformCompare(n)
|
||||
}
|
||||
return n
|
||||
case ir.OANDAND, ir.OOROR:
|
||||
x := g.expr(expr.X)
|
||||
y := g.expr(expr.Y)
|
||||
return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
|
||||
default:
|
||||
n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y))
|
||||
if op == ir.OADD && !g.delayTransform() {
|
||||
return transformAdd(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
default:
|
||||
g.unhandled("expression", expr)
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// substType does a normal type substitution, but tparams is in the form of a field
|
||||
// list, and targs is in terms of a slice of type nodes. substType records any newly
|
||||
// instantiated types into g.instTypeList.
|
||||
func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Ntype) *types.Type {
|
||||
fields := tparams.FieldSlice()
|
||||
tparams1 := make([]*types.Type, len(fields))
|
||||
for i, f := range fields {
|
||||
tparams1[i] = f.Type
|
||||
}
|
||||
targs1 := make([]*types.Type, len(targs))
|
||||
for i, n := range targs {
|
||||
targs1[i] = n.Type()
|
||||
}
|
||||
ts := typecheck.Tsubster{
|
||||
Tparams: tparams1,
|
||||
Targs: targs1,
|
||||
}
|
||||
newt := ts.Typ(typ)
|
||||
return newt
|
||||
}
|
||||
|
||||
// callExpr creates a call expression (which might be a type conversion, built-in
|
||||
// call, or a regular call) and does standard transforms, unless we are in a generic
|
||||
// function.
|
||||
func (g *irgen) callExpr(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
|
||||
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
||||
n.IsDDD = dots
|
||||
typed(typ, n)
|
||||
|
||||
if fun.Op() == ir.OTYPE {
|
||||
// Actually a type conversion, not a function call.
|
||||
if !g.delayTransform() {
|
||||
return transformConvCall(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
||||
if !g.delayTransform() {
|
||||
return transformBuiltin(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Add information, now that we know that fun is actually being called.
|
||||
switch fun := fun.(type) {
|
||||
case *ir.SelectorExpr:
|
||||
if fun.Op() == ir.OMETHVALUE {
|
||||
op := ir.ODOTMETH
|
||||
if fun.X.Type().IsInterface() {
|
||||
op = ir.ODOTINTER
|
||||
}
|
||||
fun.SetOp(op)
|
||||
// Set the type to include the receiver, since that's what
|
||||
// later parts of the compiler expect
|
||||
fun.SetType(fun.Selection.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// A function instantiation (even if fully concrete) shouldn't be
|
||||
// transformed yet, because we need to add the dictionary during the
|
||||
// transformation.
|
||||
if fun.Op() != ir.OFUNCINST && !g.delayTransform() {
|
||||
transformCall(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually
|
||||
// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather
|
||||
// than in typecheck.go.
|
||||
func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node {
|
||||
x := g.expr(expr.X)
|
||||
if x.Type().HasTParam() {
|
||||
// Leave a method call on a type param as an OXDOT, since it can
|
||||
// only be fully transformed once it has an instantiated type.
|
||||
n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value))
|
||||
typed(g.typ(typ), n)
|
||||
return n
|
||||
}
|
||||
|
||||
selinfo := g.info.Selections[expr]
|
||||
// Everything up to the last selection is an implicit embedded field access,
|
||||
// and the last selection is determined by selinfo.Kind().
|
||||
index := selinfo.Index()
|
||||
embeds, last := index[:len(index)-1], index[len(index)-1]
|
||||
|
||||
origx := x
|
||||
for _, ix := range embeds {
|
||||
x = Implicit(DotField(pos, x, ix))
|
||||
}
|
||||
|
||||
kind := selinfo.Kind()
|
||||
if kind == types2.FieldVal {
|
||||
return DotField(pos, x, last)
|
||||
}
|
||||
|
||||
var n ir.Node
|
||||
method2 := selinfo.Obj().(*types2.Func)
|
||||
|
||||
if kind == types2.MethodExpr {
|
||||
// OMETHEXPR is unusual in using directly the node and type of the
|
||||
// original OTYPE node (origx) before passing through embedded
|
||||
// fields, even though the method is selected from the type
|
||||
// (x.Type()) reached after following the embedded fields. We will
|
||||
// actually drop any ODOT nodes we created due to the embedded
|
||||
// fields.
|
||||
n = MethodExpr(pos, origx, x.Type(), last)
|
||||
} else {
|
||||
// Add implicit addr/deref for method values, if needed.
|
||||
if x.Type().IsInterface() {
|
||||
n = DotMethod(pos, x, last)
|
||||
} else {
|
||||
recvType2 := method2.Type().(*types2.Signature).Recv().Type()
|
||||
_, wantPtr := recvType2.(*types2.Pointer)
|
||||
havePtr := x.Type().IsPtr()
|
||||
|
||||
if havePtr != wantPtr {
|
||||
if havePtr {
|
||||
x = Implicit(Deref(pos, x.Type().Elem(), x))
|
||||
} else {
|
||||
x = Implicit(Addr(pos, x))
|
||||
}
|
||||
}
|
||||
recvType2Base := recvType2
|
||||
if wantPtr {
|
||||
recvType2Base = types2.AsPointer(recvType2).Elem()
|
||||
}
|
||||
if recvType2Base.(*types2.Named).TypeParams().Len() > 0 {
|
||||
// recvType2 is the original generic type that is
|
||||
// instantiated for this method call.
|
||||
// selinfo.Recv() is the instantiated type
|
||||
recvType2 = recvType2Base
|
||||
recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name())
|
||||
recvType := recvTypeSym.Def.(*ir.Name).Type()
|
||||
// method is the generic method associated with
|
||||
// the base generic type. The instantiated type may not
|
||||
// have method bodies filled in, if it was imported.
|
||||
method := recvType.Methods().Index(last).Nname.(*ir.Name)
|
||||
n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value))
|
||||
n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type())
|
||||
n.(*ir.SelectorExpr).Selection.Nname = method
|
||||
typed(method.Type(), n)
|
||||
|
||||
xt := deref(x.Type())
|
||||
targs := make([]ir.Ntype, len(xt.RParams()))
|
||||
for i := range targs {
|
||||
targs[i] = ir.TypeNode(xt.RParams()[i])
|
||||
}
|
||||
|
||||
// Create function instantiation with the type
|
||||
// args for the receiver type for the method call.
|
||||
n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs)
|
||||
typed(g.typ(typ), n)
|
||||
return n
|
||||
}
|
||||
|
||||
if !g.match(x.Type(), recvType2, false) {
|
||||
base.FatalfAt(pos, "expected %L to have type %v", x, recvType2)
|
||||
} else {
|
||||
n = DotMethod(pos, x, last)
|
||||
}
|
||||
}
|
||||
}
|
||||
if have, want := n.Sym(), g.selector(method2); have != want {
|
||||
base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
|
||||
return g.exprs(unpackListExpr(expr))
|
||||
}
|
||||
|
||||
func unpackListExpr(expr syntax.Expr) []syntax.Expr {
|
||||
switch expr := expr.(type) {
|
||||
case nil:
|
||||
|
|
@ -352,95 +22,6 @@ func unpackListExpr(expr syntax.Expr) []syntax.Expr {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
|
||||
nodes := make([]ir.Node, len(exprs))
|
||||
for i, expr := range exprs {
|
||||
nodes[i] = g.expr(expr)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
|
||||
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
|
||||
n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
|
||||
n.SetOp(ir.OPTRLIT)
|
||||
return typed(g.typ(typ), n)
|
||||
}
|
||||
|
||||
_, isStruct := types2.CoreType(typ).(*types2.Struct)
|
||||
|
||||
exprs := make([]ir.Node, len(lit.ElemList))
|
||||
for i, elem := range lit.ElemList {
|
||||
switch elem := elem.(type) {
|
||||
case *syntax.KeyValueExpr:
|
||||
var key ir.Node
|
||||
if isStruct {
|
||||
key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name)))
|
||||
} else {
|
||||
key = g.expr(elem.Key)
|
||||
}
|
||||
value := wrapname(g.pos(elem.Value), g.expr(elem.Value))
|
||||
if value.Op() == ir.OPAREN {
|
||||
// Make sure any PAREN node added by wrapper has a type
|
||||
typed(value.(*ir.ParenExpr).X.Type(), value)
|
||||
}
|
||||
exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value)
|
||||
default:
|
||||
exprs[i] = wrapname(g.pos(elem), g.expr(elem))
|
||||
if exprs[i].Op() == ir.OPAREN {
|
||||
// Make sure any PAREN node added by wrapper has a type
|
||||
typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs)
|
||||
typed(g.typ(typ), n)
|
||||
var r ir.Node = n
|
||||
if !g.delayTransform() {
|
||||
r = transformCompLit(n)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
|
||||
fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil)
|
||||
ir.NameClosure(fn.OClosure, ir.CurFunc)
|
||||
|
||||
typ := g.typ(typ2)
|
||||
typed(typ, fn.Nname)
|
||||
typed(typ, fn.OClosure)
|
||||
fn.SetTypecheck(1)
|
||||
|
||||
g.funcBody(fn, nil, expr.Type, expr.Body)
|
||||
|
||||
ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn)
|
||||
|
||||
// TODO(mdempsky): ir.CaptureName should probably handle
|
||||
// copying these fields from the canonical variable.
|
||||
for _, cv := range fn.ClosureVars {
|
||||
cv.SetType(cv.Canonical().Type())
|
||||
cv.SetTypecheck(1)
|
||||
}
|
||||
|
||||
if g.topFuncIsGeneric {
|
||||
// Don't add any closure inside a generic function/method to the
|
||||
// g.target.Decls list, even though it may not be generic itself.
|
||||
// See issue #47514.
|
||||
return ir.UseClosure(fn.OClosure, nil)
|
||||
} else {
|
||||
return ir.UseClosure(fn.OClosure, g.target)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
|
||||
n := g.expr(typ)
|
||||
if n.Op() != ir.OTYPE {
|
||||
base.FatalfAt(g.pos(typ), "expected type: %L", n)
|
||||
}
|
||||
return n.Type()
|
||||
}
|
||||
|
||||
// constExprOp returns an ir.Op that represents the outermost
|
||||
// operation of the given constant expression. It's intended for use
|
||||
// with ir.RawOrigExpr.
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2021 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 noder
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, block *syntax.BlockStmt) {
|
||||
typecheck.Func(fn)
|
||||
|
||||
// TODO(mdempsky): Remove uses of ir.CurFunc and
|
||||
// typecheck.DeclContext after we stop relying on typecheck
|
||||
// for desugaring.
|
||||
outerfn, outerctxt := ir.CurFunc, typecheck.DeclContext
|
||||
ir.CurFunc = fn
|
||||
|
||||
typ := fn.Type()
|
||||
if param := typ.Recv(); param != nil {
|
||||
g.defParam(param, recv, ir.PPARAM)
|
||||
}
|
||||
for i, param := range typ.Params().FieldSlice() {
|
||||
g.defParam(param, sig.ParamList[i], ir.PPARAM)
|
||||
}
|
||||
for i, result := range typ.Results().FieldSlice() {
|
||||
g.defParam(result, sig.ResultList[i], ir.PPARAMOUT)
|
||||
}
|
||||
|
||||
// We may have type-checked a call to this function already and
|
||||
// calculated its size, including parameter offsets. Now that we've
|
||||
// created the parameter Names, force a recalculation to ensure
|
||||
// their offsets are correct.
|
||||
types.RecalcSize(typ)
|
||||
|
||||
if block != nil {
|
||||
typecheck.DeclContext = ir.PAUTO
|
||||
|
||||
fn.Body = g.stmts(block.List)
|
||||
if fn.Body == nil {
|
||||
fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
|
||||
}
|
||||
fn.Endlineno = g.makeXPos(block.Rbrace)
|
||||
|
||||
if base.Flag.Dwarf {
|
||||
g.recordScopes(fn, sig)
|
||||
}
|
||||
}
|
||||
|
||||
ir.CurFunc, typecheck.DeclContext = outerfn, outerctxt
|
||||
}
|
||||
|
||||
func (g *irgen) defParam(param *types.Field, decl *syntax.Field, class ir.Class) {
|
||||
typecheck.DeclContext = class
|
||||
|
||||
var name *ir.Name
|
||||
if decl.Name != nil {
|
||||
name, _ = g.def(decl.Name)
|
||||
} else if class == ir.PPARAMOUT {
|
||||
name = g.obj(g.info.Implicits[decl])
|
||||
}
|
||||
|
||||
if name != nil {
|
||||
param.Nname = name
|
||||
param.Sym = name.Sym() // in case it was renamed
|
||||
}
|
||||
}
|
||||
|
|
@ -10,11 +10,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/dwarfgen"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
|
@ -124,308 +120,6 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
|
|||
return m, pkg, info
|
||||
}
|
||||
|
||||
// check2 type checks a Go package using types2, and then generates IR
|
||||
// using the results.
|
||||
func check2(noders []*noder) {
|
||||
m, pkg, info := checkFiles(noders)
|
||||
|
||||
g := irgen{
|
||||
target: typecheck.Target,
|
||||
self: pkg,
|
||||
info: info,
|
||||
posMap: m,
|
||||
objs: make(map[types2.Object]*ir.Name),
|
||||
typs: make(map[types2.Type]*types.Type),
|
||||
}
|
||||
g.generate(noders)
|
||||
}
|
||||
|
||||
// Information about sub-dictionary entries in a dictionary
|
||||
type subDictInfo struct {
|
||||
// Call or XDOT node that requires a dictionary.
|
||||
callNode ir.Node
|
||||
// Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic
|
||||
// method or function call, since this node will get dropped when the generic
|
||||
// method/function call is transformed to a call on the instantiated shape
|
||||
// function. Nil for other kinds of calls or XDOTs.
|
||||
savedXNode ir.Node
|
||||
}
|
||||
|
||||
// dictInfo is the dictionary format for an instantiation of a generic function with
|
||||
// particular shapes. shapeParams, derivedTypes, subDictCalls, itabConvs, and methodExprClosures
|
||||
// describe the actual dictionary entries in order, and the remaining fields are other info
|
||||
// needed in doing dictionary processing during compilation.
|
||||
type dictInfo struct {
|
||||
// Types substituted for the type parameters, which are shape types.
|
||||
shapeParams []*types.Type
|
||||
// All types derived from those typeparams used in the instantiation.
|
||||
derivedTypes []*types.Type
|
||||
// Nodes in the instantiation that requires a subdictionary. Includes
|
||||
// method and function calls (OCALL), function values (OFUNCINST), method
|
||||
// values/expressions (OXDOT).
|
||||
subDictCalls []subDictInfo
|
||||
// Nodes in the instantiation that are a conversion from a typeparam/derived
|
||||
// type to a specific interface.
|
||||
itabConvs []ir.Node
|
||||
// Method expression closures. For a generic type T with method M(arg1, arg2) res,
|
||||
// these closures are func(rcvr T, arg1, arg2) res.
|
||||
// These closures capture no variables, they are just the generic version of ·f symbols
|
||||
// that live in the dictionary instead of in the readonly globals section.
|
||||
methodExprClosures []methodExprClosure
|
||||
|
||||
// Mapping from each shape type that substitutes a type param, to its
|
||||
// type bound (which is also substituted with shapes if it is parameterized)
|
||||
shapeToBound map[*types.Type]*types.Type
|
||||
|
||||
// For type switches on nonempty interfaces, a map from OTYPE entries of
|
||||
// HasShape type, to the interface type we're switching from.
|
||||
type2switchType map[ir.Node]*types.Type
|
||||
|
||||
startSubDict int // Start of dict entries for subdictionaries
|
||||
startItabConv int // Start of dict entries for itab conversions
|
||||
startMethodExprClosures int // Start of dict entries for closures for method expressions
|
||||
dictLen int // Total number of entries in dictionary
|
||||
}
|
||||
|
||||
type methodExprClosure struct {
|
||||
idx int // index in list of shape parameters
|
||||
name string // method name
|
||||
}
|
||||
|
||||
// instInfo is information gathered on an shape instantiation of a function.
|
||||
type instInfo struct {
|
||||
fun *ir.Func // The instantiated function (with body)
|
||||
dictParam *ir.Name // The node inside fun that refers to the dictionary param
|
||||
|
||||
dictInfo *dictInfo
|
||||
}
|
||||
|
||||
type irgen struct {
|
||||
target *ir.Package
|
||||
self *types2.Package
|
||||
info *types2.Info
|
||||
|
||||
posMap
|
||||
objs map[types2.Object]*ir.Name
|
||||
typs map[types2.Type]*types.Type
|
||||
marker dwarfgen.ScopeMarker
|
||||
|
||||
// laterFuncs records tasks that need to run after all declarations
|
||||
// are processed.
|
||||
laterFuncs []func()
|
||||
// haveEmbed indicates whether the current node belongs to file that
|
||||
// imports "embed" package.
|
||||
haveEmbed bool
|
||||
|
||||
// exprStmtOK indicates whether it's safe to generate expressions or
|
||||
// statements yet.
|
||||
exprStmtOK bool
|
||||
|
||||
// types which we need to finish, by doing g.fillinMethods.
|
||||
typesToFinalize []*typeDelayInfo
|
||||
|
||||
// True when we are compiling a top-level generic function or method. Use to
|
||||
// avoid adding closures of generic functions/methods to the target.Decls
|
||||
// list.
|
||||
topFuncIsGeneric bool
|
||||
|
||||
// The context during type/function/method declarations that is used to
|
||||
// uniquely name type parameters. We need unique names for type params so we
|
||||
// can be sure they match up correctly between types2-to-types1 translation
|
||||
// and types1 importing.
|
||||
curDecl string
|
||||
}
|
||||
|
||||
// genInst has the information for creating needed instantiations and modifying
|
||||
// functions to use instantiations.
|
||||
type genInst struct {
|
||||
dnum int // for generating unique dictionary variables
|
||||
|
||||
// Map from the names of all instantiations to information about the
|
||||
// instantiations.
|
||||
instInfoMap map[*types.Sym]*instInfo
|
||||
|
||||
// Dictionary syms which we need to finish, by writing out any itabconv
|
||||
// or method expression closure entries.
|
||||
dictSymsToFinalize []*delayInfo
|
||||
|
||||
// New instantiations created during this round of buildInstantiations().
|
||||
newInsts []ir.Node
|
||||
}
|
||||
|
||||
func (g *irgen) later(fn func()) {
|
||||
g.laterFuncs = append(g.laterFuncs, fn)
|
||||
}
|
||||
|
||||
type delayInfo struct {
|
||||
gf *ir.Name
|
||||
targs []*types.Type
|
||||
sym *types.Sym
|
||||
off int
|
||||
isMeth bool
|
||||
}
|
||||
|
||||
type typeDelayInfo struct {
|
||||
typ *types2.Named
|
||||
ntyp *types.Type
|
||||
}
|
||||
|
||||
func (g *irgen) generate(noders []*noder) {
|
||||
types.LocalPkg.Name = g.self.Name()
|
||||
typecheck.TypecheckAllowed = true
|
||||
|
||||
// Prevent size calculations until we set the underlying type
|
||||
// for all package-block defined types.
|
||||
types.DeferCheckSize()
|
||||
|
||||
// At this point, types2 has already handled name resolution and
|
||||
// type checking. We just need to map from its object and type
|
||||
// representations to those currently used by the rest of the
|
||||
// compiler. This happens in a few passes.
|
||||
|
||||
// 1. Process all import declarations. We use the compiler's own
|
||||
// importer for this, rather than types2's gcimporter-derived one,
|
||||
// to handle extensions and inline function bodies correctly.
|
||||
//
|
||||
// Also, we need to do this in a separate pass, because mappings are
|
||||
// instantiated on demand. If we interleaved processing import
|
||||
// declarations with other declarations, it's likely we'd end up
|
||||
// wanting to map an object/type from another source file, but not
|
||||
// yet have the import data it relies on.
|
||||
declLists := make([][]syntax.Decl, len(noders))
|
||||
Outer:
|
||||
for i, p := range noders {
|
||||
g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma)
|
||||
for j, decl := range p.file.DeclList {
|
||||
switch decl := decl.(type) {
|
||||
case *syntax.ImportDecl:
|
||||
g.importDecl(p, decl)
|
||||
default:
|
||||
declLists[i] = p.file.DeclList[j:]
|
||||
continue Outer // no more ImportDecls
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Process all package-block type declarations. As with imports,
|
||||
// we need to make sure all types are properly instantiated before
|
||||
// trying to map any expressions that utilize them. In particular,
|
||||
// we need to make sure type pragmas are already known (see comment
|
||||
// in irgen.typeDecl).
|
||||
//
|
||||
// We could perhaps instead defer processing of package-block
|
||||
// variable initializers and function bodies, like noder does, but
|
||||
// special-casing just package-block type declarations minimizes the
|
||||
// differences between processing package-block and function-scoped
|
||||
// declarations.
|
||||
for _, declList := range declLists {
|
||||
for _, decl := range declList {
|
||||
switch decl := decl.(type) {
|
||||
case *syntax.TypeDecl:
|
||||
g.typeDecl((*ir.Nodes)(&g.target.Decls), decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
types.ResumeCheckSize()
|
||||
|
||||
// 3. Process all remaining declarations.
|
||||
for i, declList := range declLists {
|
||||
old := g.haveEmbed
|
||||
g.haveEmbed = noders[i].importedEmbed
|
||||
g.decls((*ir.Nodes)(&g.target.Decls), declList)
|
||||
g.haveEmbed = old
|
||||
}
|
||||
g.exprStmtOK = true
|
||||
|
||||
// 4. Run any "later" tasks. Avoid using 'range' so that tasks can
|
||||
// recursively queue further tasks. (Not currently utilized though.)
|
||||
for len(g.laterFuncs) > 0 {
|
||||
fn := g.laterFuncs[0]
|
||||
g.laterFuncs = g.laterFuncs[1:]
|
||||
fn()
|
||||
}
|
||||
|
||||
if base.Flag.W > 1 {
|
||||
for _, n := range g.target.Decls {
|
||||
s := fmt.Sprintf("\nafter noder2 %v", n)
|
||||
ir.Dump(s, n)
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range noders {
|
||||
// Process linkname and cgo pragmas.
|
||||
p.processPragmas()
|
||||
|
||||
// Double check for any type-checking inconsistencies. This can be
|
||||
// removed once we're confident in IR generation results.
|
||||
syntax.Inspect(p.file, func(n syntax.Node) bool {
|
||||
g.validate(n)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if base.Flag.Complete {
|
||||
for _, n := range g.target.Decls {
|
||||
if fn, ok := n.(*ir.Func); ok {
|
||||
if fn.Body == nil && fn.Nname.Sym().Linkname == "" {
|
||||
base.ErrorfAt(fn.Pos(), "missing function body")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unusual case where noder2 encounters a type error that types2
|
||||
// doesn't check for (e.g. notinheap incompatibility).
|
||||
base.ExitIfErrors()
|
||||
|
||||
typecheck.DeclareUniverse()
|
||||
|
||||
// Create any needed instantiations of generic functions and transform
|
||||
// existing and new functions to use those instantiations.
|
||||
BuildInstantiations()
|
||||
|
||||
// Remove all generic functions from g.target.Decl, since they have been
|
||||
// used for stenciling, but don't compile. Generic functions will already
|
||||
// have been marked for export as appropriate.
|
||||
j := 0
|
||||
for i, decl := range g.target.Decls {
|
||||
if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
|
||||
g.target.Decls[j] = g.target.Decls[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
g.target.Decls = g.target.Decls[:j]
|
||||
|
||||
base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs))
|
||||
}
|
||||
|
||||
func (g *irgen) unhandled(what string, p poser) {
|
||||
base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// delayTransform returns true if we should delay all transforms, because we are
|
||||
// creating the nodes for a generic function/method.
|
||||
func (g *irgen) delayTransform() bool {
|
||||
return g.topFuncIsGeneric
|
||||
}
|
||||
|
||||
func (g *irgen) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
|
||||
tv := x.GetTypeInfo()
|
||||
if tv.Type == nil {
|
||||
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
func (g *irgen) type2(x syntax.Expr) syntax.Type {
|
||||
tv := x.GetTypeInfo()
|
||||
if tv.Type == nil {
|
||||
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
|
||||
}
|
||||
return tv.Type
|
||||
}
|
||||
|
||||
// A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).
|
||||
type cycleFinder struct {
|
||||
cyclic map[*types2.Interface]bool
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
// Copyright 2021 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 noder
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) {
|
||||
obj, ok := g.info.Defs[name]
|
||||
if !ok {
|
||||
base.FatalfAt(g.pos(name), "unknown name %v", name)
|
||||
}
|
||||
return g.obj(obj), obj
|
||||
}
|
||||
|
||||
// use returns the Name or InstExpr node associated with the use of name,
|
||||
// possibly instantiated by type arguments. The returned node will have
|
||||
// the correct type and be marked as typechecked.
|
||||
func (g *irgen) use(name *syntax.Name) ir.Node {
|
||||
obj2, ok := g.info.Uses[name]
|
||||
if !ok {
|
||||
base.FatalfAt(g.pos(name), "unknown name %v", name)
|
||||
}
|
||||
obj := ir.CaptureName(g.pos(name), ir.CurFunc, g.obj(obj2))
|
||||
if obj.Defn != nil && obj.Defn.Op() == ir.ONAME {
|
||||
// If CaptureName created a closure variable, then transfer the
|
||||
// type of the captured name to the new closure variable.
|
||||
obj.SetTypecheck(1)
|
||||
obj.SetType(obj.Defn.Type())
|
||||
}
|
||||
|
||||
if obj.Class == ir.PFUNC {
|
||||
if inst, ok := g.info.Instances[name]; ok {
|
||||
// This is the case where inferring types required the
|
||||
// types of the function arguments.
|
||||
targs := make([]ir.Ntype, inst.TypeArgs.Len())
|
||||
for i := range targs {
|
||||
targs[i] = ir.TypeNode(g.typ(inst.TypeArgs.At(i)))
|
||||
}
|
||||
typ := g.substType(obj.Type(), obj.Type().TParams(), targs)
|
||||
return typed(typ, ir.NewInstExpr(g.pos(name), ir.OFUNCINST, obj, targs))
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// obj returns the Name that represents the given object. If no such Name exists
|
||||
// yet, it will be implicitly created. The returned node will have the correct
|
||||
// type and be marked as typechecked.
|
||||
//
|
||||
// For objects declared at function scope, ir.CurFunc must already be
|
||||
// set to the respective function when the Name is created.
|
||||
func (g *irgen) obj(obj types2.Object) *ir.Name {
|
||||
// For imported objects, we use iimport directly instead of mapping
|
||||
// the types2 representation.
|
||||
if obj.Pkg() != g.self {
|
||||
if sig, ok := obj.Type().(*types2.Signature); ok && sig.Recv() != nil {
|
||||
// We can't import a method by name - must import the type
|
||||
// and access the method from it.
|
||||
base.FatalfAt(g.pos(obj), "tried to import a method directly")
|
||||
}
|
||||
sym := g.sym(obj)
|
||||
if sym.Def != nil {
|
||||
return sym.Def.(*ir.Name)
|
||||
}
|
||||
n := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
|
||||
if n, ok := n.(*ir.Name); ok {
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
}
|
||||
base.FatalfAt(g.pos(obj), "failed to resolve %v", obj)
|
||||
}
|
||||
|
||||
if name, ok := g.objs[obj]; ok {
|
||||
return name // previously mapped
|
||||
}
|
||||
|
||||
var name *ir.Name
|
||||
pos := g.pos(obj)
|
||||
|
||||
class := typecheck.DeclContext
|
||||
if obj.Parent() == g.self.Scope() {
|
||||
class = ir.PEXTERN // forward reference to package-block declaration
|
||||
}
|
||||
|
||||
// "You are in a maze of twisting little passages, all different."
|
||||
switch obj := obj.(type) {
|
||||
case *types2.Const:
|
||||
name = g.objCommon(pos, ir.OLITERAL, g.sym(obj), class, g.typ(obj.Type()))
|
||||
|
||||
case *types2.Func:
|
||||
sig := obj.Type().(*types2.Signature)
|
||||
var sym *types.Sym
|
||||
var typ *types.Type
|
||||
if recv := sig.Recv(); recv == nil {
|
||||
if obj.Name() == "init" {
|
||||
sym = Renameinit()
|
||||
} else {
|
||||
sym = g.sym(obj)
|
||||
}
|
||||
typ = g.typ(sig)
|
||||
} else {
|
||||
sym = g.selector(obj)
|
||||
if !sym.IsBlank() {
|
||||
sym = ir.MethodSym(g.typ(recv.Type()), sym)
|
||||
}
|
||||
typ = g.signature(g.param(recv), sig)
|
||||
}
|
||||
name = g.objCommon(pos, ir.ONAME, sym, ir.PFUNC, typ)
|
||||
|
||||
case *types2.TypeName:
|
||||
if obj.IsAlias() {
|
||||
name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type()))
|
||||
name.SetAlias(true)
|
||||
} else {
|
||||
name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj))
|
||||
g.objFinish(name, class, types.NewNamed(name))
|
||||
}
|
||||
|
||||
case *types2.Var:
|
||||
sym := g.sym(obj)
|
||||
if class == ir.PPARAMOUT && (sym == nil || sym.IsBlank()) {
|
||||
// Backend needs names for result parameters,
|
||||
// even if they're anonymous or blank.
|
||||
nresults := 0
|
||||
for _, n := range ir.CurFunc.Dcl {
|
||||
if n.Class == ir.PPARAMOUT {
|
||||
nresults++
|
||||
}
|
||||
}
|
||||
if sym == nil {
|
||||
sym = typecheck.LookupNum("~r", nresults) // 'r' for "result"
|
||||
} else {
|
||||
sym = typecheck.LookupNum("~b", nresults) // 'b' for "blank"
|
||||
}
|
||||
}
|
||||
name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type()))
|
||||
|
||||
default:
|
||||
g.unhandled("object", obj)
|
||||
}
|
||||
|
||||
g.objs[obj] = name
|
||||
name.SetTypecheck(1)
|
||||
return name
|
||||
}
|
||||
|
||||
func (g *irgen) objCommon(pos src.XPos, op ir.Op, sym *types.Sym, class ir.Class, typ *types.Type) *ir.Name {
|
||||
name := ir.NewDeclNameAt(pos, op, sym)
|
||||
g.objFinish(name, class, typ)
|
||||
return name
|
||||
}
|
||||
|
||||
func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) {
|
||||
sym := name.Sym()
|
||||
|
||||
name.SetType(typ)
|
||||
name.Class = class
|
||||
if name.Class == ir.PFUNC {
|
||||
sym.SetFunc(true)
|
||||
}
|
||||
|
||||
name.SetTypecheck(1)
|
||||
|
||||
if ir.IsBlank(name) {
|
||||
return
|
||||
}
|
||||
|
||||
switch class {
|
||||
case ir.PEXTERN:
|
||||
g.target.Externs = append(g.target.Externs, name)
|
||||
fallthrough
|
||||
case ir.PFUNC:
|
||||
sym.Def = name
|
||||
if name.Class == ir.PFUNC && name.Type().Recv() != nil {
|
||||
break // methods are exported with their receiver type
|
||||
}
|
||||
if types.IsExported(sym.Name) {
|
||||
// Generic functions can be marked for export here, even
|
||||
// though they will not be compiled until instantiated.
|
||||
typecheck.Export(name)
|
||||
}
|
||||
if base.Flag.AsmHdr != "" && !name.Sym().Asm() {
|
||||
name.Sym().SetAsm(true)
|
||||
g.target.Asms = append(g.target.Asms, name)
|
||||
}
|
||||
|
||||
default:
|
||||
// Function-scoped declaration.
|
||||
name.Curfn = ir.CurFunc
|
||||
if name.Op() == ir.ONAME {
|
||||
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2021 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 noder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types2"
|
||||
)
|
||||
|
||||
// recordScopes populates fn.Parents and fn.Marks based on the scoping
|
||||
// information provided by types2.
|
||||
func (g *irgen) recordScopes(fn *ir.Func, sig *syntax.FuncType) {
|
||||
scope, ok := g.info.Scopes[sig]
|
||||
if !ok {
|
||||
base.FatalfAt(fn.Pos(), "missing scope for %v", fn)
|
||||
}
|
||||
|
||||
for i, n := 0, scope.NumChildren(); i < n; i++ {
|
||||
g.walkScope(scope.Child(i))
|
||||
}
|
||||
|
||||
g.marker.WriteTo(fn)
|
||||
}
|
||||
|
||||
func (g *irgen) walkScope(scope *types2.Scope) bool {
|
||||
// types2 doesn't provide a proper API for determining the
|
||||
// lexical element a scope represents, so we have to resort to
|
||||
// string matching. Conveniently though, this allows us to
|
||||
// skip both function types and function literals, neither of
|
||||
// which are interesting to us here.
|
||||
if strings.HasPrefix(scope.String(), "function scope ") {
|
||||
return false
|
||||
}
|
||||
|
||||
g.marker.Push(g.pos(scope))
|
||||
|
||||
haveVars := false
|
||||
for _, name := range scope.Names() {
|
||||
if obj, ok := scope.Lookup(name).(*types2.Var); ok && obj.Name() != "_" {
|
||||
haveVars = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i, n := 0, scope.NumChildren(); i < n; i++ {
|
||||
if g.walkScope(scope.Child(i)) {
|
||||
haveVars = true
|
||||
}
|
||||
}
|
||||
|
||||
if haveVars {
|
||||
g.marker.Pop(g.end(scope))
|
||||
} else {
|
||||
g.marker.Unpush()
|
||||
}
|
||||
|
||||
return haveVars
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,136 +5,10 @@
|
|||
package noder
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// stmts creates nodes for a slice of statements that form a scope.
|
||||
func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
||||
var nodes []ir.Node
|
||||
types.Markdcl()
|
||||
for _, stmt := range stmts {
|
||||
switch s := g.stmt(stmt).(type) {
|
||||
case nil: // EmptyStmt
|
||||
case *ir.BlockStmt:
|
||||
nodes = append(nodes, s.List...)
|
||||
default:
|
||||
nodes = append(nodes, s)
|
||||
}
|
||||
}
|
||||
types.Popdcl()
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
|
||||
base.Assert(g.exprStmtOK)
|
||||
switch stmt := stmt.(type) {
|
||||
case nil, *syntax.EmptyStmt:
|
||||
return nil
|
||||
case *syntax.LabeledStmt:
|
||||
return g.labeledStmt(stmt)
|
||||
case *syntax.BlockStmt:
|
||||
return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
|
||||
case *syntax.ExprStmt:
|
||||
return wrapname(g.pos(stmt.X), g.expr(stmt.X))
|
||||
case *syntax.SendStmt:
|
||||
n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
|
||||
if !g.delayTransform() {
|
||||
transformSend(n)
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
case *syntax.DeclStmt:
|
||||
if g.topFuncIsGeneric && len(stmt.DeclList) > 0 {
|
||||
if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok {
|
||||
// TODO: remove this restriction. See issue 47631.
|
||||
base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported")
|
||||
}
|
||||
}
|
||||
n := ir.NewBlockStmt(g.pos(stmt), nil)
|
||||
g.decls(&n.List, stmt.DeclList)
|
||||
return n
|
||||
|
||||
case *syntax.AssignStmt:
|
||||
if stmt.Op != 0 && stmt.Op != syntax.Def {
|
||||
op := g.op(stmt.Op, binOps[:])
|
||||
var n *ir.AssignOpStmt
|
||||
if stmt.Rhs == nil {
|
||||
n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
|
||||
} else {
|
||||
// Eval rhs before lhs, for compatibility with noder1
|
||||
rhs := g.expr(stmt.Rhs)
|
||||
lhs := g.expr(stmt.Lhs)
|
||||
n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs)
|
||||
}
|
||||
if !g.delayTransform() {
|
||||
transformAsOp(n)
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
}
|
||||
|
||||
// Eval rhs before lhs, for compatibility with noder1
|
||||
rhs := g.exprList(stmt.Rhs)
|
||||
names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
|
||||
|
||||
if len(lhs) == 1 && len(rhs) == 1 {
|
||||
n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
|
||||
n.Def = initDefn(n, names)
|
||||
|
||||
if !g.delayTransform() {
|
||||
lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
|
||||
transformAssign(n, lhs, rhs)
|
||||
n.X, n.Y = lhs[0], rhs[0]
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
}
|
||||
|
||||
n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
|
||||
n.Def = initDefn(n, names)
|
||||
if !g.delayTransform() {
|
||||
transformAssign(n, n.Lhs, n.Rhs)
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
|
||||
case *syntax.BranchStmt:
|
||||
return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
|
||||
case *syntax.CallStmt:
|
||||
return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
|
||||
case *syntax.ReturnStmt:
|
||||
n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
|
||||
if !g.delayTransform() {
|
||||
transformReturn(n)
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
case *syntax.IfStmt:
|
||||
return g.ifStmt(stmt)
|
||||
case *syntax.ForStmt:
|
||||
return g.forStmt(stmt)
|
||||
case *syntax.SelectStmt:
|
||||
n := g.selectStmt(stmt)
|
||||
|
||||
if !g.delayTransform() {
|
||||
transformSelect(n.(*ir.SelectStmt))
|
||||
}
|
||||
n.SetTypecheck(1)
|
||||
return n
|
||||
case *syntax.SwitchStmt:
|
||||
return g.switchStmt(stmt)
|
||||
|
||||
default:
|
||||
g.unhandled("statement", stmt)
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mdempsky): Investigate replacing with switch statements or dense arrays.
|
||||
|
||||
var branchOps = [...]ir.Op{
|
||||
|
|
@ -149,50 +23,6 @@ var callOps = [...]ir.Op{
|
|||
syntax.Go: ir.OGO,
|
||||
}
|
||||
|
||||
func (g *irgen) tokOp(tok int, ops []ir.Op) ir.Op {
|
||||
// TODO(mdempsky): Validate.
|
||||
return ops[tok]
|
||||
}
|
||||
|
||||
func (g *irgen) op(op syntax.Operator, ops []ir.Op) ir.Op {
|
||||
// TODO(mdempsky): Validate.
|
||||
return ops[op]
|
||||
}
|
||||
|
||||
func (g *irgen) assignList(expr syntax.Expr, def bool) ([]*ir.Name, []ir.Node) {
|
||||
if !def {
|
||||
return nil, g.exprList(expr)
|
||||
}
|
||||
|
||||
var exprs []syntax.Expr
|
||||
if list, ok := expr.(*syntax.ListExpr); ok {
|
||||
exprs = list.ElemList
|
||||
} else {
|
||||
exprs = []syntax.Expr{expr}
|
||||
}
|
||||
|
||||
var names []*ir.Name
|
||||
res := make([]ir.Node, len(exprs))
|
||||
for i, expr := range exprs {
|
||||
expr := expr.(*syntax.Name)
|
||||
if expr.Value == "_" {
|
||||
res[i] = ir.BlankNode
|
||||
continue
|
||||
}
|
||||
|
||||
if obj, ok := g.info.Uses[expr]; ok {
|
||||
res[i] = g.obj(obj)
|
||||
continue
|
||||
}
|
||||
|
||||
name, _ := g.def(expr)
|
||||
names = append(names, name)
|
||||
res[i] = name
|
||||
}
|
||||
|
||||
return names, res
|
||||
}
|
||||
|
||||
// initDefn marks the given names as declared by defn and populates
|
||||
// its Init field with ODCL nodes. It then reports whether any names
|
||||
// were so declared, which can be used to initialize defn.Def.
|
||||
|
|
@ -210,25 +40,6 @@ func initDefn(defn ir.InitNode, names []*ir.Name) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (g *irgen) blockStmt(stmt *syntax.BlockStmt) []ir.Node {
|
||||
return g.stmts(stmt.List)
|
||||
}
|
||||
|
||||
func (g *irgen) ifStmt(stmt *syntax.IfStmt) ir.Node {
|
||||
init := g.stmt(stmt.Init)
|
||||
n := ir.NewIfStmt(g.pos(stmt), g.expr(stmt.Cond), g.blockStmt(stmt.Then), nil)
|
||||
if stmt.Else != nil {
|
||||
e := g.stmt(stmt.Else)
|
||||
if e.Op() == ir.OBLOCK {
|
||||
e := e.(*ir.BlockStmt)
|
||||
n.Else = e.List
|
||||
} else {
|
||||
n.Else = []ir.Node{e}
|
||||
}
|
||||
}
|
||||
return g.init(init, n)
|
||||
}
|
||||
|
||||
// unpackTwo returns the first two nodes in list. If list has fewer
|
||||
// than 2 nodes, then the missing nodes are replaced with nils.
|
||||
func unpackTwo(list []ir.Node) (fst, snd ir.Node) {
|
||||
|
|
@ -241,113 +52,3 @@ func unpackTwo(list []ir.Node) (fst, snd ir.Node) {
|
|||
return list[0], list[1]
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node {
|
||||
if r, ok := stmt.Init.(*syntax.RangeClause); ok {
|
||||
names, lhs := g.assignList(r.Lhs, r.Def)
|
||||
key, value := unpackTwo(lhs)
|
||||
n := ir.NewRangeStmt(g.pos(r), key, value, g.expr(r.X), g.blockStmt(stmt.Body))
|
||||
n.Def = initDefn(n, names)
|
||||
if key != nil {
|
||||
transformCheckAssign(n, key)
|
||||
}
|
||||
if value != nil {
|
||||
transformCheckAssign(n, value)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
return ir.NewForStmt(g.pos(stmt), g.stmt(stmt.Init), g.expr(stmt.Cond), g.stmt(stmt.Post), g.blockStmt(stmt.Body))
|
||||
}
|
||||
|
||||
func (g *irgen) selectStmt(stmt *syntax.SelectStmt) ir.Node {
|
||||
body := make([]*ir.CommClause, len(stmt.Body))
|
||||
for i, clause := range stmt.Body {
|
||||
body[i] = ir.NewCommStmt(g.pos(clause), g.stmt(clause.Comm), g.stmts(clause.Body))
|
||||
}
|
||||
return ir.NewSelectStmt(g.pos(stmt), body)
|
||||
}
|
||||
|
||||
func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
|
||||
pos := g.pos(stmt)
|
||||
init := g.stmt(stmt.Init)
|
||||
|
||||
var expr ir.Node
|
||||
switch tag := stmt.Tag.(type) {
|
||||
case *syntax.TypeSwitchGuard:
|
||||
var ident *ir.Ident
|
||||
if tag.Lhs != nil {
|
||||
ident = ir.NewIdent(g.pos(tag.Lhs), g.name(tag.Lhs))
|
||||
}
|
||||
expr = ir.NewTypeSwitchGuard(pos, ident, g.expr(tag.X))
|
||||
default:
|
||||
expr = g.expr(tag)
|
||||
}
|
||||
|
||||
body := make([]*ir.CaseClause, len(stmt.Body))
|
||||
for i, clause := range stmt.Body {
|
||||
// Check for an implicit clause variable before
|
||||
// visiting body, because it may contain function
|
||||
// literals that reference it, and then it'll be
|
||||
// associated to the wrong function.
|
||||
//
|
||||
// Also, override its position to the clause's colon, so that
|
||||
// dwarfgen can find the right scope for it later.
|
||||
// TODO(mdempsky): We should probably just store the scope
|
||||
// directly in the ir.Name.
|
||||
var cv *ir.Name
|
||||
if obj, ok := g.info.Implicits[clause]; ok {
|
||||
cv = g.obj(obj)
|
||||
cv.SetPos(g.makeXPos(clause.Colon))
|
||||
assert(expr.Op() == ir.OTYPESW)
|
||||
cv.Defn = expr
|
||||
}
|
||||
body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body))
|
||||
body[i].Var = cv
|
||||
}
|
||||
|
||||
return g.init(init, ir.NewSwitchStmt(pos, expr, body))
|
||||
}
|
||||
|
||||
func (g *irgen) labeledStmt(label *syntax.LabeledStmt) ir.Node {
|
||||
sym := g.name(label.Label)
|
||||
lhs := ir.NewLabelStmt(g.pos(label), sym)
|
||||
ls := g.stmt(label.Stmt)
|
||||
|
||||
// Attach label directly to control statement too.
|
||||
switch ls := ls.(type) {
|
||||
case *ir.ForStmt:
|
||||
ls.Label = sym
|
||||
case *ir.RangeStmt:
|
||||
ls.Label = sym
|
||||
case *ir.SelectStmt:
|
||||
ls.Label = sym
|
||||
case *ir.SwitchStmt:
|
||||
ls.Label = sym
|
||||
}
|
||||
|
||||
l := []ir.Node{lhs}
|
||||
if ls != nil {
|
||||
if ls.Op() == ir.OBLOCK {
|
||||
ls := ls.(*ir.BlockStmt)
|
||||
l = append(l, ls.List...)
|
||||
} else {
|
||||
l = append(l, ls)
|
||||
}
|
||||
}
|
||||
return ir.NewBlockStmt(src.NoXPos, l)
|
||||
}
|
||||
|
||||
func (g *irgen) init(init ir.Node, stmt ir.InitNode) ir.InitNode {
|
||||
if init != nil {
|
||||
stmt.SetInit([]ir.Node{init})
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (g *irgen) name(name *syntax.Name) *types.Sym {
|
||||
if name == nil {
|
||||
return nil
|
||||
}
|
||||
return typecheck.Lookup(name.Value)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,473 +5,12 @@
|
|||
package noder
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
|
||||
switch pkg {
|
||||
case nil:
|
||||
return types.BuiltinPkg
|
||||
case g.self:
|
||||
return types.LocalPkg
|
||||
case types2.Unsafe:
|
||||
return types.UnsafePkg
|
||||
}
|
||||
return types.NewPkg(pkg.Path(), pkg.Name())
|
||||
}
|
||||
|
||||
var universeAny = types2.Universe.Lookup("any").Type()
|
||||
|
||||
// typ converts a types2.Type to a types.Type, including caching of previously
|
||||
// translated types.
|
||||
func (g *irgen) typ(typ types2.Type) *types.Type {
|
||||
// Defer the CheckSize calls until we have fully-defined a
|
||||
// (possibly-recursive) top-level type.
|
||||
types.DeferCheckSize()
|
||||
res := g.typ1(typ)
|
||||
types.ResumeCheckSize()
|
||||
|
||||
// Finish up any types on typesToFinalize, now that we are at the top of a
|
||||
// fully-defined (possibly recursive) type. fillinMethods could create more
|
||||
// types to finalize.
|
||||
for len(g.typesToFinalize) > 0 {
|
||||
l := len(g.typesToFinalize)
|
||||
info := g.typesToFinalize[l-1]
|
||||
g.typesToFinalize = g.typesToFinalize[:l-1]
|
||||
types.DeferCheckSize()
|
||||
g.fillinMethods(info.typ, info.ntyp)
|
||||
types.ResumeCheckSize()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// typ1 is like typ, but doesn't call CheckSize, since it may have only
|
||||
// constructed part of a recursive type. Should not be called from outside this
|
||||
// file (g.typ is the "external" entry point).
|
||||
func (g *irgen) typ1(typ types2.Type) *types.Type {
|
||||
// See issue 49583: the type checker has trouble keeping track of aliases,
|
||||
// but for such a common alias as any we can improve things by preserving a
|
||||
// pointer identity that can be checked when formatting type strings.
|
||||
if typ == universeAny {
|
||||
return types.AnyType
|
||||
}
|
||||
// Cache type2-to-type mappings. Important so that each defined generic
|
||||
// type (instantiated or not) has a single types.Type representation.
|
||||
// Also saves a lot of computation and memory by avoiding re-translating
|
||||
// types2 types repeatedly.
|
||||
res, ok := g.typs[typ]
|
||||
if !ok {
|
||||
res = g.typ0(typ)
|
||||
// Calculate the size for all concrete types seen by the frontend.
|
||||
// This is the replacement for the CheckSize() calls in the types1
|
||||
// typechecker. These will be deferred until the top-level g.typ().
|
||||
if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() {
|
||||
types.CheckSize(res)
|
||||
}
|
||||
g.typs[typ] = res
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// instTypeName2 creates a name for an instantiated type, base on the type args
|
||||
// (given as types2 types).
|
||||
func (g *irgen) instTypeName2(name string, targs *types2.TypeList) string {
|
||||
rparams := make([]*types.Type, targs.Len())
|
||||
for i := range rparams {
|
||||
rparams[i] = g.typ(targs.At(i))
|
||||
}
|
||||
return typecheck.InstTypeName(name, rparams)
|
||||
}
|
||||
|
||||
// typ0 converts a types2.Type to a types.Type, but doesn't do the caching check
|
||||
// at the top level.
|
||||
func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||
switch typ := typ.(type) {
|
||||
case *types2.Basic:
|
||||
return g.basic(typ)
|
||||
case *types2.Named:
|
||||
// If tparams is set, but targs is not, typ is a base generic
|
||||
// type. typ is appearing as part of the source type of an alias,
|
||||
// since that is the only use of a generic type that doesn't
|
||||
// involve instantiation. We just translate the named type in the
|
||||
// normal way below using g.obj().
|
||||
if typ.TypeParams() != nil && typ.TypeArgs() != nil {
|
||||
// typ is an instantiation of a defined (named) generic type.
|
||||
// This instantiation should also be a defined (named) type.
|
||||
// types2 gives us the substituted type in t.Underlying()
|
||||
// The substituted type may or may not still have type
|
||||
// params. We might, for example, be substituting one type
|
||||
// param for another type param.
|
||||
//
|
||||
// When converted to types.Type, typ has a unique name,
|
||||
// based on the names of the type arguments.
|
||||
instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
|
||||
s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
|
||||
|
||||
// Make sure the base generic type exists in type1 (it may
|
||||
// not yet if we are referecing an imported generic type, as
|
||||
// opposed to a generic type declared in this package). Make
|
||||
// sure to do this lookup before checking s.Def, in case
|
||||
// s.Def gets defined while importing base (if an imported
|
||||
// type). (Issue #50486).
|
||||
base := g.obj(typ.Origin().Obj())
|
||||
|
||||
if s.Def != nil {
|
||||
// We have already encountered this instantiation.
|
||||
// Use the type we previously created, since there
|
||||
// must be exactly one instance of a defined type.
|
||||
return s.Def.Type()
|
||||
}
|
||||
|
||||
if base.Class == ir.PAUTO {
|
||||
// If the base type is a local type, we want to pop
|
||||
// this instantiated type symbol/definition when we
|
||||
// leave the containing block, so we don't use it
|
||||
// incorrectly later.
|
||||
types.Pushdcl(s)
|
||||
}
|
||||
|
||||
// Create a forwarding type first and put it in the g.typs
|
||||
// map, in order to deal with recursive generic types
|
||||
// (including via method signatures). Set up the extra
|
||||
// ntyp information (Def, RParams, which may set
|
||||
// HasTParam) before translating the underlying type
|
||||
// itself, so we handle recursion correctly.
|
||||
ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
|
||||
g.typs[typ] = ntyp
|
||||
|
||||
// If ntyp still has type params, then we must be
|
||||
// referencing something like 'value[T2]', as when
|
||||
// specifying the generic receiver of a method, where
|
||||
// value was defined as "type value[T any] ...". Save the
|
||||
// type args, which will now be the new typeparams of the
|
||||
// current type.
|
||||
//
|
||||
// If ntyp does not have type params, we are saving the
|
||||
// non-generic types used to instantiate this type. We'll
|
||||
// use these when instantiating the methods of the
|
||||
// instantiated type.
|
||||
targs := typ.TypeArgs()
|
||||
rparams := make([]*types.Type, targs.Len())
|
||||
for i := range rparams {
|
||||
rparams[i] = g.typ1(targs.At(i))
|
||||
}
|
||||
ntyp.SetRParams(rparams)
|
||||
//fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam())
|
||||
|
||||
// Save the symbol for the base generic type.
|
||||
ntyp.SetOrigType(base.Type())
|
||||
ntyp.SetUnderlying(g.typ1(typ.Underlying()))
|
||||
if typ.NumMethods() != 0 {
|
||||
// Save a delayed call to g.fillinMethods() (once
|
||||
// potentially recursive types have been fully
|
||||
// resolved).
|
||||
g.typesToFinalize = append(g.typesToFinalize,
|
||||
&typeDelayInfo{
|
||||
typ: typ,
|
||||
ntyp: ntyp,
|
||||
})
|
||||
}
|
||||
return ntyp
|
||||
}
|
||||
obj := g.obj(typ.Obj())
|
||||
if obj.Op() != ir.OTYPE {
|
||||
base.FatalfAt(obj.Pos(), "expected type: %L", obj)
|
||||
}
|
||||
return obj.Type()
|
||||
|
||||
case *types2.Array:
|
||||
return types.NewArray(g.typ1(typ.Elem()), typ.Len())
|
||||
case *types2.Chan:
|
||||
return types.NewChan(g.typ1(typ.Elem()), dirs[typ.Dir()])
|
||||
case *types2.Map:
|
||||
return types.NewMap(g.typ1(typ.Key()), g.typ1(typ.Elem()))
|
||||
case *types2.Pointer:
|
||||
return types.NewPtr(g.typ1(typ.Elem()))
|
||||
case *types2.Signature:
|
||||
return g.signature(nil, typ)
|
||||
case *types2.Slice:
|
||||
return types.NewSlice(g.typ1(typ.Elem()))
|
||||
|
||||
case *types2.Struct:
|
||||
fields := make([]*types.Field, typ.NumFields())
|
||||
for i := range fields {
|
||||
v := typ.Field(i)
|
||||
f := types.NewField(g.pos(v), g.selector(v), g.typ1(v.Type()))
|
||||
f.Note = typ.Tag(i)
|
||||
if v.Embedded() {
|
||||
f.Embedded = 1
|
||||
}
|
||||
fields[i] = f
|
||||
}
|
||||
return types.NewStruct(g.tpkg(typ), fields)
|
||||
|
||||
case *types2.Interface:
|
||||
embeddeds := make([]*types.Field, typ.NumEmbeddeds())
|
||||
j := 0
|
||||
for i := range embeddeds {
|
||||
// TODO(mdempsky): Get embedding position.
|
||||
e := typ.EmbeddedType(i)
|
||||
|
||||
// With Go 1.18, an embedded element can be any type, not
|
||||
// just an interface.
|
||||
embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
|
||||
j++
|
||||
}
|
||||
embeddeds = embeddeds[:j]
|
||||
|
||||
methods := make([]*types.Field, typ.NumExplicitMethods())
|
||||
for i := range methods {
|
||||
m := typ.ExplicitMethod(i)
|
||||
mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature))
|
||||
methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
|
||||
}
|
||||
|
||||
return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...), typ.IsImplicit())
|
||||
|
||||
case *types2.TypeParam:
|
||||
// Save the name of the type parameter in the sym of the type.
|
||||
// Include the types2 subscript in the sym name
|
||||
pkg := g.tpkg(typ)
|
||||
// Create the unique types1 name for a type param, using its context
|
||||
// with a function, type, or method declaration. Also, map blank type
|
||||
// param names to a unique name based on their type param index. The
|
||||
// unique blank names will be exported, but will be reverted during
|
||||
// types2 and gcimporter import.
|
||||
assert(g.curDecl != "")
|
||||
nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index())
|
||||
sym := pkg.Lookup(nm)
|
||||
if sym.Def != nil {
|
||||
// Make sure we use the same type param type for the same
|
||||
// name, whether it is created during types1-import or
|
||||
// this types2-to-types1 translation.
|
||||
return sym.Def.Type()
|
||||
}
|
||||
obj := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym)
|
||||
sym.Def = obj
|
||||
tp := types.NewTypeParam(obj, typ.Index())
|
||||
obj.SetType(tp)
|
||||
// Set g.typs[typ] in case the bound methods reference typ.
|
||||
g.typs[typ] = tp
|
||||
|
||||
bound := g.typ1(typ.Constraint())
|
||||
tp.SetBound(bound)
|
||||
return tp
|
||||
|
||||
case *types2.Union:
|
||||
nt := typ.Len()
|
||||
tlist := make([]*types.Type, nt)
|
||||
tildes := make([]bool, nt)
|
||||
for i := range tlist {
|
||||
t := typ.Term(i)
|
||||
tlist[i] = g.typ1(t.Type())
|
||||
tildes[i] = t.Tilde()
|
||||
}
|
||||
return types.NewUnion(tlist, tildes)
|
||||
|
||||
case *types2.Tuple:
|
||||
// Tuples are used for the type of a function call (i.e. the
|
||||
// return value of the function).
|
||||
if typ == nil {
|
||||
return (*types.Type)(nil)
|
||||
}
|
||||
fields := make([]*types.Field, typ.Len())
|
||||
for i := range fields {
|
||||
fields[i] = g.param(typ.At(i))
|
||||
}
|
||||
t := types.NewStruct(types.LocalPkg, fields)
|
||||
t.StructType().Funarg = types.FunargResults
|
||||
return t
|
||||
|
||||
default:
|
||||
base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ)
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// fillinMethods fills in the method name nodes and types for a defined type with at
|
||||
// least one method. This is needed for later typechecking when looking up methods of
|
||||
// instantiated types, and for actually generating the methods for instantiated
|
||||
// types.
|
||||
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
|
||||
targs2 := typ.TypeArgs()
|
||||
targs := make([]*types.Type, targs2.Len())
|
||||
for i := range targs {
|
||||
targs[i] = g.typ1(targs2.At(i))
|
||||
}
|
||||
|
||||
methods := make([]*types.Field, typ.NumMethods())
|
||||
for i := range methods {
|
||||
m := typ.Method(i)
|
||||
recvType := deref2(types2.AsSignature(m.Type()).Recv().Type())
|
||||
var meth *ir.Name
|
||||
imported := false
|
||||
if m.Pkg() != g.self {
|
||||
// Imported methods cannot be loaded by name (what
|
||||
// g.obj() does) - they must be loaded via their
|
||||
// type.
|
||||
meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name)
|
||||
// XXX Because Obj() returns the object of the base generic
|
||||
// type, we have to still do the method translation below.
|
||||
imported = true
|
||||
} else {
|
||||
meth = g.obj(m)
|
||||
}
|
||||
assert(recvType == types2.Type(typ))
|
||||
if imported {
|
||||
// Unfortunately, meth is the type of the method of the
|
||||
// generic type, so we have to do a substitution to get
|
||||
// the name/type of the method of the instantiated type,
|
||||
// using m.Type().RParams() and typ.TArgs()
|
||||
inst2 := g.instTypeName2("", typ.TypeArgs())
|
||||
name := meth.Sym().Name
|
||||
i1 := strings.Index(name, "[")
|
||||
i2 := strings.Index(name[i1:], "]")
|
||||
assert(i1 >= 0 && i2 >= 0)
|
||||
// Generate the name of the instantiated method.
|
||||
name = name[0:i1] + inst2 + name[i1+i2+1:]
|
||||
newsym := meth.Sym().Pkg.Lookup(name)
|
||||
var meth2 *ir.Name
|
||||
if newsym.Def != nil {
|
||||
meth2 = newsym.Def.(*ir.Name)
|
||||
} else {
|
||||
meth2 = ir.NewNameAt(meth.Pos(), newsym)
|
||||
rparams := types2.AsSignature(m.Type()).RecvTypeParams()
|
||||
tparams := make([]*types.Type, rparams.Len())
|
||||
// Set g.curDecl to be the method context, so type
|
||||
// params in the receiver of the method that we are
|
||||
// translating gets the right unique name. We could
|
||||
// be in a top-level typeDecl, so save and restore
|
||||
// the current contents of g.curDecl.
|
||||
savedCurDecl := g.curDecl
|
||||
g.curDecl = typ.Obj().Name() + "." + m.Name()
|
||||
for i := range tparams {
|
||||
tparams[i] = g.typ1(rparams.At(i))
|
||||
}
|
||||
g.curDecl = savedCurDecl
|
||||
assert(len(tparams) == len(targs))
|
||||
ts := typecheck.Tsubster{
|
||||
Tparams: tparams,
|
||||
Targs: targs,
|
||||
}
|
||||
// Do the substitution of the type
|
||||
meth2.SetType(ts.Typ(meth.Type()))
|
||||
newsym.Def = meth2
|
||||
}
|
||||
meth = meth2
|
||||
}
|
||||
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
|
||||
methods[i].Nname = meth
|
||||
}
|
||||
ntyp.Methods().Set(methods)
|
||||
if !ntyp.HasTParam() && !ntyp.HasShape() {
|
||||
// Generate all the methods for a new fully-instantiated type.
|
||||
typecheck.NeedInstType(ntyp)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
|
||||
tparams2 := sig.TypeParams()
|
||||
tparams := make([]*types.Field, tparams2.Len())
|
||||
for i := range tparams {
|
||||
tp := tparams2.At(i).Obj()
|
||||
tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type()))
|
||||
}
|
||||
|
||||
do := func(typ *types2.Tuple) []*types.Field {
|
||||
fields := make([]*types.Field, typ.Len())
|
||||
for i := range fields {
|
||||
fields[i] = g.param(typ.At(i))
|
||||
}
|
||||
return fields
|
||||
}
|
||||
params := do(sig.Params())
|
||||
results := do(sig.Results())
|
||||
if sig.Variadic() {
|
||||
params[len(params)-1].SetIsDDD(true)
|
||||
}
|
||||
|
||||
return types.NewSignature(g.tpkg(sig), recv, tparams, params, results)
|
||||
}
|
||||
|
||||
func (g *irgen) param(v *types2.Var) *types.Field {
|
||||
return types.NewField(g.pos(v), g.sym(v), g.typ1(v.Type()))
|
||||
}
|
||||
|
||||
func (g *irgen) sym(obj types2.Object) *types.Sym {
|
||||
if name := obj.Name(); name != "" {
|
||||
return g.pkg(obj.Pkg()).Lookup(obj.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *irgen) selector(obj types2.Object) *types.Sym {
|
||||
pkg, name := g.pkg(obj.Pkg()), obj.Name()
|
||||
if types.IsExported(name) {
|
||||
pkg = types.LocalPkg
|
||||
}
|
||||
return pkg.Lookup(name)
|
||||
}
|
||||
|
||||
// tpkg returns the package that a function, interface, struct, or typeparam type
|
||||
// expression appeared in.
|
||||
//
|
||||
// Caveat: For the degenerate types "func()", "interface{}", and
|
||||
// "struct{}", tpkg always returns LocalPkg. However, we only need the
|
||||
// package information so that go/types can report it via its API, and
|
||||
// the reason we fail to return the original package for these
|
||||
// particular types is because go/types does *not* report it for
|
||||
// them. So in practice this limitation is probably moot.
|
||||
func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
|
||||
if obj := anyObj(typ); obj != nil {
|
||||
return g.pkg(obj.Pkg())
|
||||
}
|
||||
return types.LocalPkg
|
||||
}
|
||||
|
||||
// anyObj returns some object accessible from typ, if any.
|
||||
func anyObj(typ types2.Type) types2.Object {
|
||||
switch typ := typ.(type) {
|
||||
case *types2.Signature:
|
||||
if recv := typ.Recv(); recv != nil {
|
||||
return recv
|
||||
}
|
||||
if params := typ.Params(); params.Len() > 0 {
|
||||
return params.At(0)
|
||||
}
|
||||
if results := typ.Results(); results.Len() > 0 {
|
||||
return results.At(0)
|
||||
}
|
||||
case *types2.Struct:
|
||||
if typ.NumFields() > 0 {
|
||||
return typ.Field(0)
|
||||
}
|
||||
case *types2.Interface:
|
||||
if typ.NumExplicitMethods() > 0 {
|
||||
return typ.ExplicitMethod(0)
|
||||
}
|
||||
case *types2.TypeParam:
|
||||
return typ.Obj()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *irgen) basic(typ *types2.Basic) *types.Type {
|
||||
switch typ.Name() {
|
||||
case "byte":
|
||||
return types.ByteType
|
||||
case "rune":
|
||||
return types.RuneType
|
||||
}
|
||||
return *basics[typ.Kind()]
|
||||
}
|
||||
|
||||
var basics = [...]**types.Type{
|
||||
types2.Invalid: new(*types.Type),
|
||||
types2.Bool: &types.Types[types.TBOOL],
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
// Copyright 2021 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 noder
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
)
|
||||
|
||||
// match reports whether types t1 and t2 are consistent
|
||||
// representations for a given expression's type.
|
||||
func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
|
||||
tuple, ok := t2.(*types2.Tuple)
|
||||
if !ok {
|
||||
// Not a tuple; can use simple type identity comparison.
|
||||
return types.Identical(t1, g.typ(t2))
|
||||
}
|
||||
|
||||
if hasOK {
|
||||
// For has-ok values, types2 represents the expression's type as a
|
||||
// 2-element tuple, whereas ir just uses the first type and infers
|
||||
// that the second type is boolean. Must match either, since we
|
||||
// sometimes delay the transformation to the ir form.
|
||||
if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) {
|
||||
return true
|
||||
}
|
||||
return types.Identical(t1, g.typ(t2))
|
||||
}
|
||||
|
||||
if t1 == nil || tuple == nil {
|
||||
return t1 == nil && tuple == nil
|
||||
}
|
||||
if !t1.IsFuncArgStruct() {
|
||||
return false
|
||||
}
|
||||
if t1.NumFields() != tuple.Len() {
|
||||
return false
|
||||
}
|
||||
for i, result := range t1.FieldSlice() {
|
||||
if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *irgen) validate(n syntax.Node) {
|
||||
switch n := n.(type) {
|
||||
case *syntax.CallExpr:
|
||||
tv := g.typeAndValue(n.Fun)
|
||||
if tv.IsBuiltin() {
|
||||
fun := n.Fun
|
||||
for {
|
||||
builtin, ok := fun.(*syntax.ParenExpr)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fun = builtin.X
|
||||
}
|
||||
switch builtin := fun.(type) {
|
||||
case *syntax.Name:
|
||||
g.validateBuiltin(builtin.Value, n)
|
||||
case *syntax.SelectorExpr:
|
||||
g.validateBuiltin(builtin.Sel.Value, n)
|
||||
default:
|
||||
g.unhandled("builtin", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
|
||||
switch name {
|
||||
case "Alignof", "Offsetof", "Sizeof":
|
||||
// Check that types2+gcSizes calculates sizes the same
|
||||
// as cmd/compile does.
|
||||
|
||||
tv := g.typeAndValue(call)
|
||||
if !tv.IsValue() {
|
||||
base.FatalfAt(g.pos(call), "expected a value")
|
||||
}
|
||||
|
||||
if tv.Value == nil {
|
||||
break // unsafe op is not a constant, so no further validation
|
||||
}
|
||||
|
||||
got, ok := constant.Int64Val(tv.Value)
|
||||
if !ok {
|
||||
base.FatalfAt(g.pos(call), "expected int64 constant value")
|
||||
}
|
||||
|
||||
want := g.unsafeExpr(name, call.ArgList[0])
|
||||
if got != want {
|
||||
base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsafeExpr evaluates the given unsafe builtin function on arg.
|
||||
func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 {
|
||||
switch name {
|
||||
case "Alignof":
|
||||
return g.typ(g.type2(arg)).Alignment()
|
||||
case "Sizeof":
|
||||
return g.typ(g.type2(arg)).Size()
|
||||
}
|
||||
|
||||
// Offsetof
|
||||
|
||||
sel := arg.(*syntax.SelectorExpr)
|
||||
selection := g.info.Selections[sel]
|
||||
|
||||
typ := g.typ(g.type2(sel.X))
|
||||
typ = deref(typ)
|
||||
|
||||
var offset int64
|
||||
for _, i := range selection.Index() {
|
||||
// Ensure field offsets have been calculated.
|
||||
types.CalcSize(typ)
|
||||
|
||||
f := typ.Field(i)
|
||||
offset += f.Offset
|
||||
typ = f.Type
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
|
@ -1954,53 +1954,6 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
|
|||
r.Type = objabi.R_USEIFACEMETHOD
|
||||
}
|
||||
|
||||
// getDictionary returns the dictionary for the given named generic function
|
||||
// or method, with the given type arguments.
|
||||
func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
|
||||
if len(targs) == 0 {
|
||||
base.Fatalf("%s should have type arguments", gf.Name)
|
||||
}
|
||||
for _, t := range targs {
|
||||
if t.HasShape() {
|
||||
base.Fatalf("dictionary for %s should only use concrete types: %+v", gf.Name, t)
|
||||
}
|
||||
}
|
||||
|
||||
sym := typecheck.MakeDictSym(gf, targs, true)
|
||||
|
||||
// Dictionary should already have been generated by instantiateMethods().
|
||||
// Extra dictionaries needed because of an inlined function should have been
|
||||
// exported, and so available via Resolve.
|
||||
if lsym := sym.Linksym(); len(lsym.P) == 0 {
|
||||
in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
|
||||
if in.Op() == ir.ONONAME {
|
||||
base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name)
|
||||
}
|
||||
sym = in.Sym()
|
||||
}
|
||||
|
||||
// Make (or reuse) a node referencing the dictionary symbol.
|
||||
var n *ir.Name
|
||||
if sym.Def != nil {
|
||||
n = sym.Def.(*ir.Name)
|
||||
} else {
|
||||
n = typecheck.NewName(sym)
|
||||
n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
|
||||
n.SetTypecheck(1)
|
||||
n.Class = ir.PEXTERN
|
||||
sym.Def = n
|
||||
}
|
||||
|
||||
// Return the address of the dictionary.
|
||||
np := typecheck.NodAddr(n)
|
||||
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
|
||||
// with respect to GC. That saves on stack scanning work, write barriers, etc.
|
||||
// We can get away with it because dictionaries are global variables.
|
||||
np.SetType(types.Types[types.TUINTPTR])
|
||||
np.SetTypecheck(1)
|
||||
return np
|
||||
}
|
||||
|
||||
func deref(t *types.Type) *types.Type {
|
||||
if t.IsPtr() {
|
||||
return t.Elem()
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@
|
|||
|
||||
package typecheck
|
||||
|
||||
import "cmd/compile/internal/types"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Export format
|
||||
|
||||
// Tags. Must be < 0.
|
||||
const (
|
||||
// Objects
|
||||
|
|
@ -18,91 +13,4 @@ const (
|
|||
varTag
|
||||
funcTag
|
||||
endTag
|
||||
|
||||
// Types
|
||||
namedTag
|
||||
arrayTag
|
||||
sliceTag
|
||||
dddTag
|
||||
structTag
|
||||
pointerTag
|
||||
signatureTag
|
||||
interfaceTag
|
||||
mapTag
|
||||
chanTag
|
||||
|
||||
// Values
|
||||
falseTag
|
||||
trueTag
|
||||
int64Tag
|
||||
floatTag
|
||||
fractionTag // not used by gc
|
||||
complexTag
|
||||
stringTag
|
||||
nilTag
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
var predecl []*types.Type // initialized lazily
|
||||
|
||||
func predeclared() []*types.Type {
|
||||
if predecl == nil {
|
||||
// initialize lazily to be sure that all
|
||||
// elements have been initialized before
|
||||
predecl = []*types.Type{
|
||||
// basic types
|
||||
types.Types[types.TBOOL],
|
||||
types.Types[types.TINT],
|
||||
types.Types[types.TINT8],
|
||||
types.Types[types.TINT16],
|
||||
types.Types[types.TINT32],
|
||||
types.Types[types.TINT64],
|
||||
types.Types[types.TUINT],
|
||||
types.Types[types.TUINT8],
|
||||
types.Types[types.TUINT16],
|
||||
types.Types[types.TUINT32],
|
||||
types.Types[types.TUINT64],
|
||||
types.Types[types.TUINTPTR],
|
||||
types.Types[types.TFLOAT32],
|
||||
types.Types[types.TFLOAT64],
|
||||
types.Types[types.TCOMPLEX64],
|
||||
types.Types[types.TCOMPLEX128],
|
||||
types.Types[types.TSTRING],
|
||||
|
||||
// basic type aliases
|
||||
types.ByteType,
|
||||
types.RuneType,
|
||||
|
||||
// error
|
||||
types.ErrorType,
|
||||
|
||||
// untyped types
|
||||
types.UntypedBool,
|
||||
types.UntypedInt,
|
||||
types.UntypedRune,
|
||||
types.UntypedFloat,
|
||||
types.UntypedComplex,
|
||||
types.UntypedString,
|
||||
types.Types[types.TNIL],
|
||||
|
||||
// package unsafe
|
||||
types.Types[types.TUNSAFEPTR],
|
||||
|
||||
// invalid type (package contains errors)
|
||||
types.Types[types.Txxx],
|
||||
|
||||
// any type, for builtin export data
|
||||
types.Types[types.TANY],
|
||||
|
||||
// comparable
|
||||
types.ComparableType,
|
||||
|
||||
// any
|
||||
types.AnyType,
|
||||
}
|
||||
}
|
||||
return predecl
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,378 +0,0 @@
|
|||
// Copyright 2021 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 typecheck
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
// crawlExports crawls the type/object graph rooted at the given list of exported
|
||||
// objects (which are variables, functions, and types). It descends through all parts
|
||||
// of types and follows methods on defined types. Any functions that are found to be
|
||||
// potentially callable by importers directly or after inlining are marked with
|
||||
// ExportInline, so that iexport.go knows to export their inline body.
|
||||
//
|
||||
// The overall purpose of crawlExports is to AVOID exporting inlineable methods
|
||||
// that cannot actually be referenced, thereby reducing the size of the exports
|
||||
// significantly.
|
||||
//
|
||||
// For non-generic defined types reachable from global variables, we only set
|
||||
// ExportInline for exported methods. For defined types that are directly named or are
|
||||
// embedded recursively in such a type, we set ExportInline for all methods, since
|
||||
// these types can be embedded in another local type. For instantiated types that are
|
||||
// used anywhere in a inlineable function, we set ExportInline on all methods of the
|
||||
// base generic type, since all methods will be needed for creating any instantiated
|
||||
// type.
|
||||
func crawlExports(exports []*ir.Name) {
|
||||
p := crawler{
|
||||
marked: make(map[*types.Type]bool),
|
||||
embedded: make(map[*types.Type]bool),
|
||||
generic: make(map[*types.Type]bool),
|
||||
checkFullyInst: make(map[*types.Type]bool),
|
||||
}
|
||||
for _, n := range exports {
|
||||
p.markObject(n)
|
||||
}
|
||||
}
|
||||
|
||||
type crawler struct {
|
||||
marked map[*types.Type]bool // types already seen by markType
|
||||
embedded map[*types.Type]bool // types already seen by markEmbed
|
||||
generic map[*types.Type]bool // types already seen by markGeneric
|
||||
checkFullyInst map[*types.Type]bool // types already seen by checkForFullyInst
|
||||
}
|
||||
|
||||
// markObject visits a reachable object (function, method, global type, or global variable)
|
||||
func (p *crawler) markObject(n *ir.Name) {
|
||||
if n.Op() == ir.ONAME && n.Class == ir.PFUNC {
|
||||
p.markInlBody(n)
|
||||
}
|
||||
|
||||
// If a declared type name is reachable, users can embed it in their
|
||||
// own types, which makes even its unexported methods reachable.
|
||||
if n.Op() == ir.OTYPE {
|
||||
p.markEmbed(n.Type())
|
||||
}
|
||||
|
||||
p.markType(n.Type())
|
||||
}
|
||||
|
||||
// markType recursively visits types reachable from t to identify functions whose
|
||||
// inline bodies may be needed. For instantiated generic types, it visits the base
|
||||
// generic type, which has the relevant methods.
|
||||
func (p *crawler) markType(t *types.Type) {
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = orig
|
||||
}
|
||||
if p.marked[t] {
|
||||
return
|
||||
}
|
||||
p.marked[t] = true
|
||||
|
||||
// If this is a defined type, mark all of its associated
|
||||
// methods. Skip interface types because t.Methods contains
|
||||
// only their unexpanded method set (i.e., exclusive of
|
||||
// interface embeddings), and the switch statement below
|
||||
// handles their full method set.
|
||||
if t.Sym() != nil && t.Kind() != types.TINTER {
|
||||
for _, m := range t.Methods().Slice() {
|
||||
if types.IsExported(m.Sym.Name) {
|
||||
p.markObject(m.Nname.(*ir.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively mark any types that can be produced given a
|
||||
// value of type t: dereferencing a pointer; indexing or
|
||||
// iterating over an array, slice, or map; receiving from a
|
||||
// channel; accessing a struct field or interface method; or
|
||||
// calling a function.
|
||||
//
|
||||
// Notably, we don't mark function parameter types, because
|
||||
// the user already needs some way to construct values of
|
||||
// those types.
|
||||
switch t.Kind() {
|
||||
case types.TPTR, types.TARRAY, types.TSLICE:
|
||||
p.markType(t.Elem())
|
||||
|
||||
case types.TCHAN:
|
||||
if t.ChanDir().CanRecv() {
|
||||
p.markType(t.Elem())
|
||||
}
|
||||
|
||||
case types.TMAP:
|
||||
p.markType(t.Key())
|
||||
p.markType(t.Elem())
|
||||
|
||||
case types.TSTRUCT:
|
||||
if t.IsFuncArgStruct() {
|
||||
break
|
||||
}
|
||||
for _, f := range t.FieldSlice() {
|
||||
// Mark the type of a unexported field if it is a
|
||||
// fully-instantiated type, since we create and instantiate
|
||||
// the methods of any fully-instantiated type that we see
|
||||
// during import (see end of typecheck.substInstType).
|
||||
if types.IsExported(f.Sym.Name) || f.Embedded != 0 ||
|
||||
isPtrFullyInstantiated(f.Type) {
|
||||
p.markType(f.Type)
|
||||
}
|
||||
}
|
||||
|
||||
case types.TFUNC:
|
||||
for _, f := range t.Results().FieldSlice() {
|
||||
p.markType(f.Type)
|
||||
}
|
||||
|
||||
case types.TINTER:
|
||||
for _, f := range t.AllMethods().Slice() {
|
||||
if types.IsExported(f.Sym.Name) {
|
||||
p.markType(f.Type)
|
||||
}
|
||||
}
|
||||
|
||||
case types.TTYPEPARAM:
|
||||
// No other type that needs to be followed.
|
||||
}
|
||||
}
|
||||
|
||||
// markEmbed is similar to markType, but handles finding methods that
|
||||
// need to be re-exported because t can be embedded in user code
|
||||
// (possibly transitively).
|
||||
func (p *crawler) markEmbed(t *types.Type) {
|
||||
if t.IsPtr() {
|
||||
// Defined pointer type; not allowed to embed anyway.
|
||||
if t.Sym() != nil {
|
||||
return
|
||||
}
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = orig
|
||||
}
|
||||
|
||||
if p.embedded[t] {
|
||||
return
|
||||
}
|
||||
p.embedded[t] = true
|
||||
|
||||
// If t is a defined type, then re-export all of its methods. Unlike
|
||||
// in markType, we include even unexported methods here, because we
|
||||
// still need to generate wrappers for them, even if the user can't
|
||||
// refer to them directly.
|
||||
if t.Sym() != nil && t.Kind() != types.TINTER {
|
||||
for _, m := range t.Methods().Slice() {
|
||||
p.markObject(m.Nname.(*ir.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// If t is a struct, recursively visit its embedded fields.
|
||||
if t.IsStruct() {
|
||||
for _, f := range t.FieldSlice() {
|
||||
if f.Embedded != 0 {
|
||||
p.markEmbed(f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// markGeneric takes an instantiated type or a base generic type t, and marks all the
|
||||
// methods of the base generic type of t. If a base generic type is written out for
|
||||
// export, even if not explicitly marked for export, then all of its methods need to
|
||||
// be available for instantiation, since we always create all methods of a specified
|
||||
// instantiated type. Non-exported methods must generally be instantiated, since they may
|
||||
// be called by the exported methods or other generic function in the same package.
|
||||
func (p *crawler) markGeneric(t *types.Type) {
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = orig
|
||||
}
|
||||
if p.generic[t] {
|
||||
return
|
||||
}
|
||||
p.generic[t] = true
|
||||
|
||||
if t.Sym() != nil && t.Kind() != types.TINTER {
|
||||
for _, m := range t.Methods().Slice() {
|
||||
p.markObject(m.Nname.(*ir.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkForFullyInst looks for fully-instantiated types in a type (at any nesting
|
||||
// level). If it finds a fully-instantiated type, it ensures that the necessary
|
||||
// dictionary and shape methods are exported. It updates p.checkFullyInst, so it
|
||||
// traverses each particular type only once.
|
||||
func (p *crawler) checkForFullyInst(t *types.Type) {
|
||||
if p.checkFullyInst[t] {
|
||||
return
|
||||
}
|
||||
p.checkFullyInst[t] = true
|
||||
|
||||
if t.IsFullyInstantiated() && !t.HasShape() && !t.IsInterface() && t.Methods().Len() > 0 {
|
||||
// For any fully-instantiated type, the relevant
|
||||
// dictionaries and shape instantiations will have
|
||||
// already been created or are in the import data.
|
||||
// Make sure that they are exported, so that any
|
||||
// other package that inlines this function will have
|
||||
// them available for import, and so will not need
|
||||
// another round of method and dictionary
|
||||
// instantiation after inlining.
|
||||
baseType := t.OrigType()
|
||||
shapes := make([]*types.Type, len(t.RParams()))
|
||||
for i, t1 := range t.RParams() {
|
||||
shapes[i] = Shapify(t1, i, baseType.RParams()[i])
|
||||
}
|
||||
for j, tmethod := range t.Methods().Slice() {
|
||||
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
|
||||
dictsym := MakeDictSym(baseNname.Sym(), t.RParams(), true)
|
||||
if dictsym.Def == nil {
|
||||
in := Resolve(ir.NewIdent(src.NoXPos, dictsym))
|
||||
dictsym = in.Sym()
|
||||
}
|
||||
Export(dictsym.Def.(*ir.Name))
|
||||
methsym := MakeFuncInstSym(baseNname.Sym(), shapes, false, true)
|
||||
if methsym.Def == nil {
|
||||
in := Resolve(ir.NewIdent(src.NoXPos, methsym))
|
||||
methsym = in.Sym()
|
||||
}
|
||||
methNode := methsym.Def.(*ir.Name)
|
||||
Export(methNode)
|
||||
if HaveInlineBody(methNode.Func) {
|
||||
// Export the body as well if
|
||||
// instantiation is inlineable.
|
||||
ImportedBody(methNode.Func)
|
||||
methNode.Func.SetExportInline(true)
|
||||
}
|
||||
// Make sure that any associated types are also exported. (See #52279)
|
||||
p.checkForFullyInst(tmethod.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// Descend into the type. We descend even if it is a fully-instantiated type,
|
||||
// since the instantiated type may have other instantiated types inside of
|
||||
// it (in fields, methods, etc.).
|
||||
switch t.Kind() {
|
||||
case types.TPTR, types.TARRAY, types.TSLICE:
|
||||
p.checkForFullyInst(t.Elem())
|
||||
|
||||
case types.TCHAN:
|
||||
p.checkForFullyInst(t.Elem())
|
||||
|
||||
case types.TMAP:
|
||||
p.checkForFullyInst(t.Key())
|
||||
p.checkForFullyInst(t.Elem())
|
||||
|
||||
case types.TSTRUCT:
|
||||
if t.IsFuncArgStruct() {
|
||||
break
|
||||
}
|
||||
for _, f := range t.FieldSlice() {
|
||||
p.checkForFullyInst(f.Type)
|
||||
}
|
||||
|
||||
case types.TFUNC:
|
||||
if recv := t.Recv(); recv != nil {
|
||||
p.checkForFullyInst(t.Recv().Type)
|
||||
}
|
||||
for _, f := range t.Params().FieldSlice() {
|
||||
p.checkForFullyInst(f.Type)
|
||||
}
|
||||
for _, f := range t.Results().FieldSlice() {
|
||||
p.checkForFullyInst(f.Type)
|
||||
}
|
||||
|
||||
case types.TINTER:
|
||||
for _, f := range t.AllMethods().Slice() {
|
||||
p.checkForFullyInst(f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// markInlBody marks n's inline body for export and recursively
|
||||
// ensures all called functions are marked too.
|
||||
func (p *crawler) markInlBody(n *ir.Name) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
|
||||
base.Fatalf("markInlBody: unexpected %v, %v, %v", n, n.Op(), n.Class)
|
||||
}
|
||||
fn := n.Func
|
||||
if fn == nil {
|
||||
base.Fatalf("markInlBody: missing Func on %v", n)
|
||||
}
|
||||
if !HaveInlineBody(fn) {
|
||||
return
|
||||
}
|
||||
|
||||
if fn.ExportInline() {
|
||||
return
|
||||
}
|
||||
fn.SetExportInline(true)
|
||||
|
||||
ImportedBody(fn)
|
||||
|
||||
var doFlood func(n ir.Node)
|
||||
doFlood = func(n ir.Node) {
|
||||
t := n.Type()
|
||||
if t != nil {
|
||||
if t.HasTParam() {
|
||||
// If any generic types are used, then make sure that
|
||||
// the methods of the generic type are exported and
|
||||
// scanned for other possible exports.
|
||||
p.markGeneric(t)
|
||||
} else {
|
||||
p.checkForFullyInst(t)
|
||||
}
|
||||
}
|
||||
|
||||
switch n.Op() {
|
||||
case ir.OMETHEXPR, ir.ODOTMETH:
|
||||
p.markInlBody(ir.MethodExprName(n))
|
||||
case ir.ONAME:
|
||||
n := n.(*ir.Name)
|
||||
switch n.Class {
|
||||
case ir.PFUNC:
|
||||
p.markInlBody(n)
|
||||
// Note: this Export() and the one below seem unneeded,
|
||||
// since any function/extern name encountered in an
|
||||
// exported function body will be exported
|
||||
// automatically via qualifiedIdent() in iexport.go.
|
||||
Export(n)
|
||||
case ir.PEXTERN:
|
||||
Export(n)
|
||||
}
|
||||
case ir.OMETHVALUE:
|
||||
// Okay, because we don't yet inline indirect
|
||||
// calls to method values.
|
||||
case ir.OCLOSURE:
|
||||
// VisitList doesn't visit closure bodies, so force a
|
||||
// recursive call to VisitList on the body of the closure.
|
||||
ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood)
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively identify all referenced functions for
|
||||
// reexport. We want to include even non-called functions,
|
||||
// because after inlining they might be callable.
|
||||
ir.VisitList(fn.Inl.Body, doFlood)
|
||||
}
|
||||
|
||||
// isPtrFullyInstantiated returns true if t is a fully-instantiated type, or it is a
|
||||
// pointer to a fully-instantiated type.
|
||||
func isPtrFullyInstantiated(t *types.Type) bool {
|
||||
return t.IsPtr() && t.Elem().IsFullyInstantiated() ||
|
||||
t.IsFullyInstantiated()
|
||||
}
|
||||
|
|
@ -137,54 +137,6 @@ func MethodValueType(n *ir.SelectorExpr) *types.Type {
|
|||
return t
|
||||
}
|
||||
|
||||
// ImportedBody returns immediately if the inlining information for fn is
|
||||
// populated. Otherwise, fn must be an imported function. If so, ImportedBody
|
||||
// loads in the dcls and body for fn, and typechecks as needed.
|
||||
func ImportedBody(fn *ir.Func) {
|
||||
if fn.Inl.Body != nil {
|
||||
return
|
||||
}
|
||||
lno := ir.SetPos(fn.Nname)
|
||||
|
||||
// When we load an inlined body, we need to allow OADDR
|
||||
// operations on untyped expressions. We will fix the
|
||||
// addrtaken flags on all the arguments of the OADDR with the
|
||||
// computeAddrtaken call below (after we typecheck the body).
|
||||
// TODO: export/import types and addrtaken marks along with inlined bodies,
|
||||
// so this will be unnecessary.
|
||||
IncrementalAddrtaken = false
|
||||
defer func() {
|
||||
if DirtyAddrtaken {
|
||||
// We do ComputeAddrTaken on function instantiations, but not
|
||||
// generic functions (since we may not yet know if x in &x[i]
|
||||
// is an array or a slice).
|
||||
if !fn.Type().HasTParam() {
|
||||
ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available
|
||||
}
|
||||
DirtyAddrtaken = false
|
||||
}
|
||||
IncrementalAddrtaken = true
|
||||
}()
|
||||
|
||||
ImportBody(fn)
|
||||
|
||||
// Stmts(fn.Inl.Body) below is only for imported functions;
|
||||
// their bodies may refer to unsafe as long as the package
|
||||
// was marked safe during import (which was checked then).
|
||||
// the ->inl of a local function has been typechecked before CanInline copied it.
|
||||
pkg := fnpkg(fn.Nname)
|
||||
|
||||
if pkg == types.LocalPkg || pkg == nil {
|
||||
return // ImportedBody on local function
|
||||
}
|
||||
|
||||
if base.Flag.LowerM > 2 || base.Debug.Export != 0 {
|
||||
fmt.Printf("typecheck import [%v] %L { %v }\n", fn.Sym(), fn, ir.Nodes(fn.Inl.Body))
|
||||
}
|
||||
|
||||
base.Pos = lno
|
||||
}
|
||||
|
||||
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
|
||||
// the ->sym can be re-used in the local package, so peel it off the receiver's type.
|
||||
func fnpkg(fn *ir.Name) *types.Pkg {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,6 @@
|
|||
package typecheck
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -14,7 +13,6 @@ import (
|
|||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
|
|
@ -888,696 +886,6 @@ type symlink struct {
|
|||
field *types.Field
|
||||
}
|
||||
|
||||
// TypesOf converts a list of nodes to a list
|
||||
// of types of those nodes.
|
||||
func TypesOf(x []ir.Ntype) []*types.Type {
|
||||
r := make([]*types.Type, len(x))
|
||||
for i, n := range x {
|
||||
r[i] = n.Type()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// addTargs writes out the targs to buffer b as a comma-separated list enclosed by
|
||||
// brackets.
|
||||
func addTargs(b *bytes.Buffer, targs []*types.Type) {
|
||||
b.WriteByte('[')
|
||||
for i, targ := range targs {
|
||||
if i > 0 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
// Make sure that type arguments (including type params), are
|
||||
// uniquely specified. LinkString() eliminates all spaces
|
||||
// and includes the package path (local package path is "" before
|
||||
// linker substitution).
|
||||
tstring := targ.LinkString()
|
||||
b.WriteString(tstring)
|
||||
}
|
||||
b.WriteString("]")
|
||||
}
|
||||
|
||||
// InstTypeName creates a name for an instantiated type, based on the name of the
|
||||
// generic type and the type args.
|
||||
func InstTypeName(name string, targs []*types.Type) string {
|
||||
b := bytes.NewBufferString(name)
|
||||
addTargs(b, targs)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// makeInstName1 returns the name of the generic function instantiated with the
|
||||
// given types, which can have type params or shapes, or be concrete types. name is
|
||||
// the name of the generic function or method.
|
||||
func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string {
|
||||
b := bytes.NewBufferString("")
|
||||
i := strings.Index(name, "[")
|
||||
assert(hasBrackets == (i >= 0))
|
||||
if i >= 0 {
|
||||
b.WriteString(name[0:i])
|
||||
} else {
|
||||
b.WriteString(name)
|
||||
}
|
||||
addTargs(b, targs)
|
||||
if i >= 0 {
|
||||
i2 := strings.LastIndex(name[i:], "]")
|
||||
assert(i2 >= 0)
|
||||
b.WriteString(name[i+i2+1:])
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// MakeFuncInstSym makes the unique sym for a stenciled generic function or method,
|
||||
// based on the name of the function gf and the targs. It replaces any
|
||||
// existing bracket type list in the name. MakeInstName asserts that gf has
|
||||
// brackets in its name if and only if hasBrackets is true.
|
||||
//
|
||||
// Names of declared generic functions have no brackets originally, so hasBrackets
|
||||
// should be false. Names of generic methods already have brackets, since the new
|
||||
// type parameter is specified in the generic type of the receiver (e.g. func
|
||||
// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set.
|
||||
//
|
||||
// The standard naming is something like: 'genFn[int,bool]' for functions and
|
||||
// '(*genType[int,bool]).methodName' for methods
|
||||
//
|
||||
// isMethodNode specifies if the name of a method node is being generated (as opposed
|
||||
// to a name of an instantiation of generic function or name of the shape-based
|
||||
// function that helps implement a method of an instantiated type). For method nodes
|
||||
// on shape types, we prepend "nofunc.", because method nodes for shape types will
|
||||
// have no body, and we want to avoid a name conflict with the shape-based function
|
||||
// that helps implement the same method for fully-instantiated types. Function names
|
||||
// are also created at the end of (*Tsubster).typ1, so we append "nofunc" there as
|
||||
// well, as needed.
|
||||
func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, isMethodNode, hasBrackets bool) *types.Sym {
|
||||
nm := makeInstName1(gf.Name, targs, hasBrackets)
|
||||
if targs[0].HasShape() && isMethodNode {
|
||||
nm = "nofunc." + nm
|
||||
}
|
||||
return gf.Pkg.Lookup(nm)
|
||||
}
|
||||
|
||||
func MakeDictSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
|
||||
for _, targ := range targs {
|
||||
if targ.HasTParam() {
|
||||
fmt.Printf("FUNCTION %s\n", gf.Name)
|
||||
for _, targ := range targs {
|
||||
fmt.Printf(" PARAM %+v\n", targ)
|
||||
}
|
||||
panic("dictionary should always have concrete type args")
|
||||
}
|
||||
}
|
||||
name := makeInstName1(gf.Name, targs, hasBrackets)
|
||||
name = fmt.Sprintf("%s.%s", objabi.GlobalDictPrefix, name)
|
||||
return gf.Pkg.Lookup(name)
|
||||
}
|
||||
|
||||
func assert(p bool) {
|
||||
base.Assert(p)
|
||||
}
|
||||
|
||||
// List of newly fully-instantiated types who should have their methods generated.
|
||||
var instTypeList []*types.Type
|
||||
|
||||
// NeedInstType adds a new fully-instantiated type to instTypeList.
|
||||
func NeedInstType(t *types.Type) {
|
||||
instTypeList = append(instTypeList, t)
|
||||
}
|
||||
|
||||
// GetInstTypeList returns the current contents of instTypeList.
|
||||
func GetInstTypeList() []*types.Type {
|
||||
r := instTypeList
|
||||
return r
|
||||
}
|
||||
|
||||
// ClearInstTypeList clears the contents of instTypeList.
|
||||
func ClearInstTypeList() {
|
||||
instTypeList = nil
|
||||
}
|
||||
|
||||
// General type substituter, for replacing typeparams with type args.
|
||||
type Tsubster struct {
|
||||
Tparams []*types.Type
|
||||
Targs []*types.Type
|
||||
// If non-nil, the substitution map from name nodes in the generic function to the
|
||||
// name nodes in the new stenciled function.
|
||||
Vars map[*ir.Name]*ir.Name
|
||||
// If non-nil, function to substitute an incomplete (TFORW) type.
|
||||
SubstForwFunc func(*types.Type) *types.Type
|
||||
// Prevent endless recursion on functions. See #51832.
|
||||
Funcs map[*types.Type]bool
|
||||
}
|
||||
|
||||
// Typ computes the type obtained by substituting any type parameter or shape in t
|
||||
// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
|
||||
// If t contains no type parameters, the result is t; otherwise the result is a new
|
||||
// type. It deals with recursive types by using TFORW types and finding partially or
|
||||
// fully created types via sym.Def.
|
||||
func (ts *Tsubster) Typ(t *types.Type) *types.Type {
|
||||
// Defer the CheckSize calls until we have fully-defined
|
||||
// (possibly-recursive) top-level type.
|
||||
types.DeferCheckSize()
|
||||
r := ts.typ1(t)
|
||||
types.ResumeCheckSize()
|
||||
return r
|
||||
}
|
||||
|
||||
func (ts *Tsubster) typ1(t *types.Type) *types.Type {
|
||||
hasParamOrShape := t.HasTParam() || t.HasShape()
|
||||
if !hasParamOrShape && t.Kind() != types.TFUNC {
|
||||
// Note: function types need to be copied regardless, as the
|
||||
// types of closures may contain declarations that need
|
||||
// to be copied. See #45738.
|
||||
return t
|
||||
}
|
||||
|
||||
if t.IsTypeParam() || t.IsShape() {
|
||||
for i, tp := range ts.Tparams {
|
||||
if tp == t {
|
||||
return ts.Targs[i]
|
||||
}
|
||||
}
|
||||
// If t is a simple typeparam T, then t has the name/symbol 'T'
|
||||
// and t.Underlying() == t.
|
||||
//
|
||||
// However, consider the type definition: 'type P[T any] T'. We
|
||||
// might use this definition so we can have a variant of type T
|
||||
// that we can add new methods to. Suppose t is a reference to
|
||||
// P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM,
|
||||
// because P[T] is defined as T. If we look at t.Underlying(), it
|
||||
// is different, because the name of t.Underlying() is 'T' rather
|
||||
// than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM.
|
||||
// In this case, we do the needed recursive substitution in the
|
||||
// case statement below.
|
||||
if t.Underlying() == t {
|
||||
// t is a simple typeparam that didn't match anything in tparam
|
||||
return t
|
||||
}
|
||||
// t is a more complex typeparam (e.g. P[T], as above, whose
|
||||
// definition is just T).
|
||||
assert(t.Sym() != nil)
|
||||
}
|
||||
|
||||
var newsym *types.Sym
|
||||
var neededTargs []*types.Type
|
||||
var targsChanged bool // == are there any substitutions from this
|
||||
var forw *types.Type
|
||||
|
||||
if t.Sym() != nil && hasParamOrShape {
|
||||
// Need to test for t.HasTParam() again because of special TFUNC case above.
|
||||
// Translate the type params for this type according to
|
||||
// the tparam/targs mapping from subst.
|
||||
neededTargs = make([]*types.Type, len(t.RParams()))
|
||||
for i, rparam := range t.RParams() {
|
||||
neededTargs[i] = ts.typ1(rparam)
|
||||
if !types.IdenticalStrict(neededTargs[i], rparam) {
|
||||
targsChanged = true
|
||||
}
|
||||
}
|
||||
// For a named (defined) type, we have to change the name of the
|
||||
// type as well. We do this first, so we can look up if we've
|
||||
// already seen this type during this substitution or other
|
||||
// definitions/substitutions.
|
||||
genName := genericTypeName(t.Sym())
|
||||
newsym = t.Sym().Pkg.Lookup(InstTypeName(genName, neededTargs))
|
||||
if newsym.Def != nil {
|
||||
// We've already created this instantiated defined type.
|
||||
return newsym.Def.Type()
|
||||
}
|
||||
|
||||
// In order to deal with recursive generic types, create a TFORW
|
||||
// type initially and set the Def field of its sym, so it can be
|
||||
// found if this type appears recursively within the type.
|
||||
forw = NewIncompleteNamedType(t.Pos(), newsym)
|
||||
//println("Creating new type by sub", newsym.Name, forw.HasTParam())
|
||||
forw.SetRParams(neededTargs)
|
||||
// Copy the OrigType from the re-instantiated type (which is the sym of
|
||||
// the base generic type).
|
||||
assert(t.OrigType() != nil)
|
||||
forw.SetOrigType(t.OrigType())
|
||||
}
|
||||
|
||||
var newt *types.Type
|
||||
|
||||
switch t.Kind() {
|
||||
case types.TTYPEPARAM:
|
||||
if t.Sym() == newsym && !targsChanged {
|
||||
// The substitution did not change the type.
|
||||
return t
|
||||
}
|
||||
// Substitute the underlying typeparam (e.g. T in P[T], see
|
||||
// the example describing type P[T] above).
|
||||
newt = ts.typ1(t.Underlying())
|
||||
assert(newt != t)
|
||||
|
||||
case types.TARRAY:
|
||||
elem := t.Elem()
|
||||
newelem := ts.typ1(elem)
|
||||
if newelem != elem || targsChanged {
|
||||
newt = types.NewArray(newelem, t.NumElem())
|
||||
}
|
||||
|
||||
case types.TPTR:
|
||||
elem := t.Elem()
|
||||
newelem := ts.typ1(elem)
|
||||
if newelem != elem || targsChanged {
|
||||
newt = types.NewPtr(newelem)
|
||||
}
|
||||
|
||||
case types.TSLICE:
|
||||
elem := t.Elem()
|
||||
newelem := ts.typ1(elem)
|
||||
if newelem != elem || targsChanged {
|
||||
newt = types.NewSlice(newelem)
|
||||
}
|
||||
|
||||
case types.TSTRUCT:
|
||||
newt = ts.tstruct(t, targsChanged)
|
||||
if newt == t {
|
||||
newt = nil
|
||||
}
|
||||
|
||||
case types.TFUNC:
|
||||
// watch out for endless recursion on plain function types that mention themselves, e.g. "type T func() T"
|
||||
if !hasParamOrShape {
|
||||
if ts.Funcs[t] { // Visit such function types only once.
|
||||
return t
|
||||
}
|
||||
if ts.Funcs == nil {
|
||||
// allocate lazily
|
||||
ts.Funcs = make(map[*types.Type]bool)
|
||||
}
|
||||
ts.Funcs[t] = true
|
||||
}
|
||||
newrecvs := ts.tstruct(t.Recvs(), false)
|
||||
newparams := ts.tstruct(t.Params(), false)
|
||||
newresults := ts.tstruct(t.Results(), false)
|
||||
// Translate the tparams of a signature.
|
||||
newtparams := ts.tstruct(t.TParams(), false)
|
||||
if newrecvs != t.Recvs() || newparams != t.Params() ||
|
||||
newresults != t.Results() || newtparams != t.TParams() || targsChanged {
|
||||
// If any types have changed, then the all the fields of
|
||||
// of recv, params, and results must be copied, because they have
|
||||
// offset fields that are dependent, and so must have an
|
||||
// independent copy for each new signature.
|
||||
var newrecv *types.Field
|
||||
if newrecvs.NumFields() > 0 {
|
||||
if newrecvs == t.Recvs() {
|
||||
newrecvs = ts.tstruct(t.Recvs(), true)
|
||||
}
|
||||
newrecv = newrecvs.Field(0)
|
||||
}
|
||||
if newparams == t.Params() {
|
||||
newparams = ts.tstruct(t.Params(), true)
|
||||
}
|
||||
if newresults == t.Results() {
|
||||
newresults = ts.tstruct(t.Results(), true)
|
||||
}
|
||||
var tparamfields []*types.Field
|
||||
if newtparams.HasTParam() {
|
||||
tparamfields = newtparams.FieldSlice()
|
||||
} else {
|
||||
// Completely remove the tparams from the resulting
|
||||
// signature, if the tparams are now concrete types.
|
||||
tparamfields = nil
|
||||
}
|
||||
newt = types.NewSignature(t.Pkg(), newrecv, tparamfields,
|
||||
newparams.FieldSlice(), newresults.FieldSlice())
|
||||
}
|
||||
if !hasParamOrShape {
|
||||
delete(ts.Funcs, t)
|
||||
}
|
||||
|
||||
case types.TINTER:
|
||||
newt = ts.tinter(t, targsChanged)
|
||||
if newt == t {
|
||||
newt = nil
|
||||
}
|
||||
|
||||
case types.TMAP:
|
||||
newkey := ts.typ1(t.Key())
|
||||
newval := ts.typ1(t.Elem())
|
||||
if newkey != t.Key() || newval != t.Elem() || targsChanged {
|
||||
newt = types.NewMap(newkey, newval)
|
||||
}
|
||||
|
||||
case types.TCHAN:
|
||||
elem := t.Elem()
|
||||
newelem := ts.typ1(elem)
|
||||
if newelem != elem || targsChanged {
|
||||
newt = types.NewChan(newelem, t.ChanDir())
|
||||
}
|
||||
case types.TFORW:
|
||||
if ts.SubstForwFunc != nil {
|
||||
return ts.SubstForwFunc(forw)
|
||||
} else {
|
||||
assert(false)
|
||||
}
|
||||
case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
|
||||
types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
|
||||
types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
|
||||
newt = t.Underlying()
|
||||
case types.TUNION:
|
||||
nt := t.NumTerms()
|
||||
newterms := make([]*types.Type, nt)
|
||||
tildes := make([]bool, nt)
|
||||
changed := false
|
||||
for i := 0; i < nt; i++ {
|
||||
term, tilde := t.Term(i)
|
||||
tildes[i] = tilde
|
||||
newterms[i] = ts.typ1(term)
|
||||
if newterms[i] != term {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
newt = types.NewUnion(newterms, tildes)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Bad type in (*TSubster).Typ: %v", t.Kind()))
|
||||
}
|
||||
if newt == nil {
|
||||
// Even though there were typeparams in the type, there may be no
|
||||
// change if this is a function type for a function call (which will
|
||||
// have its own tparams/targs in the function instantiation).
|
||||
return t
|
||||
}
|
||||
|
||||
if forw != nil {
|
||||
forw.SetUnderlying(newt)
|
||||
newt = forw
|
||||
}
|
||||
|
||||
if !newt.HasTParam() && !newt.IsFuncArgStruct() {
|
||||
// Calculate the size of any new types created. These will be
|
||||
// deferred until the top-level ts.Typ() or g.typ() (if this is
|
||||
// called from g.fillinMethods()).
|
||||
types.CheckSize(newt)
|
||||
}
|
||||
|
||||
if t.Kind() != types.TINTER && t.Methods().Len() > 0 {
|
||||
// Fill in the method info for the new type.
|
||||
var newfields []*types.Field
|
||||
newfields = make([]*types.Field, t.Methods().Len())
|
||||
for i, f := range t.Methods().Slice() {
|
||||
t2 := ts.typ1(f.Type)
|
||||
oldsym := f.Nname.Sym()
|
||||
|
||||
// Use the name of the substituted receiver to create the
|
||||
// method name, since the receiver name may have many levels
|
||||
// of nesting (brackets) with type names to be substituted.
|
||||
recvType := t2.Recv().Type
|
||||
var nm string
|
||||
if recvType.IsPtr() {
|
||||
recvType = recvType.Elem()
|
||||
nm = "(*" + recvType.Sym().Name + ")." + f.Sym.Name
|
||||
} else {
|
||||
nm = recvType.Sym().Name + "." + f.Sym.Name
|
||||
}
|
||||
if recvType.RParams()[0].HasShape() {
|
||||
// We add "nofunc" to methods of shape type to avoid
|
||||
// conflict with the name of the shape-based helper
|
||||
// function. See header comment of MakeFuncInstSym.
|
||||
nm = "nofunc." + nm
|
||||
}
|
||||
newsym := oldsym.Pkg.Lookup(nm)
|
||||
var nname *ir.Name
|
||||
if newsym.Def != nil {
|
||||
nname = newsym.Def.(*ir.Name)
|
||||
} else {
|
||||
nname = ir.NewNameAt(f.Pos, newsym)
|
||||
nname.SetType(t2)
|
||||
ir.MarkFunc(nname)
|
||||
newsym.Def = nname
|
||||
}
|
||||
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
|
||||
newfields[i].Nname = nname
|
||||
}
|
||||
newt.Methods().Set(newfields)
|
||||
if !newt.HasTParam() && !newt.HasShape() {
|
||||
// Generate all the methods for a new fully-instantiated type.
|
||||
|
||||
NeedInstType(newt)
|
||||
}
|
||||
}
|
||||
return newt
|
||||
}
|
||||
|
||||
// tstruct substitutes type params in types of the fields of a structure type. For
|
||||
// each field, tstruct copies the Nname, and translates it if Nname is in
|
||||
// ts.vars. To always force the creation of a new (top-level) struct,
|
||||
// regardless of whether anything changed with the types or names of the struct's
|
||||
// fields, set force to true.
|
||||
func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
|
||||
if t.NumFields() == 0 {
|
||||
if t.HasTParam() || t.HasShape() {
|
||||
// For an empty struct, we need to return a new type, if
|
||||
// substituting from a generic type or shape type, since it
|
||||
// will change HasTParam/HasShape flags.
|
||||
return types.NewStruct(t.Pkg(), nil)
|
||||
}
|
||||
return t
|
||||
}
|
||||
var newfields []*types.Field
|
||||
if force {
|
||||
newfields = make([]*types.Field, t.NumFields())
|
||||
}
|
||||
for i, f := range t.Fields().Slice() {
|
||||
t2 := ts.typ1(f.Type)
|
||||
if (t2 != f.Type || f.Nname != nil) && newfields == nil {
|
||||
newfields = make([]*types.Field, t.NumFields())
|
||||
for j := 0; j < i; j++ {
|
||||
newfields[j] = t.Field(j)
|
||||
}
|
||||
}
|
||||
if newfields != nil {
|
||||
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
|
||||
newfields[i].Embedded = f.Embedded
|
||||
newfields[i].Note = f.Note
|
||||
if f.IsDDD() {
|
||||
newfields[i].SetIsDDD(true)
|
||||
}
|
||||
if f.Nointerface() {
|
||||
newfields[i].SetNointerface(true)
|
||||
}
|
||||
if f.Nname != nil && ts.Vars != nil {
|
||||
n := f.Nname.(*ir.Name)
|
||||
v := ts.Vars[n]
|
||||
if v != nil {
|
||||
// This is the case where we are
|
||||
// translating the type of the function we
|
||||
// are substituting, so its dcls are in
|
||||
// the subst.ts.vars table, and we want to
|
||||
// change to reference the new dcl.
|
||||
newfields[i].Nname = v
|
||||
} else if ir.IsBlank(n) {
|
||||
// Blank variable is not dcl list. Make a
|
||||
// new one to not share.
|
||||
m := ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
|
||||
m.SetType(n.Type())
|
||||
m.SetTypecheck(1)
|
||||
newfields[i].Nname = m
|
||||
} else {
|
||||
// This is the case where we are
|
||||
// translating the type of a function
|
||||
// reference inside the function we are
|
||||
// substituting, so we leave the Nname
|
||||
// value as is.
|
||||
newfields[i].Nname = f.Nname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if newfields != nil {
|
||||
news := types.NewStruct(t.Pkg(), newfields)
|
||||
news.StructType().Funarg = t.StructType().Funarg
|
||||
return news
|
||||
}
|
||||
return t
|
||||
|
||||
}
|
||||
|
||||
// tinter substitutes type params in types of the methods of an interface type.
|
||||
func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type {
|
||||
if t.Methods().Len() == 0 {
|
||||
if t.HasTParam() || t.HasShape() {
|
||||
// For an empty interface, we need to return a new type, if
|
||||
// substituting from a generic type or shape type, since
|
||||
// since it will change HasTParam/HasShape flags.
|
||||
return types.NewInterface(t.Pkg(), nil, false)
|
||||
}
|
||||
return t
|
||||
}
|
||||
var newfields []*types.Field
|
||||
if force {
|
||||
newfields = make([]*types.Field, t.Methods().Len())
|
||||
}
|
||||
for i, f := range t.Methods().Slice() {
|
||||
t2 := ts.typ1(f.Type)
|
||||
if (t2 != f.Type || f.Nname != nil) && newfields == nil {
|
||||
newfields = make([]*types.Field, t.Methods().Len())
|
||||
for j := 0; j < i; j++ {
|
||||
newfields[j] = t.Methods().Index(j)
|
||||
}
|
||||
}
|
||||
if newfields != nil {
|
||||
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
|
||||
}
|
||||
}
|
||||
if newfields != nil {
|
||||
return types.NewInterface(t.Pkg(), newfields, t.IsImplicit())
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// genericTypeName returns the name of the base generic type for the type named by
|
||||
// sym. It simply returns the name obtained by removing everything after the
|
||||
// first bracket ("[").
|
||||
func genericTypeName(sym *types.Sym) string {
|
||||
return sym.Name[0:strings.Index(sym.Name, "[")]
|
||||
}
|
||||
|
||||
// getShapes appends the list of the shape types that are used within type t to
|
||||
// listp. The type traversal is simplified for two reasons: (1) we can always stop a
|
||||
// type traversal when t.HasShape() is false; and (2) shape types can't appear inside
|
||||
// a named type, except for the type args of a generic type. So, the traversal will
|
||||
// always stop before we have to deal with recursive types.
|
||||
func getShapes(t *types.Type, listp *[]*types.Type) {
|
||||
if !t.HasShape() {
|
||||
return
|
||||
}
|
||||
if t.IsShape() {
|
||||
*listp = append(*listp, t)
|
||||
return
|
||||
}
|
||||
|
||||
if t.Sym() != nil {
|
||||
// A named type can't have shapes in it, except for type args of a
|
||||
// generic type. We will have to deal with this differently once we
|
||||
// alloc local types in generic functions (#47631).
|
||||
for _, rparam := range t.RParams() {
|
||||
getShapes(rparam, listp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
|
||||
getShapes(t.Elem(), listp)
|
||||
|
||||
case types.TSTRUCT:
|
||||
for _, f := range t.FieldSlice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
|
||||
case types.TFUNC:
|
||||
for _, f := range t.Recvs().FieldSlice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
for _, f := range t.Params().FieldSlice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
for _, f := range t.Results().FieldSlice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
for _, f := range t.TParams().FieldSlice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
|
||||
case types.TINTER:
|
||||
for _, f := range t.Methods().Slice() {
|
||||
getShapes(f.Type, listp)
|
||||
}
|
||||
|
||||
case types.TMAP:
|
||||
getShapes(t.Key(), listp)
|
||||
getShapes(t.Elem(), listp)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Bad type in getShapes: %v", t.Kind()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
|
||||
// be used in place of the input type and still generate identical code.
|
||||
// No methods are added - all methods calls directly on a shape should
|
||||
// be done by converting to an interface using the dictionary.
|
||||
//
|
||||
// For now, we only consider two types to have the same shape, if they have exactly
|
||||
// the same underlying type or they are both pointer types.
|
||||
//
|
||||
// tparam is the associated typeparam - it must be TTYPEPARAM type. If there is a
|
||||
// structural type for the associated type param (not common), then a pointer type t
|
||||
// is mapped to its underlying type, rather than being merged with other pointers.
|
||||
//
|
||||
// Shape types are also distinguished by the index of the type in a type param/arg
|
||||
// list. We need to do this so we can distinguish and substitute properly for two
|
||||
// type params in the same function that have the same shape for a particular
|
||||
// instantiation.
|
||||
func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
|
||||
assert(!t.IsShape())
|
||||
if t.HasShape() {
|
||||
// We are sometimes dealing with types from a shape instantiation
|
||||
// that were constructed from existing shape types, so t may
|
||||
// sometimes have shape types inside it. In that case, we find all
|
||||
// those shape types with getShapes() and replace them with their
|
||||
// underlying type.
|
||||
//
|
||||
// If we don't do this, we may create extra unneeded shape types that
|
||||
// have these other shape types embedded in them. This may lead to
|
||||
// generating extra shape instantiations, and a mismatch between the
|
||||
// instantiations that we used in generating dictionaries and the
|
||||
// instantiations that are actually called. (#51303).
|
||||
list := []*types.Type{}
|
||||
getShapes(t, &list)
|
||||
list2 := make([]*types.Type, len(list))
|
||||
for i, shape := range list {
|
||||
list2[i] = shape.Underlying()
|
||||
}
|
||||
ts := Tsubster{
|
||||
Tparams: list,
|
||||
Targs: list2,
|
||||
}
|
||||
t = ts.Typ(t)
|
||||
}
|
||||
// Map all types with the same underlying type to the same shape.
|
||||
u := t.Underlying()
|
||||
|
||||
// All pointers have the same shape.
|
||||
// TODO: Make unsafe.Pointer the same shape as normal pointers.
|
||||
// Note: pointers to arrays are special because of slice-to-array-pointer
|
||||
// conversions. See issue 49295.
|
||||
if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY &&
|
||||
tparam.Bound().StructuralType() == nil && !u.Elem().NotInHeap() {
|
||||
u = types.Types[types.TUINT8].PtrTo()
|
||||
}
|
||||
|
||||
submap := shapeMap[index]
|
||||
if submap == nil {
|
||||
submap = map[*types.Type]*types.Type{}
|
||||
shapeMap[index] = submap
|
||||
}
|
||||
if s := submap[u]; s != nil {
|
||||
return s
|
||||
}
|
||||
|
||||
// LinkString specifies the type uniquely, but has no spaces.
|
||||
nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
|
||||
sym := types.ShapePkg.Lookup(nm)
|
||||
if sym.Def != nil {
|
||||
// Use any existing type with the same name
|
||||
submap[u] = sym.Def.Type()
|
||||
return submap[u]
|
||||
}
|
||||
name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
|
||||
s := types.NewNamed(name)
|
||||
sym.Def = name
|
||||
s.SetUnderlying(u)
|
||||
s.SetIsShape(true)
|
||||
s.SetHasShape(true)
|
||||
types.CalcSize(s)
|
||||
name.SetType(s)
|
||||
name.SetTypecheck(1)
|
||||
submap[u] = s
|
||||
return s
|
||||
}
|
||||
|
||||
var shapeMap = map[int]map[*types.Type]*types.Type{}
|
||||
|
|
|
|||
|
|
@ -47,16 +47,6 @@ func Callee(n ir.Node) ir.Node {
|
|||
|
||||
var importlist []*ir.Func
|
||||
|
||||
// AllImportedBodies reads in the bodies of all imported functions and typechecks
|
||||
// them, if needed.
|
||||
func AllImportedBodies() {
|
||||
for _, n := range importlist {
|
||||
if n.Inl != nil {
|
||||
ImportedBody(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var traceIndent []byte
|
||||
|
||||
func tracePrint(title string, n ir.Node) func(np *ir.Node) {
|
||||
|
|
|
|||
|
|
@ -1,191 +0,0 @@
|
|||
// Copyright 2022 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 types
|
||||
|
||||
// Implementation of structural type computation for types.
|
||||
|
||||
// TODO: we would like to depend only on the types2 computation of structural type,
|
||||
// but we can only do that the next time we change the export format and export
|
||||
// structural type info along with each constraint type, since the compiler imports
|
||||
// types directly into types1 format.
|
||||
|
||||
// A term describes elementary type sets:
|
||||
//
|
||||
// term{false, T} set of type T
|
||||
// term{true, T} set of types with underlying type t
|
||||
// term{} empty set (we specifically check for typ == nil)
|
||||
type term struct {
|
||||
tilde bool
|
||||
typ *Type
|
||||
}
|
||||
|
||||
// StructuralType returns the structural type of an interface, or nil if it has no
|
||||
// structural type.
|
||||
func (t *Type) StructuralType() *Type {
|
||||
sts, _ := specificTypes(t)
|
||||
var su *Type
|
||||
for _, st := range sts {
|
||||
u := st.typ.Underlying()
|
||||
if su != nil {
|
||||
u = match(su, u)
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// su == nil || match(su, u) != nil
|
||||
su = u
|
||||
}
|
||||
return su
|
||||
}
|
||||
|
||||
// If x and y are identical, match returns x.
|
||||
// If x and y are identical channels but for their direction
|
||||
// and one of them is unrestricted, match returns the channel
|
||||
// with the restricted direction.
|
||||
// In all other cases, match returns nil.
|
||||
// x and y are assumed to be underlying types, hence are not named types.
|
||||
func match(x, y *Type) *Type {
|
||||
if IdenticalStrict(x, y) {
|
||||
return x
|
||||
}
|
||||
|
||||
if x.IsChan() && y.IsChan() && IdenticalStrict(x.Elem(), y.Elem()) {
|
||||
// We have channels that differ in direction only.
|
||||
// If there's an unrestricted channel, select the restricted one.
|
||||
// If both have the same direction, return x (either is fine).
|
||||
switch {
|
||||
case x.ChanDir().CanSend() && x.ChanDir().CanRecv():
|
||||
return y
|
||||
case y.ChanDir().CanSend() && y.ChanDir().CanRecv():
|
||||
return x
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// specificTypes returns the list of specific types of an interface type or nil if
|
||||
// there are none. It also returns a flag that indicates, for an empty term list
|
||||
// result, whether it represents the empty set, or the infinite set of all types (in
|
||||
// both cases, there are no specific types).
|
||||
func specificTypes(t *Type) (list []term, inf bool) {
|
||||
t.wantEtype(TINTER)
|
||||
|
||||
// We have infinite term list before processing any type elements
|
||||
// (or if there are no type elements).
|
||||
inf = true
|
||||
for _, m := range t.Methods().Slice() {
|
||||
var r2 []term
|
||||
inf2 := false
|
||||
|
||||
switch {
|
||||
case m.IsMethod():
|
||||
inf2 = true
|
||||
|
||||
case m.Type.IsUnion():
|
||||
nt := m.Type.NumTerms()
|
||||
for i := 0; i < nt; i++ {
|
||||
t, tilde := m.Type.Term(i)
|
||||
if t.IsInterface() {
|
||||
r3, r3inf := specificTypes(t)
|
||||
if r3inf {
|
||||
// Union with an infinite set of types is
|
||||
// infinite, so skip remaining terms.
|
||||
r2 = nil
|
||||
inf2 = true
|
||||
break
|
||||
}
|
||||
// Add the elements of r3 to r2.
|
||||
for _, r3e := range r3 {
|
||||
r2 = insertType(r2, r3e)
|
||||
}
|
||||
} else {
|
||||
r2 = insertType(r2, term{tilde, t})
|
||||
}
|
||||
}
|
||||
|
||||
case m.Type.IsInterface():
|
||||
r2, inf2 = specificTypes(m.Type)
|
||||
|
||||
default:
|
||||
// m.Type is a single non-interface type, so r2 is just a
|
||||
// one-element list, inf2 is false.
|
||||
r2 = []term{{false, m.Type}}
|
||||
}
|
||||
|
||||
if inf2 {
|
||||
// If the current type element has infinite types,
|
||||
// its intersection with r is just r, so skip this type element.
|
||||
continue
|
||||
}
|
||||
|
||||
if inf {
|
||||
// If r is infinite, then the intersection of r and r2 is just r2.
|
||||
list = r2
|
||||
inf = false
|
||||
continue
|
||||
}
|
||||
|
||||
// r and r2 are finite, so intersect r and r2.
|
||||
var r3 []term
|
||||
for _, re := range list {
|
||||
for _, r2e := range r2 {
|
||||
if tm := intersect(re, r2e); tm.typ != nil {
|
||||
r3 = append(r3, tm)
|
||||
}
|
||||
}
|
||||
}
|
||||
list = r3
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// insertType adds t to the returned list if it is not already in list.
|
||||
func insertType(list []term, tm term) []term {
|
||||
for i, elt := range list {
|
||||
if new := union(elt, tm); new.typ != nil {
|
||||
// Replace existing elt with the union of elt and new.
|
||||
list[i] = new
|
||||
return list
|
||||
}
|
||||
}
|
||||
return append(list, tm)
|
||||
}
|
||||
|
||||
// If x and y are disjoint, return term with nil typ (which means the union should
|
||||
// include both types). If x and y are not disjoint, return the single type which is
|
||||
// the union of x and y.
|
||||
func union(x, y term) term {
|
||||
if disjoint(x, y) {
|
||||
return term{false, nil}
|
||||
}
|
||||
if x.tilde || !y.tilde {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// intersect returns the intersection x ∩ y.
|
||||
func intersect(x, y term) term {
|
||||
if disjoint(x, y) {
|
||||
return term{false, nil}
|
||||
}
|
||||
if !x.tilde || y.tilde {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// disjoint reports whether x ∩ y == ∅.
|
||||
func disjoint(x, y term) bool {
|
||||
ux := x.typ
|
||||
if y.tilde {
|
||||
ux = ux.Underlying()
|
||||
}
|
||||
uy := y.typ
|
||||
if x.tilde {
|
||||
uy = uy.Underlying()
|
||||
}
|
||||
return !IdenticalStrict(ux, uy)
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
// Copyright 2022 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.
|
||||
|
||||
// Test that StructuralType() calculates the correct value of structural type for
|
||||
// unusual cases.
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/ir"
|
||||
. "cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
typ *Type
|
||||
structuralType *Type
|
||||
}
|
||||
|
||||
func TestStructuralType(t *testing.T) {
|
||||
// These are the few constants that need to be initialized in order to use
|
||||
// the types package without using the typecheck package by calling
|
||||
// typecheck.InitUniverse() (the normal way to initialize the types package).
|
||||
PtrSize = 8
|
||||
RegSize = 8
|
||||
MaxWidth = 1 << 50
|
||||
|
||||
InitTypes(func(sym *Sym, typ *Type) Object {
|
||||
obj := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym)
|
||||
obj.SetType(typ)
|
||||
sym.Def = obj
|
||||
return obj
|
||||
})
|
||||
|
||||
// type intType = int
|
||||
intType := Types[TINT]
|
||||
// type structf = struct { f int }
|
||||
structf := NewStruct(nil, []*Field{
|
||||
NewField(src.NoXPos, LocalPkg.Lookup("f"), intType),
|
||||
})
|
||||
|
||||
defNamed := func(name string, underlying *Type) *Type {
|
||||
sym := LocalPkg.Lookup(name)
|
||||
obj := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym)
|
||||
typ := NewNamed(obj)
|
||||
typ.SetUnderlying(underlying)
|
||||
return typ
|
||||
}
|
||||
|
||||
Sf := defNamed("Sf", structf) // type Sf structf
|
||||
A := defNamed("A", intType) // type A int
|
||||
B := defNamed("B", intType) // type B int
|
||||
|
||||
any := AnyType
|
||||
|
||||
// The tests marked NONE have no structural type; all the others have a
|
||||
// structural type of structf - "struct { f int }"
|
||||
tests := []*test{
|
||||
{
|
||||
// interface { struct { f int } }
|
||||
embed(structf),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { struct { f int }; any }
|
||||
embed(structf, any),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { Sf }
|
||||
embed(Sf),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { any; Sf }
|
||||
embed(any, Sf),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { struct { f int }; Sf } - NONE
|
||||
embed(structf, Sf),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
// interface { struct { f int } | ~struct { f int } }
|
||||
embed(NewUnion([]*Type{structf, structf}, []bool{false, true})),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { ~struct { f int } ; Sf }
|
||||
embed(NewUnion([]*Type{structf}, []bool{true}), Sf),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { struct { f int } ; Sf } - NONE
|
||||
embed(NewUnion([]*Type{structf}, []bool{false}), Sf),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
// interface { Sf | A; B | Sf}
|
||||
embed(NewUnion([]*Type{Sf, A}, []bool{false, false}),
|
||||
NewUnion([]*Type{B, Sf}, []bool{false, false})),
|
||||
structf,
|
||||
},
|
||||
{
|
||||
// interface { Sf | A; A | Sf } - NONE
|
||||
embed(NewUnion([]*Type{Sf, A}, []bool{false, false}),
|
||||
NewUnion([]*Type{A, Sf}, []bool{false, false})),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
// interface { Sf | any } - NONE
|
||||
embed(NewUnion([]*Type{Sf, any}, []bool{false, false})),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
// interface { Sf | any; Sf }
|
||||
embed(NewUnion([]*Type{Sf, any}, []bool{false, false}), Sf),
|
||||
structf,
|
||||
},
|
||||
}
|
||||
for i, tst := range tests {
|
||||
if got, want := tst.typ.StructuralType(), tst.structuralType; got != want {
|
||||
t.Errorf("#%v: StructuralType(%v) = %v, wanted %v",
|
||||
i, tst.typ, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func embed(types ...*Type) *Type {
|
||||
fields := make([]*Field, len(types))
|
||||
for i, t := range types {
|
||||
fields[i] = NewField(src.NoXPos, nil, t)
|
||||
}
|
||||
return NewInterface(LocalPkg, fields, false)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"cmd/compile/internal/base"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
|
@ -288,19 +287,6 @@ func (t *Type) SetRParams(rparams []*Type) {
|
|||
}
|
||||
}
|
||||
|
||||
// IsBaseGeneric returns true if t is a generic type (not reinstantiated with
|
||||
// another type params or fully instantiated.
|
||||
func (t *Type) IsBaseGeneric() bool {
|
||||
return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") < 0
|
||||
}
|
||||
|
||||
// IsInstantiatedGeneric returns t if t ia generic type that has been
|
||||
// reinstantiated with new typeparams (i.e. is not fully instantiated).
|
||||
func (t *Type) IsInstantiatedGeneric() bool {
|
||||
return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") >= 0 &&
|
||||
t.HasTParam()
|
||||
}
|
||||
|
||||
// IsFullyInstantiated reports whether t is a fully instantiated generic type; i.e. an
|
||||
// instantiated generic type where all type arguments are non-generic or fully
|
||||
// instantiated generic types.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue