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:
Matthew Dempsky 2022-12-01 16:27:34 -08:00
parent a7de684e1b
commit da9761303c
23 changed files with 0 additions and 11575 deletions

View file

@ -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 {

View file

@ -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")
}
}
}

View file

@ -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.

View file

@ -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
}
}

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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],

View file

@ -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
}

View file

@ -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()

View file

@ -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
}

View file

@ -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()
}

View file

@ -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

View file

@ -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{}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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.