"go/constant"
"sort"
"strconv"
- "strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
- "cmd/internal/src"
)
// Inlining budget parameters, gathered in one place
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
}
}
-// 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 {
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 {
}
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")
- }
- }
-}
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:
}
}
-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.
+++ /dev/null
-// 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
- }
-}
"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"
)
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
+++ /dev/null
-// 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)
- }
- }
-}
+++ /dev/null
-// 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
-}
import (
"cmd/compile/internal/base"
- "cmd/compile/internal/ir"
- "cmd/compile/internal/objw"
- "cmd/compile/internal/reflectdata"
- "cmd/compile/internal/typecheck"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
- "fmt"
- "go/constant"
)
-// Enable extra consistency checks.
-const doubleCheck = false
-
func assert(p bool) {
base.Assert(p)
}
-
-// For outputting debug information on dictionary format and instantiated dictionaries
-// (type arg, derived types, sub-dictionary, and itab entries).
-var infoPrintMode = false
-
-func infoPrint(format string, a ...interface{}) {
- if infoPrintMode {
- fmt.Printf(format, a...)
- }
-}
-
-var geninst genInst
-
-func BuildInstantiations() {
- geninst.instInfoMap = make(map[*types.Sym]*instInfo)
- geninst.buildInstantiations()
- geninst.instInfoMap = nil
-}
-
-// buildInstantiations scans functions for generic function calls and methods, and
-// creates the required instantiations. It also creates instantiated methods for all
-// fully-instantiated generic types that have been encountered already or new ones
-// that are encountered during the instantiation process. It scans all declarations
-// in typecheck.Target.Decls first, before scanning any new instantiations created.
-func (g *genInst) buildInstantiations() {
- // Instantiate the methods of instantiated generic types that we have seen so far.
- g.instantiateMethods()
-
- // Scan all currentdecls for call to generic functions/methods.
- n := len(typecheck.Target.Decls)
- for i := 0; i < n; i++ {
- g.scanForGenCalls(typecheck.Target.Decls[i])
- }
-
- // Scan all new instantiations created due to g.instantiateMethods() and the
- // scan of current decls. This loop purposely runs until no new
- // instantiations are created.
- for i := 0; i < len(g.newInsts); i++ {
- g.scanForGenCalls(g.newInsts[i])
- }
-
- g.finalizeSyms()
-
- // All the instantiations and dictionaries have been created. Now go through
- // each new instantiation and transform the various operations that need to make
- // use of their dictionary.
- l := len(g.newInsts)
- for _, fun := range g.newInsts {
- info := g.instInfoMap[fun.Sym()]
- g.dictPass(info)
- if doubleCheck {
- ir.Visit(info.fun, func(n ir.Node) {
- if n.Op() != ir.OCONVIFACE {
- return
- }
- c := n.(*ir.ConvExpr)
- if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
- ir.Dump("BAD FUNCTION", info.fun)
- ir.Dump("BAD CONVERSION", c)
- base.Fatalf("converting shape type to interface")
- }
- })
- }
- if base.Flag.W > 1 {
- ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
- }
- }
- assert(l == len(g.newInsts))
- g.newInsts = nil
-}
-
-// scanForGenCalls scans a single function (or global assignment), looking for
-// references to generic functions/methods. At each such reference, it creates any
-// required instantiation and transforms the reference.
-func (g *genInst) scanForGenCalls(decl ir.Node) {
- switch decl.Op() {
- case ir.ODCLFUNC:
- if decl.Type().HasTParam() {
- // Skip any generic functions
- return
- }
- // transformCall() below depends on CurFunc being set.
- ir.CurFunc = decl.(*ir.Func)
-
- case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
- // These are all the various kinds of global assignments,
- // whose right-hand-sides might contain a function
- // instantiation.
-
- default:
- // The other possible ops at the top level are ODCLCONST
- // and ODCLTYPE, which don't have any function
- // instantiations.
- return
- }
-
- // Search for any function references using generic function/methods. Then
- // create the needed instantiated function if it hasn't been created yet, and
- // change to calling that function directly.
- modified := false
- closureRequired := false
- // declInfo will be non-nil exactly if we are scanning an instantiated function
- declInfo := g.instInfoMap[decl.Sym()]
-
- ir.Visit(decl, func(n ir.Node) {
- if n.Op() == ir.OFUNCINST {
- // generic F, not immediately called
- closureRequired = true
- }
- if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
- // T.M or x.M, where T or x is generic, but not immediately
- // called. Not necessary if the method selected is
- // actually for an embedded interface field.
- closureRequired = true
- }
- if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
- // We have found a function call using a generic function
- // instantiation.
- call := n.(*ir.CallExpr)
- inst := call.X.(*ir.InstExpr)
- nameNode, isMeth := g.getInstNameNode(inst)
- targs := typecheck.TypesOf(inst.Targs)
- st := g.getInstantiation(nameNode, targs, isMeth).fun
- dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
- if infoPrintMode {
- dictkind := "Main dictionary"
- if usingSubdict {
- dictkind = "Sub-dictionary"
- }
- if inst.X.Op() == ir.OMETHVALUE {
- fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
- } else {
- fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
- }
- }
-
- // Transform the Call now, which changes OCALL to
- // OCALLFUNC and does typecheckaste/assignconvfn. Do
- // it before installing the instantiation, so we are
- // checking against non-shape param types in
- // typecheckaste.
- transformCall(call)
-
- // Replace the OFUNCINST with a direct reference to the
- // new stenciled function
- call.X = st.Nname
- if inst.X.Op() == ir.OMETHVALUE {
- // When we create an instantiation of a method
- // call, we make it a function. So, move the
- // receiver to be the first arg of the function
- // call.
- call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
- }
-
- // Add dictionary to argument list.
- call.Args.Prepend(dictValue)
- modified = true
- }
- if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
- // Method call on a generic type, which was instantiated by stenciling.
- // Method calls on explicitly instantiated types will have an OFUNCINST
- // and are handled above.
- call := n.(*ir.CallExpr)
- meth := call.X.(*ir.SelectorExpr)
- targs := deref(meth.Type().Recv().Type).RParams()
-
- t := meth.X.Type()
- baseType := deref(t).OrigType()
- var gf *ir.Name
- for _, m := range baseType.Methods().Slice() {
- if meth.Sel == m.Sym {
- gf = m.Nname.(*ir.Name)
- break
- }
- }
-
- // Transform the Call now, which changes OCALL
- // to OCALLFUNC and does typecheckaste/assignconvfn.
- transformCall(call)
-
- st := g.getInstantiation(gf, targs, true).fun
- dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
- if hasShapeTypes(targs) {
- // We have to be using a subdictionary, since this is
- // a generic method call.
- assert(usingSubdict)
- } else {
- // We should use main dictionary, because the receiver is
- // an instantiation already, see issue #53406.
- assert(!usingSubdict)
- }
-
- // Transform to a function call, by appending the
- // dictionary and the receiver to the args.
- call.SetOp(ir.OCALLFUNC)
- call.X = st.Nname
- call.Args.Prepend(dictValue, meth.X)
- modified = true
- }
- })
-
- // If we found a reference to a generic instantiation that wasn't an
- // immediate call, then traverse the nodes of decl again (with
- // EditChildren rather than Visit), where we actually change the
- // reference to the instantiation to a closure that captures the
- // dictionary, then does a direct call.
- // EditChildren is more expensive than Visit, so we only do this
- // in the infrequent case of an OFUNCINST without a corresponding
- // call.
- if closureRequired {
- modified = true
- var edit func(ir.Node) ir.Node
- var outer *ir.Func
- if f, ok := decl.(*ir.Func); ok {
- outer = f
- }
- edit = func(x ir.Node) ir.Node {
- if x.Op() == ir.OFUNCINST {
- child := x.(*ir.InstExpr).X
- if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
- // Call EditChildren on child (x.X),
- // not x, so that we don't do
- // buildClosure() on the
- // METHEXPR/METHVALUE nodes as well.
- ir.EditChildren(child, edit)
- return g.buildClosure(outer, x)
- }
- }
- ir.EditChildren(x, edit)
- switch {
- case x.Op() == ir.OFUNCINST:
- return g.buildClosure(outer, x)
- case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
- len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
- !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
- return g.buildClosure(outer, x)
- }
- return x
- }
- edit(decl)
- }
- if base.Flag.W > 1 && modified {
- ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
- }
- ir.CurFunc = nil
- // We may have seen new fully-instantiated generic types while
- // instantiating any needed functions/methods in the above
- // function. If so, instantiate all the methods of those types
- // (which will then lead to more function/methods to scan in the loop).
- g.instantiateMethods()
-}
-
-// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR/OMETHVALUE
-// of generic type. outer is the containing function (or nil if closure is
-// in a global assignment instead of a function).
-func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
- pos := x.Pos()
- var target *ir.Func // target instantiated function/method
- var dictValue ir.Node // dictionary to use
- var rcvrValue ir.Node // receiver, if a method value
- typ := x.Type() // type of the closure
- var outerInfo *instInfo
- if outer != nil {
- outerInfo = g.instInfoMap[outer.Sym()]
- }
- usingSubdict := false
- valueMethod := false
- if x.Op() == ir.OFUNCINST {
- inst := x.(*ir.InstExpr)
-
- // Type arguments we're instantiating with.
- targs := typecheck.TypesOf(inst.Targs)
-
- // Find the generic function/method.
- var gf *ir.Name
- if inst.X.Op() == ir.ONAME {
- // Instantiating a generic function call.
- gf = inst.X.(*ir.Name)
- } else if inst.X.Op() == ir.OMETHVALUE {
- // Instantiating a method value x.M.
- se := inst.X.(*ir.SelectorExpr)
- rcvrValue = se.X
- gf = se.Selection.Nname.(*ir.Name)
- } else {
- panic("unhandled")
- }
-
- // target is the instantiated function we're trying to call.
- // For functions, the target expects a dictionary as its first argument.
- // For method values, the target expects a dictionary and the receiver
- // as its first two arguments.
- // dictValue is the value to use for the dictionary argument.
- target = g.getInstantiation(gf, targs, rcvrValue != nil).fun
- dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
- if infoPrintMode {
- dictkind := "Main dictionary"
- if usingSubdict {
- dictkind = "Sub-dictionary"
- }
- if rcvrValue == nil {
- fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
- } else {
- fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
- }
- }
- } else { // ir.OMETHEXPR or ir.METHVALUE
- // Method expression T.M where T is a generic type.
- se := x.(*ir.SelectorExpr)
- if x.Op() == ir.OMETHVALUE {
- rcvrValue = se.X
- }
-
- // se.X.Type() is the top-level type of the method expression. To
- // correctly handle method expressions involving embedded fields,
- // look up the generic method below using the type of the receiver
- // of se.Selection, since that will be the type that actually has
- // the method.
- recv := deref(se.Selection.Type.Recv().Type)
- targs := recv.RParams()
- if len(targs) == 0 {
- // The embedded type that actually has the method is not
- // actually generic, so no need to build a closure.
- return x
- }
- baseType := recv.OrigType()
- var gf *ir.Name
- for _, m := range baseType.Methods().Slice() {
- if se.Sel == m.Sym {
- gf = m.Nname.(*ir.Name)
- break
- }
- }
- if !gf.Type().Recv().Type.IsPtr() {
- // Remember if value method, so we can detect (*T).M case.
- valueMethod = true
- }
- target = g.getInstantiation(gf, targs, true).fun
- dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
- if infoPrintMode {
- dictkind := "Main dictionary"
- if usingSubdict {
- dictkind = "Sub-dictionary"
- }
- fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
- }
- }
-
- // Build a closure to implement a function instantiation.
- //
- // func f[T any] (int, int) (int, int) { ...whatever... }
- //
- // Then any reference to f[int] not directly called gets rewritten to
- //
- // .dictN := ... dictionary to use ...
- // func(a0, a1 int) (r0, r1 int) {
- // return .inst.f[int](.dictN, a0, a1)
- // }
- //
- // Similarly for method expressions,
- //
- // type g[T any] ....
- // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... }
- //
- // Any reference to g[int].f not directly called gets rewritten to
- //
- // .dictN := ... dictionary to use ...
- // func(rcvr g[int], a0, a1 int) (r0, r1 int) {
- // return .inst.g[int].f(.dictN, rcvr, a0, a1)
- // }
- //
- // Also method values
- //
- // var x g[int]
- //
- // Any reference to x.f not directly called gets rewritten to
- //
- // .dictN := ... dictionary to use ...
- // x2 := x
- // func(a0, a1 int) (r0, r1 int) {
- // return .inst.g[int].f(.dictN, x2, a0, a1)
- // }
-
- // Make a new internal function.
- fn, formalParams, formalResults := startClosure(pos, outer, typ)
- fn.SetWrapper(true) // See issue 52237
-
- // This is the dictionary we want to use.
- // It may be a constant, it may be the outer functions's dictionary, or it may be
- // a subdictionary acquired from the outer function's dictionary.
- // For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary
- // read from the outer function's dictionary.
- var dictVar *ir.Name
- var dictAssign *ir.AssignStmt
- if outer != nil {
- dictVar = ir.NewNameAt(pos, closureSym(outer, typecheck.LocalDictName, g.dnum))
- g.dnum++
- dictVar.Class = ir.PAUTO
- typed(types.Types[types.TUINTPTR], dictVar)
- dictVar.Curfn = outer
- dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue)
- dictAssign.SetTypecheck(1)
- dictVar.Defn = dictAssign
- outer.Dcl = append(outer.Dcl, dictVar)
- }
- // assign the receiver to a temporary.
- var rcvrVar *ir.Name
- var rcvrAssign ir.Node
- if rcvrValue != nil {
- rcvrVar = ir.NewNameAt(pos, closureSym(outer, ".rcvr", g.dnum))
- g.dnum++
- typed(rcvrValue.Type(), rcvrVar)
- rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
- rcvrAssign.SetTypecheck(1)
- rcvrVar.Defn = rcvrAssign
- if outer == nil {
- rcvrVar.Class = ir.PEXTERN
- typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
- typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
- } else {
- rcvrVar.Class = ir.PAUTO
- rcvrVar.Curfn = outer
- outer.Dcl = append(outer.Dcl, rcvrVar)
- }
- }
-
- // Build body of closure. This involves just calling the wrapped function directly
- // with the additional dictionary argument.
-
- // First, figure out the dictionary argument.
- var dict2Var ir.Node
- if usingSubdict {
- // Capture sub-dictionary calculated in the outer function
- dict2Var = ir.CaptureName(pos, fn, dictVar)
- typed(types.Types[types.TUINTPTR], dict2Var)
- } else {
- // Static dictionary, so can be used directly in the closure
- dict2Var = dictValue
- }
- // Also capture the receiver variable.
- var rcvr2Var *ir.Name
- if rcvrValue != nil {
- rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
- }
-
- // Build arguments to call inside the closure.
- var args []ir.Node
-
- // First the dictionary argument.
- args = append(args, dict2Var)
- // Then the receiver.
- if rcvrValue != nil {
- args = append(args, rcvr2Var)
- }
- // Then all the other arguments (including receiver for method expressions).
- for i := 0; i < typ.NumParams(); i++ {
- if x.Op() == ir.OMETHEXPR && i == 0 {
- // If we are doing a method expression, we need to
- // explicitly traverse any embedded fields in the receiver
- // argument in order to call the method instantiation.
- arg0 := formalParams[0].Nname.(ir.Node)
- arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
- if valueMethod && arg0.Type().IsPtr() {
- // For handling the (*T).M case: if we have a pointer
- // receiver after following all the embedded fields,
- // but it's a value method, add a star operator.
- arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
- }
- args = append(args, arg0)
- } else {
- args = append(args, formalParams[i].Nname.(*ir.Name))
- }
- }
-
- // Build call itself.
- var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
- innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic()
- if len(formalResults) > 0 {
- innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
- }
- // Finish building body of closure.
- ir.CurFunc = fn
- // TODO: set types directly here instead of using typecheck.Stmt
- typecheck.Stmt(innerCall)
- ir.CurFunc = nil
- fn.Body = []ir.Node{innerCall}
-
- // We're all done with the captured dictionary (and receiver, for method values).
- ir.FinishCaptureNames(pos, outer, fn)
-
- // Make a closure referencing our new internal function.
- c := ir.UseClosure(fn.OClosure, typecheck.Target)
- var init []ir.Node
- if outer != nil {
- init = append(init, dictAssign)
- }
- if rcvrValue != nil {
- init = append(init, rcvrAssign)
- }
- return ir.InitExpr(init, c)
-}
-
-// instantiateMethods instantiates all the methods (and associated dictionaries) of
-// all fully-instantiated generic types that have been added to typecheck.instTypeList.
-// It continues until no more types are added to typecheck.instTypeList.
-func (g *genInst) instantiateMethods() {
- for {
- instTypeList := typecheck.GetInstTypeList()
- if len(instTypeList) == 0 {
- break
- }
- typecheck.ClearInstTypeList()
- for _, typ := range instTypeList {
- assert(!typ.HasShape())
- // Mark runtime type as needed, since this ensures that the
- // compiler puts out the needed DWARF symbols, when this
- // instantiated type has a different package from the local
- // package.
- typecheck.NeedRuntimeType(typ)
- // Lookup the method on the base generic type, since methods may
- // not be set on imported instantiated types.
- baseType := typ.OrigType()
- for j := range typ.Methods().Slice() {
- if baseType.Methods().Slice()[j].Nointerface() {
- typ.Methods().Slice()[j].SetNointerface(true)
- }
- baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
- // Eagerly generate the instantiations and dictionaries that implement these methods.
- // We don't use the instantiations here, just generate them (and any
- // further instantiations those generate, etc.).
- // Note that we don't set the Func for any methods on instantiated
- // types. Their signatures don't match so that would be confusing.
- // Direct method calls go directly to the instantiations, implemented above.
- // Indirect method calls use wrappers generated in reflectcall. Those wrappers
- // will use these instantiations if they are needed (for interface tables or reflection).
- _ = g.getInstantiation(baseNname, typ.RParams(), true)
- _ = g.getDictionarySym(baseNname, typ.RParams(), true)
- }
- }
- }
-}
-
-// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated.
-func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
- if meth, ok := inst.X.(*ir.SelectorExpr); ok {
- return meth.Selection.Nname.(*ir.Name), true
- } else {
- return inst.X.(*ir.Name), false
- }
-}
-
-// getDictOrSubdict returns, for a method/function call or reference (node n) in an
-// instantiation (described by instInfo), a node which is accessing a sub-dictionary
-// or main/static dictionary, as needed, and also returns a boolean indicating if a
-// sub-dictionary was accessed. nameNode is the particular function or method being
-// called/referenced, and targs are the type arguments.
-func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
- var dict ir.Node
- usingSubdict := false
- if declInfo != nil {
- entry := -1
- for i, de := range declInfo.dictInfo.subDictCalls {
- if n == de.callNode {
- entry = declInfo.dictInfo.startSubDict + i
- break
- }
- }
- // If the entry is not found, it may be that this node did not have
- // any type args that depend on type params, so we need a main
- // dictionary, not a sub-dictionary.
- if entry >= 0 {
- dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen)
- usingSubdict = true
- }
- }
- if !usingSubdict {
- dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth)
- }
- return dict, usingSubdict
-}
-
-// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded
-// yet. If so, it imports the body.
-func checkFetchBody(nameNode *ir.Name) {
- if nameNode.Func.Body == nil && nameNode.Func.Inl != nil {
- // If there is no body yet but Func.Inl exists, then we can
- // import the whole generic body.
- assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
- typecheck.ImportBody(nameNode.Func)
- assert(nameNode.Func.Inl.Body != nil)
- nameNode.Func.Body = nameNode.Func.Inl.Body
- nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
- }
-}
-
-// getInstantiation gets the instantiation and dictionary of the function or method nameNode
-// with the type arguments shapes. If the instantiated function is not already
-// cached, then it calls genericSubst to create the new instantiation.
-func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
- if nameNode.Func == nil {
- // If nameNode.Func is nil, this must be a reference to a method of
- // an imported instantiated type. We will have already called
- // g.instantiateMethods() on the fully-instantiated type, so
- // g.instInfoMap[sym] will be non-nil below.
- rcvr := nameNode.Type().Recv()
- if rcvr == nil || !deref(rcvr.Type).IsFullyInstantiated() {
- base.FatalfAt(nameNode.Pos(), "Unexpected function instantiation %v with no body", nameNode)
- }
- } else {
- checkFetchBody(nameNode)
- }
-
- var tparams []*types.Type
- if isMeth {
- // Get the type params from the method receiver (after skipping
- // over any pointer)
- recvType := nameNode.Type().Recv().Type
- recvType = deref(recvType)
- if recvType.IsFullyInstantiated() {
- // Get the type of the base generic type, so we get
- // its original typeparams.
- recvType = recvType.OrigType()
- }
- tparams = recvType.RParams()
- } else {
- fields := nameNode.Type().TParams().Fields().Slice()
- tparams = make([]*types.Type, len(fields))
- for i, f := range fields {
- tparams[i] = f.Type
- }
- }
-
- // Convert any non-shape type arguments to their shape, so we can reduce the
- // number of instantiations we have to generate. You can actually have a mix
- // of shape and non-shape arguments, because of inferred or explicitly
- // specified concrete type args.
- s1 := make([]*types.Type, len(shapes))
- for i, t := range shapes {
- var tparam *types.Type
- // Shapes are grouped differently for structural types, so we
- // pass the type param to Shapify(), so we can distinguish.
- tparam = tparams[i]
- if !t.IsShape() {
- s1[i] = typecheck.Shapify(t, i, tparam)
- } else {
- // Already a shape, but make sure it has the correct index.
- s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam)
- }
- }
- shapes = s1
-
- sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, false, isMeth)
- info := g.instInfoMap[sym]
- if info == nil {
- // If instantiation doesn't exist yet, create it and add
- // to the list of decls.
- info = &instInfo{
- dictInfo: &dictInfo{},
- }
- info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
-
- if sym.Def != nil {
- // This instantiation must have been imported from another
- // package (because it was needed for inlining), so we should
- // not re-generate it and have conflicting definitions for the
- // symbol (issue #50121). It will have already gone through the
- // dictionary transformations of dictPass, so we don't actually
- // need the info.dictParam and info.shapeToBound info filled in
- // below. We just set the imported instantiation as info.fun.
- assert(sym.Pkg != types.LocalPkg)
- info.fun = sym.Def.(*ir.Name).Func
- assert(info.fun != nil)
- g.instInfoMap[sym] = info
- return info
- }
-
- // genericSubst fills in info.dictParam and info.shapeToBound.
- st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info)
- info.fun = st
- g.instInfoMap[sym] = info
-
- // getInstInfo fills in info.dictInfo.
- g.getInstInfo(st, shapes, info)
- if base.Flag.W > 1 {
- ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
- }
-
- // This ensures that the linker drops duplicates of this instantiation.
- // All just works!
- st.SetDupok(true)
- typecheck.Target.Decls = append(typecheck.Target.Decls, st)
- g.newInsts = append(g.newInsts, st)
- }
- return info
-}
-
-// Struct containing info needed for doing the substitution as we create the
-// instantiation of a generic function with specified type arguments.
-type subster struct {
- g *genInst
- isMethod bool // If a method is being instantiated
- newf *ir.Func // Func node for the new stenciled function
- ts typecheck.Tsubster
- info *instInfo // Place to put extra info in the instantiation
- skipClosure bool // Skip substituting closures
-
- // Map from non-nil, non-ONAME node n to slice of all m, where m.Defn = n
- defnMap map[ir.Node][]**ir.Name
-}
-
-// genericSubst returns a new function with name newsym. The function is an
-// instantiation of a generic function or method specified by namedNode with type
-// args shapes. For a method with a generic receiver, it returns an instantiated
-// function type where the receiver becomes the first parameter. For either a generic
-// method or function, a dictionary parameter is the added as the very first
-// parameter. genericSubst fills in info.dictParam and info.shapeToBound.
-func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
- gf := nameNode.Func
- // Pos of the instantiated function is same as the generic function
- newf := ir.NewFunc(gf.Pos())
- newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation.
- newf.Endlineno = gf.Endlineno
- newf.Nname = ir.NewNameAt(gf.Pos(), newsym)
- newf.Nname.Func = newf
- newf.Nname.Defn = newf
- newsym.Def = newf.Nname
- savef := ir.CurFunc
- // transformCall/transformReturn (called during stenciling of the body)
- // depend on ir.CurFunc being set.
- ir.CurFunc = newf
-
- assert(len(tparams) == len(shapes))
-
- subst := &subster{
- g: g,
- isMethod: isMethod,
- newf: newf,
- info: info,
- ts: typecheck.Tsubster{
- Tparams: tparams,
- Targs: shapes,
- Vars: make(map[*ir.Name]*ir.Name),
- },
- defnMap: make(map[ir.Node][]**ir.Name),
- }
-
- newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
-
- // Create the needed dictionary param
- dictionarySym := newsym.Pkg.Lookup(typecheck.LocalDictName)
- dictionaryType := types.Types[types.TUINTPTR]
- dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
- typed(dictionaryType, dictionaryName)
- dictionaryName.Class = ir.PPARAM
- dictionaryName.Curfn = newf
- newf.Dcl = append(newf.Dcl, dictionaryName)
- for _, n := range gf.Dcl {
- if n.Sym().Name == typecheck.LocalDictName {
- panic("already has dictionary")
- }
- newf.Dcl = append(newf.Dcl, subst.localvar(n))
- }
- dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
- dictionaryArg.Nname = dictionaryName
- info.dictParam = dictionaryName
-
- // We add the dictionary as the first parameter in the function signature.
- // We also transform a method type to the corresponding function type
- // (make the receiver be the next parameter after the dictionary).
- oldt := nameNode.Type()
- var args []*types.Field
- args = append(args, dictionaryArg)
- args = append(args, oldt.Recvs().FieldSlice()...)
- args = append(args, oldt.Params().FieldSlice()...)
-
- // Replace the types in the function signature via subst.fields.
- // Ugly: also, we have to insert the Name nodes of the parameters/results into
- // the function type. The current function type has no Nname fields set,
- // because it came via conversion from the types2 type.
- newt := types.NewSignature(oldt.Pkg(), nil, nil,
- subst.fields(ir.PPARAM, args, newf.Dcl),
- subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
-
- typed(newt, newf.Nname)
- ir.MarkFunc(newf.Nname)
- newf.SetTypecheck(1)
-
- // Make sure name/type of newf is set before substituting the body.
- newf.Body = subst.list(gf.Body)
- if len(newf.Body) == 0 {
- // Ensure the body is nonempty, for issue 49524.
- // TODO: have some other way to detect the difference between
- // a function declared with no body, vs. one with an empty body?
- newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil))
- }
-
- if len(subst.defnMap) > 0 {
- base.Fatalf("defnMap is not empty")
- }
-
- for i, tp := range tparams {
- info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound())
- }
-
- ir.CurFunc = savef
-
- return subst.newf
-}
-
-// localvar creates a new name node for the specified local variable and enters it
-// in subst.vars. It substitutes type arguments for type parameters in the type of
-// name as needed.
-func (subst *subster) localvar(name *ir.Name) *ir.Name {
- m := ir.NewNameAt(name.Pos(), name.Sym())
- if name.IsClosureVar() {
- m.SetIsClosureVar(true)
- }
- m.SetType(subst.ts.Typ(name.Type()))
- m.BuiltinOp = name.BuiltinOp
- m.Curfn = subst.newf
- m.Class = name.Class
- assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
- m.Func = name.Func
- subst.ts.Vars[name] = m
- m.SetTypecheck(1)
- m.DictIndex = name.DictIndex
- if name.Defn != nil {
- if name.Defn.Op() == ir.ONAME {
- // This is a closure variable, so its Defn is the outer
- // captured variable, which has already been substituted.
- m.Defn = subst.node(name.Defn)
- } else {
- // The other values of Defn are nodes in the body of the
- // function, so just remember the mapping so we can set Defn
- // properly in node() when we create the new body node. We
- // always call localvar() on all the local variables before
- // we substitute the body.
- slice := subst.defnMap[name.Defn]
- subst.defnMap[name.Defn] = append(slice, &m)
- }
- }
- if name.Outer != nil {
- m.Outer = subst.node(name.Outer).(*ir.Name)
- }
-
- return m
-}
-
-// getDictionaryEntry gets the i'th entry in the dictionary dict.
-func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
- // Convert dictionary to *[N]uintptr
- // All entries in the dictionary are pointers. They all point to static data, though, so we
- // treat them as uintptrs so the GC doesn't need to keep track of them.
- d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict)
- d.SetTypecheck(1)
- d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d)
- d.SetTypecheck(1)
- types.CheckSize(d.Type().Elem())
-
- // Load entry i out of the dictionary.
- deref := ir.NewStarExpr(pos, d)
- typed(d.Type().Elem(), deref)
- idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to?
- typed(types.Types[types.TUINTPTR], idx)
- r := ir.NewIndexExpr(pos, deref, idx)
- typed(types.Types[types.TUINTPTR], r)
- return r
-}
-
-// getDictionaryEntryAddr gets the address of the i'th entry in dictionary dict.
-func getDictionaryEntryAddr(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
- a := ir.NewAddrExpr(pos, getDictionaryEntry(pos, dict, i, size))
- typed(types.Types[types.TUINTPTR].PtrTo(), a)
- return a
-}
-
-// getDictionaryType returns a *runtime._type from the dictionary entry i (which
-// refers to a type param or a derived type that uses type params). It uses the
-// specified dictionary dictParam, rather than the one in info.dictParam.
-func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node {
- if i < 0 || i >= info.dictInfo.startSubDict {
- base.Fatalf(fmt.Sprintf("bad dict index %d", i))
- }
-
- r := getDictionaryEntry(pos, dictParam, i, info.dictInfo.startSubDict)
- // change type of retrieved dictionary entry to *byte, which is the
- // standard typing of a *runtime._type in the compiler
- typed(types.Types[types.TUINT8].PtrTo(), r)
- return r
-}
-
-// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and
-// also descends into closures. It substitutes type arguments for type parameters in
-// all the new nodes and does the transformations that were delayed on the generic
-// function.
-func (subst *subster) node(n ir.Node) ir.Node {
- // Use closure to capture all state needed by the ir.EditChildren argument.
- var edit func(ir.Node) ir.Node
- edit = func(x ir.Node) ir.Node {
- // Analogous to ir.SetPos() at beginning of typecheck.typecheck() -
- // allows using base.Pos during the transform functions, just like
- // the tc*() functions.
- ir.SetPos(x)
- switch x.Op() {
- case ir.OTYPE:
- return ir.TypeNode(subst.ts.Typ(x.Type()))
-
- case ir.ONAME:
- if v := subst.ts.Vars[x.(*ir.Name)]; v != nil {
- return v
- }
- if ir.IsBlank(x) {
- // Special case, because a blank local variable is
- // not in the fn.Dcl list.
- m := ir.NewNameAt(x.Pos(), ir.BlankNode.Sym())
- return typed(subst.ts.Typ(x.Type()), m)
- }
- return x
- case ir.ONONAME:
- // This handles the identifier in a type switch guard
- fallthrough
- case ir.OLITERAL, ir.ONIL:
- if x.Sym() != nil {
- return x
- }
- }
- m := ir.Copy(x)
-
- slice, ok := subst.defnMap[x]
- if ok {
- // We just copied a non-ONAME node which was the Defn value
- // of a local variable. Set the Defn value of the copied
- // local variable to this new Defn node.
- for _, ptr := range slice {
- (*ptr).Defn = m
- }
- delete(subst.defnMap, x)
- }
-
- if _, isExpr := m.(ir.Expr); isExpr {
- t := x.Type()
- if t == nil {
- // Check for known cases where t can be nil (call
- // that has no return values, and key expressions)
- // and otherwise cause a fatal error.
- _, isCallExpr := m.(*ir.CallExpr)
- _, isStructKeyExpr := m.(*ir.StructKeyExpr)
- _, isKeyExpr := m.(*ir.KeyExpr)
- if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
- x.Op() != ir.OCLOSE {
- base.FatalfAt(m.Pos(), "Nil type for %v", x)
- }
- } else if x.Op() != ir.OCLOSURE {
- m.SetType(subst.ts.Typ(x.Type()))
- }
- }
-
- old := subst.skipClosure
- // For unsafe.{Alignof,Offsetof,Sizeof}, subster will transform them to OLITERAL nodes,
- // and discard their arguments. However, their children nodes were already process before,
- // thus if they contain any closure, the closure was still be added to package declarations
- // queue for processing later. Thus, genInst will fail to generate instantiation for the
- // closure because of lacking dictionary information, see issue #53390.
- if call, ok := m.(*ir.CallExpr); ok && call.X.Op() == ir.ONAME {
- switch call.X.Name().BuiltinOp {
- case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
- subst.skipClosure = true
- }
- }
- ir.EditChildren(m, edit)
- subst.skipClosure = old
-
- m.SetTypecheck(1)
-
- // Do the transformations that we delayed on the generic function
- // node, now that we have substituted in the type args.
- switch x.Op() {
- case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
- transformCompare(m.(*ir.BinaryExpr))
-
- case ir.OSLICE, ir.OSLICE3:
- transformSlice(m.(*ir.SliceExpr))
-
- case ir.OADD:
- m = transformAdd(m.(*ir.BinaryExpr))
-
- case ir.OINDEX:
- transformIndex(m.(*ir.IndexExpr))
-
- case ir.OAS2:
- as2 := m.(*ir.AssignListStmt)
- transformAssign(as2, as2.Lhs, as2.Rhs)
-
- case ir.OAS:
- as := m.(*ir.AssignStmt)
- if as.Y != nil {
- // transformAssign doesn't handle the case
- // of zeroing assignment of a dcl (rhs[0] is nil).
- lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
- transformAssign(as, lhs, rhs)
- as.X, as.Y = lhs[0], rhs[0]
- }
-
- case ir.OASOP:
- as := m.(*ir.AssignOpStmt)
- transformCheckAssign(as, as.X)
-
- case ir.ORETURN:
- transformReturn(m.(*ir.ReturnStmt))
-
- case ir.OSEND:
- transformSend(m.(*ir.SendStmt))
-
- case ir.OSELECT:
- transformSelect(m.(*ir.SelectStmt))
-
- case ir.OCOMPLIT:
- transformCompLit(m.(*ir.CompLitExpr))
-
- case ir.OADDR:
- transformAddr(m.(*ir.AddrExpr))
-
- case ir.OLITERAL:
- t := m.Type()
- if t != x.Type() {
- // types2 will give us a constant with a type T,
- // if an untyped constant is used with another
- // operand of type T (in a provably correct way).
- // When we substitute in the type args during
- // stenciling, we now know the real type of the
- // constant. We may then need to change the
- // BasicLit.val to be the correct type (e.g.
- // convert an int64Val constant to a floatVal
- // constant).
- m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work
- m = typecheck.DefaultLit(m, t)
- }
-
- case ir.OXDOT:
- // Finish the transformation of an OXDOT, unless this is
- // bound call or field access on a type param. A bound call
- // or field access on a type param will be transformed during
- // the dictPass. Otherwise, m will be transformed to an
- // OMETHVALUE node. It will be transformed to an ODOTMETH or
- // ODOTINTER node if we find in the OCALL case below that the
- // method value is actually called.
- mse := m.(*ir.SelectorExpr)
- if src := mse.X.Type(); !src.IsShape() {
- transformDot(mse, false)
- }
-
- case ir.OCALL:
- call := m.(*ir.CallExpr)
- switch call.X.Op() {
- case ir.OTYPE:
- // Transform the conversion, now that we know the
- // type argument.
- m = transformConvCall(call)
-
- case ir.OMETHVALUE, ir.OMETHEXPR:
- // Redo the transformation of OXDOT, now that we
- // know the method value is being called. Then
- // transform the call.
- call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
- transformDot(call.X.(*ir.SelectorExpr), true)
- transformCall(call)
-
- case ir.ODOT, ir.ODOTPTR:
- // An OXDOT for a generic receiver was resolved to
- // an access to a field which has a function
- // value. Transform the call to that function, now
- // that the OXDOT was resolved.
- transformCall(call)
-
- case ir.ONAME:
- name := call.X.Name()
- if name.BuiltinOp != ir.OXXX {
- m = transformBuiltin(call)
- } else {
- // This is the case of a function value that was a
- // type parameter (implied to be a function via a
- // structural constraint) which is now resolved.
- transformCall(call)
- }
-
- case ir.OFUNCINST:
- // A call with an OFUNCINST will get transformed
- // in stencil() once we have created & attached the
- // instantiation to be called.
- // We must transform the arguments of the call now, though,
- // so that any needed CONVIFACE nodes are exposed,
- // so the dictionary format is correct.
- transformEarlyCall(call)
-
- case ir.OXDOT:
- // This is the case of a bound call or a field access
- // on a typeparam, which will be handled in the
- // dictPass. As with OFUNCINST, we must transform the
- // arguments of the call now, so any needed CONVIFACE
- // nodes are exposed.
- transformEarlyCall(call)
-
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- // These are DOTTYPEs that could get transformed into
- // ODYNAMIC DOTTYPEs by the dict pass.
-
- default:
- // Transform a call for all other values of
- // call.X.Op() that don't require any special
- // handling.
- transformCall(call)
-
- }
-
- case ir.OCLOSURE:
- if subst.skipClosure {
- break
- }
- // We're going to create a new closure from scratch, so clear m
- // to avoid using the ir.Copy by accident until we reassign it.
- m = nil
-
- x := x.(*ir.ClosureExpr)
- // Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
- // x.Func.Body.
- oldfn := x.Func
- newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil)
- ir.NameClosure(newfn.OClosure, subst.newf)
-
- saveNewf := subst.newf
- ir.CurFunc = newfn
- subst.newf = newfn
- newfn.Dcl = subst.namelist(oldfn.Dcl)
-
- // Make a closure variable for the dictionary of the
- // containing function.
- cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam)
- typed(types.Types[types.TUINTPTR], cdict)
- ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
- newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)
-
- // Copy that closure variable to a local one.
- // Note: this allows the dictionary to be captured by child closures.
- // See issue 47723.
- ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(typecheck.LocalDictName))
- typed(types.Types[types.TUINTPTR], ldict)
- ldict.Class = ir.PAUTO
- ldict.Curfn = newfn
- newfn.Dcl = append(newfn.Dcl, ldict)
- as := ir.NewAssignStmt(x.Pos(), ldict, cdict)
- as.SetTypecheck(1)
- ldict.Defn = as
- newfn.Body.Append(as)
-
- // Create inst info for the instantiated closure. The dict
- // param is the closure variable for the dictionary of the
- // outer function. Since the dictionary is shared, use the
- // same dictInfo.
- cinfo := &instInfo{
- fun: newfn,
- dictParam: ldict,
- dictInfo: subst.info.dictInfo,
- }
- subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
-
- typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
- typed(newfn.Nname.Type(), newfn.OClosure)
- newfn.SetTypecheck(1)
-
- outerinfo := subst.info
- subst.info = cinfo
- // Make sure type of closure function is set before doing body.
- newfn.Body.Append(subst.list(oldfn.Body)...)
- subst.info = outerinfo
- subst.newf = saveNewf
- ir.CurFunc = saveNewf
-
- m = ir.UseClosure(newfn.OClosure, typecheck.Target)
- subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func)
- m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
-
- case ir.OSWITCH:
- m := m.(*ir.SwitchStmt)
- if m.Tag != nil && m.Tag.Op() == ir.OTYPESW {
- break // Nothing to do here for type switches.
- }
- if m.Tag != nil && !types.IsComparable(m.Tag.Type()) {
- break // Nothing to do here for un-comparable types.
- }
- if m.Tag != nil && !m.Tag.Type().IsEmptyInterface() && m.Tag.Type().HasShape() {
- // To implement a switch on a value that is or has a type parameter, we first convert
- // that thing we're switching on to an interface{}.
- m.Tag = assignconvfn(m.Tag, types.Types[types.TINTER])
- }
- for _, c := range m.Cases {
- for i, x := range c.List {
- // If we have a case that is or has a type parameter, convert that case
- // to an interface{}.
- if !x.Type().IsEmptyInterface() && x.Type().HasShape() {
- c.List[i] = assignconvfn(x, types.Types[types.TINTER])
- }
- }
- }
-
- }
- return m
- }
-
- return edit(n)
-}
-
-// dictPass takes a function instantiation and does the transformations on the
-// operations that need to make use of the dictionary param.
-func (g *genInst) dictPass(info *instInfo) {
- savef := ir.CurFunc
- ir.CurFunc = info.fun
-
- callMap := make(map[ir.Node]bool)
-
- var edit func(ir.Node) ir.Node
- edit = func(m ir.Node) ir.Node {
- if m.Op() == ir.OCALL && m.(*ir.CallExpr).X.Op() == ir.OXDOT {
- callMap[m.(*ir.CallExpr).X] = true
- }
-
- ir.EditChildren(m, edit)
-
- switch m.Op() {
- case ir.OCLOSURE:
- newf := m.(*ir.ClosureExpr).Func
- ir.CurFunc = newf
- outerinfo := info
- info = g.instInfoMap[newf.Nname.Sym()]
-
- body := newf.Body
- for i, n := range body {
- body[i] = edit(n)
- }
-
- info = outerinfo
- ir.CurFunc = info.fun
-
- case ir.OXDOT:
- // This is the case of a dot access on a type param. This is
- // typically a bound call on the type param, but could be a
- // field access, if the constraint has a single structural type.
- mse := m.(*ir.SelectorExpr)
- src := mse.X.Type()
- assert(src.IsShape())
-
- if mse.X.Op() == ir.OTYPE {
- // Method expression T.M
- idx := findMethodExprClosure(info.dictInfo, mse)
- c := getDictionaryEntryAddr(m.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen)
- m = ir.NewConvExpr(m.Pos(), ir.OCONVNOP, mse.Type(), c)
- m.SetTypecheck(1)
- } else {
- // If we can't find the selected method in the
- // AllMethods of the bound, then this must be an access
- // to a field of a structural type. If so, we skip the
- // dictionary lookups - transformDot() will convert to
- // the desired direct field access.
- if isBoundMethod(info.dictInfo, mse) {
- if callMap[m] {
- // The OCALL surrounding this XDOT will rewrite the call
- // to use the method expression closure directly.
- break
- }
- // Convert this method value to a closure.
- // TODO: use method expression closure.
- dst := info.dictInfo.shapeToBound[mse.X.Type()]
- // Implement x.M as a conversion-to-bound-interface
- // 1) convert x to the bound interface
- // 2) select method value M on that interface
- if src.IsInterface() {
- // If type arg is an interface (unusual case),
- // we do a type assert to the type bound.
- mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
- } else {
- mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
- }
- }
- transformDot(mse, false)
- }
- case ir.OCALL:
- call := m.(*ir.CallExpr)
- op := call.X.Op()
- if op == ir.OXDOT {
- // This is a call of a method value where the value has a type parameter type.
- // We transform to a call of the appropriate method expression closure
- // in the dictionary.
- // So if x has a type parameter type:
- // _ = x.m(a)
- // Rewrite to:
- // _ = methexpr<m>(x, a)
- se := call.X.(*ir.SelectorExpr)
- call.SetOp(ir.OCALLFUNC)
- idx := findMethodExprClosure(info.dictInfo, se)
- c := getDictionaryEntryAddr(se.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen)
- t := typecheck.NewMethodType(se.Type(), se.X.Type())
- call.X = ir.NewConvExpr(se.Pos(), ir.OCONVNOP, t, c)
- typed(t, call.X)
- call.Args.Prepend(se.X)
- break
- // TODO: deref case?
- }
- if op == ir.OMETHVALUE {
- // Redo the transformation of OXDOT, now that we
- // know the method value is being called.
- call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
- transformDot(call.X.(*ir.SelectorExpr), true)
- }
- transformCall(call)
-
- case ir.OCONVIFACE:
- if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
- // Was T->interface{}, after stenciling it is now interface{}->interface{}.
- // No longer need the conversion. See issue 48276.
- m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
- break
- }
- mce := m.(*ir.ConvExpr)
- // Note: x's argument is still typed as a type parameter.
- // m's argument now has an instantiated type.
- if mce.X.Type().HasShape() || (m.Type().HasShape() && !m.Type().IsEmptyInterface()) {
- m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type())
- }
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- dt := m.(*ir.TypeAssertExpr)
- if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) {
- break
- }
- if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) {
- break
- }
- var rtype, itab ir.Node
- if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
- // TODO(mdempsky): Investigate executing this block unconditionally.
- ix := findDictType(info, m.Type())
- assert(ix >= 0)
- rtype = getDictionaryType(info, info.dictParam, dt.Pos(), ix)
- } else {
- // nonempty interface to noninterface. Need an itab.
- ix := -1
- for i, ic := range info.dictInfo.itabConvs {
- if ic == m {
- ix = info.dictInfo.startItabConv + i
- break
- }
- }
- assert(ix >= 0)
- itab = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
- }
- op := ir.ODYNAMICDOTTYPE
- if m.Op() == ir.ODOTTYPE2 {
- op = ir.ODYNAMICDOTTYPE2
- }
- m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rtype)
- m.(*ir.DynamicTypeAssertExpr).ITab = itab
- m.SetType(dt.Type())
- m.SetTypecheck(1)
- case ir.OCASE:
- if _, ok := m.(*ir.CommClause); ok {
- // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
- break
- }
- m := m.(*ir.CaseClause)
- for i, c := range m.List {
- if c.Op() == ir.OTYPE && c.Type().HasShape() {
- // Use a *runtime._type for the dynamic type.
- ix := findDictType(info, m.List[i].Type())
- assert(ix >= 0)
- dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen))
-
- // For type switch from nonempty interfaces to non-interfaces, we need an itab as well.
- if !m.List[i].Type().IsInterface() {
- if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok {
- // Type switch from nonempty interface. We need a *runtime.itab
- // for the dynamic type.
- ix := -1
- for j, ic := range info.dictInfo.itabConvs {
- if ic == m.List[i] {
- ix = info.dictInfo.startItabConv + j
- break
- }
- }
- assert(ix >= 0)
- dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
- }
- }
- typed(m.List[i].Type(), dt)
- m.List[i] = dt
- }
- }
-
- }
- return m
- }
- edit(info.fun)
- ir.CurFunc = savef
-}
-
-// findDictType looks for type t in the typeparams or derived types in the generic
-// function info.gfInfo. This will indicate the dictionary entry with the
-// correct concrete type for the associated instantiated function.
-func findDictType(info *instInfo, t *types.Type) int {
- for i, dt := range info.dictInfo.shapeParams {
- if dt == t {
- return i
- }
- }
- for i, dt := range info.dictInfo.derivedTypes {
- if types.IdenticalStrict(dt, t) {
- return i + len(info.dictInfo.shapeParams)
- }
- }
- return -1
-}
-
-// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface
-// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
-// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
-// conversion.
-func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
- assert(v.Type().HasShape() || (in.Type().HasShape() && !in.Type().IsEmptyInterface()))
- assert(dst.IsInterface())
-
- if v.Type().IsInterface() {
- // Converting from an interface. The shape-ness of the source doesn't really matter, as
- // we'll be using the concrete type from the first interface word.
- if dst.IsEmptyInterface() {
- // Converting I2E. OCONVIFACE does that for us, and doesn't depend
- // on what the empty interface was instantiated with. No dictionary entry needed.
- v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
- v.SetTypecheck(1)
- return v
- }
- if !in.Type().HasShape() {
- // Regular OCONVIFACE works if the destination isn't parameterized.
- v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
- v.SetTypecheck(1)
- return v
- }
-
- // We get the destination interface type from the dictionary and the concrete
- // type from the argument's itab. Call runtime.convI2I to get the new itab.
- tmp := typecheck.Temp(v.Type())
- as := ir.NewAssignStmt(pos, tmp, v)
- as.SetTypecheck(1)
- itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp)
- typed(types.Types[types.TUINTPTR].PtrTo(), itab)
- idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp)
- typed(types.Types[types.TUNSAFEPTR], idata)
-
- fn := typecheck.LookupRuntime("convI2I")
- fn.SetTypecheck(1)
- types.CalcSize(fn.Type())
- call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
- typed(types.Types[types.TUINT8].PtrTo(), call)
- ix := findDictType(info, in.Type())
- assert(ix >= 0)
- inter := getDictionaryType(info, dictParam, pos, ix)
- call.Args = []ir.Node{inter, itab}
- i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata)
- typed(dst, i)
- i.PtrInit().Append(as)
- return i
- }
-
- var rt ir.Node
- if !dst.IsEmptyInterface() {
- // We should have an itab entry in the dictionary. Using this itab
- // will be more efficient than converting to an empty interface first
- // and then type asserting to dst.
- ix := -1
- for i, ic := range info.dictInfo.itabConvs {
- if ic == in {
- ix = info.dictInfo.startItabConv + i
- break
- }
- }
- assert(ix >= 0)
- rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
- } else {
- ix := findDictType(info, v.Type())
- assert(ix >= 0)
- // Load the actual runtime._type of the type parameter from the dictionary.
- rt = getDictionaryType(info, dictParam, pos, ix)
- }
-
- // Figure out what the data field of the interface will be.
- data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
- typed(types.Types[types.TUNSAFEPTR], data)
-
- // Build an interface from the type and data parts.
- var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
- typed(dst, i)
- return i
-}
-
-func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
- s := make([]*ir.Name, len(l))
- for i, n := range l {
- s[i] = subst.localvar(n)
- }
- return s
-}
-
-func (subst *subster) list(l []ir.Node) []ir.Node {
- s := make([]ir.Node, len(l))
- for i, n := range l {
- s[i] = subst.node(n)
- }
- return s
-}
-
-// fields sets the Nname field for the Field nodes inside a type signature, based
-// on the corresponding in/out parameters in dcl. It depends on the in and out
-// parameters being in order in dcl.
-func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
- // Find the starting index in dcl of declarations of the class (either
- // PPARAM or PPARAMOUT).
- var i int
- for i = range dcl {
- if dcl[i].Class == class {
- break
- }
- }
-
- // Create newfields nodes that are copies of the oldfields nodes, but
- // with substitution for any type params, and with Nname set to be the node in
- // Dcl for the corresponding PPARAM or PPARAMOUT.
- newfields := make([]*types.Field, len(oldfields))
- for j := range oldfields {
- newfields[j] = oldfields[j].Copy()
- newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
- // A PPARAM field will be missing from dcl if its name is
- // unspecified or specified as "_". So, we compare the dcl sym
- // with the field sym (or sym of the field's Nname node). (Unnamed
- // results still have a name like ~r2 in their Nname node.) If
- // they don't match, this dcl (if there is one left) must apply to
- // a later field.
- if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
- (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
- newfields[j].Nname = dcl[i]
- i++
- }
- }
- return newfields
-}
-
-// deref does a single deref of type t, if it is a pointer type.
-func deref(t *types.Type) *types.Type {
- if t.IsPtr() {
- return t.Elem()
- }
- return t
-}
-
-// markTypeUsed marks type t as used in order to help avoid dead-code elimination of
-// needed methods.
-func markTypeUsed(t *types.Type, lsym *obj.LSym) {
- if t.IsInterface() {
- return
- }
- // TODO: This is somewhat overkill, we really only need it
- // for types that are put into interfaces.
- // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
- reflectdata.MarkTypeUsedInInterface(t, lsym)
-}
-
-// getDictionarySym returns the dictionary for the named generic function gf, which
-// is instantiated with the type arguments targs.
-func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
- if len(targs) == 0 {
- base.Fatalf("%s should have type arguments", gf.Sym().Name)
- }
-
- // Enforce that only concrete types can make it to here.
- for _, t := range targs {
- if t.HasShape() {
- panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
- }
- }
-
- // Get a symbol representing the dictionary.
- sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth)
-
- // Initialize the dictionary, if we haven't yet already.
- lsym := sym.Linksym()
- if len(lsym.P) > 0 {
- // We already started creating this dictionary and its lsym.
- return sym
- }
-
- infoPrint("=== Creating dictionary %v\n", sym.Name)
- off := 0
- // Emit an entry for each targ (concrete type or gcshape).
- for _, t := range targs {
- infoPrint(" * %v\n", t)
- s := reflectdata.TypeLinksym(t)
- off = objw.SymPtr(lsym, off, s, 0)
- markTypeUsed(t, lsym)
- }
-
- instInfo := g.getInstantiation(gf, targs, isMeth)
- info := instInfo.dictInfo
-
- subst := typecheck.Tsubster{
- Tparams: info.shapeParams,
- Targs: targs,
- }
- // Emit an entry for each derived type (after substituting targs)
- for _, t := range info.derivedTypes {
- ts := subst.Typ(t)
- infoPrint(" - %v\n", ts)
- s := reflectdata.TypeLinksym(ts)
- off = objw.SymPtr(lsym, off, s, 0)
- markTypeUsed(ts, lsym)
- }
- // Emit an entry for each subdictionary (after substituting targs)
- for _, subDictInfo := range info.subDictCalls {
- var sym *types.Sym
- n := subDictInfo.callNode
- switch n.Op() {
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
- call := n.(*ir.CallExpr)
- if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
- var nameNode *ir.Name
- se := call.X.(*ir.SelectorExpr)
- if se.X.Type().IsShape() {
- tparam := se.X.Type()
- // Ensure methods on all instantiating types are computed.
- typecheck.CalcMethods(tparam)
- if typecheck.Lookdot1(nil, se.Sel, tparam, tparam.AllMethods(), 0) != nil {
- // This is a method call enabled by a type bound.
- // We need this extra check for method expressions,
- // which don't add in the implicit XDOTs.
- tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
- tmpse = typecheck.AddImplicitDots(tmpse)
- tparam = tmpse.X.Type()
- }
- if !tparam.IsShape() {
- // The method expression is not
- // really on a typeparam.
- break
- }
- ix := -1
- for i, shape := range info.shapeParams {
- if shape == tparam {
- ix = i
- break
- }
- }
- assert(ix >= 0)
- recvType := targs[ix]
- if recvType.IsInterface() || len(recvType.RParams()) == 0 {
- // No sub-dictionary entry is
- // actually needed, since the
- // type arg is not an
- // instantiated type that
- // will have generic methods.
- break
- }
- // This is a method call for an
- // instantiated type, so we need a
- // sub-dictionary.
- targs := recvType.RParams()
- genRecvType := recvType.OrigType()
- nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
- sym = g.getDictionarySym(nameNode, targs, true)
- } else {
- // This is the case of a normal
- // method call on a generic type.
- assert(subDictInfo.savedXNode == se)
- sym = g.getSymForMethodCall(se, &subst)
- }
- } else {
- inst, ok := call.X.(*ir.InstExpr)
- if ok {
- // Code hasn't been transformed yet
- assert(subDictInfo.savedXNode == inst)
- }
- // If !ok, then the generic method/function call has
- // already been transformed to a shape instantiation
- // call. Either way, use the SelectorExpr/InstExpr
- // node saved in info.
- cex := subDictInfo.savedXNode
- if se, ok := cex.(*ir.SelectorExpr); ok {
- sym = g.getSymForMethodCall(se, &subst)
- } else {
- inst := cex.(*ir.InstExpr)
- nameNode := inst.X.(*ir.Name)
- subtargs := typecheck.TypesOf(inst.Targs)
- for i, t := range subtargs {
- subtargs[i] = subst.Typ(t)
- }
- sym = g.getDictionarySym(nameNode, subtargs, false)
- }
- }
-
- case ir.OFUNCINST:
- inst := n.(*ir.InstExpr)
- nameNode := inst.X.(*ir.Name)
- subtargs := typecheck.TypesOf(inst.Targs)
- for i, t := range subtargs {
- subtargs[i] = subst.Typ(t)
- }
- sym = g.getDictionarySym(nameNode, subtargs, false)
-
- case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
- sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)
-
- default:
- assert(false)
- }
-
- if sym == nil {
- // Unused sub-dictionary entry, just emit 0.
- off = objw.Uintptr(lsym, off, 0)
- infoPrint(" - Unused subdict entry\n")
- } else {
- off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
- infoPrint(" - Subdict %v\n", sym.Name)
- }
- }
-
- g.instantiateMethods()
- delay := &delayInfo{
- gf: gf,
- targs: targs,
- sym: sym,
- off: off,
- isMeth: isMeth,
- }
- g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
- return sym
-}
-
-// getSymForMethodCall gets the dictionary sym for a method call, method value, or method
-// expression that has selector se. subst gives the substitution from shape types to
-// concrete types.
-func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym {
- // For everything except method expressions, 'recvType = deref(se.X.Type)' would
- // also give the receiver type. For method expressions with embedded types, we
- // need to look at the type of the selection to get the final receiver type.
- recvType := deref(se.Selection.Type.Recv().Type)
- genRecvType := recvType.OrigType()
- nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
- subtargs := recvType.RParams()
- s2targs := make([]*types.Type, len(subtargs))
- for i, t := range subtargs {
- s2targs[i] = subst.Typ(t)
- }
- return g.getDictionarySym(nameNode, s2targs, true)
-}
-
-// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
-// any needed LSyms for itabs. The itab lsyms create wrappers which need various
-// dictionaries and method instantiations to be complete, so, to avoid recursive
-// dependencies, we finalize the itab lsyms only after all dictionaries syms and
-// instantiations have been created.
-// Also handles writing method expression closures into the dictionaries.
-func (g *genInst) finalizeSyms() {
-Outer:
- for _, d := range g.dictSymsToFinalize {
- infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
-
- lsym := d.sym.Linksym()
- instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
- info := instInfo.dictInfo
-
- subst := typecheck.Tsubster{
- Tparams: info.shapeParams,
- Targs: d.targs,
- }
-
- // Emit an entry for each itab
- for _, n := range info.itabConvs {
- var srctype, dsttype *types.Type
- switch n.Op() {
- case ir.OXDOT, ir.OMETHVALUE:
- se := n.(*ir.SelectorExpr)
- srctype = subst.Typ(se.X.Type())
- dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type())
- dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type())
- case ir.OCONVIFACE:
- srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
- dsttype = subst.Typ(n.Type())
- case ir.OTYPE:
- srctype = subst.Typ(n.Type())
- dsttype = subst.Typ(info.type2switchType[n])
- default:
- base.Fatalf("itab entry with unknown op %s", n.Op())
- }
- if srctype.IsInterface() || dsttype.IsEmptyInterface() {
- // No itab is wanted if src type is an interface. We
- // will use a type assert instead.
- d.off = objw.Uintptr(lsym, d.off, 0)
- infoPrint(" + Unused itab entry for %v\n", srctype)
- } else {
- // Make sure all new fully-instantiated types have
- // their methods created before generating any itabs.
- g.instantiateMethods()
- itabLsym := reflectdata.ITabLsym(srctype, dsttype)
- d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
- markTypeUsed(srctype, lsym)
- infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
- }
- }
-
- // Emit an entry for each method expression closure.
- // Each entry is a (captureless) closure pointing to the method on the instantiating type.
- // In other words, the entry is a runtime.funcval whose fn field is set to the method
- // in question, and has no other fields. The address of this dictionary entry can be
- // cast to a func of the appropriate type.
- // TODO: do these need to be done when finalizing, or can we do them earlier?
- for _, bf := range info.methodExprClosures {
- rcvr := d.targs[bf.idx]
- rcvr2 := deref(rcvr)
- found := false
- typecheck.CalcMethods(rcvr2) // Ensure methods on all instantiating types are computed.
- for _, f := range rcvr2.AllMethods().Slice() {
- if f.Sym.Name == bf.name {
- codePtr := ir.MethodSym(rcvr, f.Sym).Linksym()
- d.off = objw.SymPtr(lsym, d.off, codePtr, 0)
- infoPrint(" + MethodExprClosure for %v.%s\n", rcvr, bf.name)
- found = true
- break
- }
- }
- if !found {
- // We failed to find a method expression needed for this
- // dictionary. This may happen because we tried to create a
- // dictionary for an invalid instantiation.
- //
- // For example, in test/typeparam/issue54225.go, we attempt to
- // construct a dictionary for "Node[struct{}].contentLen",
- // even though "struct{}" does not implement "Value", so it
- // cannot actually be used as a type argument to "Node".
- //
- // The real issue here is we shouldn't be attempting to create
- // those dictionaries in the first place (e.g., CL 428356),
- // but that fix is scarier for backporting to Go 1.19. Too
- // many backport CLs to this code have fixed one issue while
- // introducing another.
- //
- // So as a hack, instead of calling Fatalf, we simply skip
- // calling objw.Global below, which prevents us from emitting
- // the broken dictionary. The linker's dead code elimination
- // should then naturally prune this invalid, unneeded
- // dictionary. Worst case, if the dictionary somehow *is*
- // needed by the final executable, we've just turned an ICE
- // into a link-time missing symbol failure.
- infoPrint(" ! abandoning dictionary %v; missing method expression %v.%s\n", d.sym.Name, rcvr, bf.name)
- continue Outer
- }
- }
-
- objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
- infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
- }
- g.dictSymsToFinalize = nil
-}
-
-func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
- sym := g.getDictionarySym(gf, targs, isMeth)
-
- // Make (or reuse) a node referencing the dictionary symbol.
- var n *ir.Name
- if sym.Def != nil {
- n = sym.Def.(*ir.Name)
- } else {
- // We set the position of a static dictionary to be the position of
- // one of its uses.
- n = ir.NewNameAt(pos, sym)
- n.Curfn = ir.CurFunc
- 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. Addr node gets position that was passed in.
- np := typecheck.NodAddrAt(pos, 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.
- // TODO: use a cast, or is typing directly ok?
- np.SetType(types.Types[types.TUINTPTR])
- np.SetTypecheck(1)
- return np
-}
-
-// hasShapeNodes returns true if the type of any node in targs has a shape.
-func hasShapeNodes(targs []ir.Ntype) bool {
- for _, n := range targs {
- if n.Type().HasShape() {
- return true
- }
- }
- return false
-}
-
-// hasShapeTypes returns true if any type in targs has a shape.
-func hasShapeTypes(targs []*types.Type) bool {
- for _, t := range targs {
- if t.HasShape() {
- return true
- }
- }
- return false
-}
-
-// getInstInfo get the dictionary format for a function instantiation- type params, derived
-// types, and needed subdictionaries, itabs, and method expression closures.
-func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
- info := instInfo.dictInfo
- info.shapeParams = shapes
-
- for _, t := range info.shapeParams {
- b := info.shapeToBound[t]
- if b.HasShape() {
- // If a type bound is parameterized (unusual case), then we
- // may need its derived type to do a type assert when doing a
- // bound call for a type arg that is an interface.
- addType(info, nil, b)
- }
- }
-
- for _, n := range st.Dcl {
- addType(info, n, n.Type())
- n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
- }
-
- if infoPrintMode {
- fmt.Printf(">>> InstInfo for %v\n", st)
- for _, t := range info.shapeParams {
- fmt.Printf(" Typeparam %v\n", t)
- }
- }
-
- // Map to remember when we have seen an instantiated function value or method
- // expression/value as part of a call, so we can determine when we encounter
- // an uncalled function value or method expression/value.
- callMap := make(map[ir.Node]bool)
-
- var visitFunc func(ir.Node)
- visitFunc = func(n ir.Node) {
- switch n.Op() {
- case ir.OFUNCINST:
- if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
- infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
- info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
- }
- case ir.OMETHEXPR, ir.OMETHVALUE:
- if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
- len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
- hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
- if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
- infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n)
- } else {
- infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
- }
- info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
- }
- case ir.OCALL:
- ce := n.(*ir.CallExpr)
- if ce.X.Op() == ir.OFUNCINST {
- callMap[ce.X] = true
- if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
- infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
- // Save the instExpr node for the function call,
- // since we will lose this information when the
- // generic function call is transformed to a call
- // on the shape instantiation.
- info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
- }
- }
- // Note: this XDOT code is not actually needed as long as we
- // continue to disable type parameters on RHS of type
- // declarations (#45639).
- if ce.X.Op() == ir.OXDOT {
- callMap[ce.X] = true
- if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) {
- infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
- info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
- }
- }
- case ir.OCALLMETH:
- ce := n.(*ir.CallExpr)
- if ce.X.Op() == ir.ODOTMETH &&
- len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
- callMap[ce.X] = true
- if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
- infoPrint(" Subdictionary at generic method call: %v\n", n)
- // Save the selector for the method call, since we
- // will eventually lose this information when the
- // generic method call is transformed into a
- // function call on the method shape instantiation.
- info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
- }
- }
- case ir.OCONVIFACE:
- if n.Type().IsInterface() && !n.Type().IsEmptyInterface() &&
- (n.Type().HasShape() || n.(*ir.ConvExpr).X.Type().HasShape()) {
- infoPrint(" Itab for interface conv: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
- case ir.OXDOT:
- se := n.(*ir.SelectorExpr)
- if se.X.Op() == ir.OTYPE && se.X.Type().IsShape() {
- // Method expression.
- addMethodExprClosure(info, se)
- break
- }
- if isBoundMethod(info, se) {
- if callMap[n] {
- // Method value called directly. Use method expression closure.
- addMethodExprClosure(info, se)
- break
- }
- // Method value not called directly. Still doing the old way.
- infoPrint(" Itab for bound call: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
-
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() {
- infoPrint(" Itab for dot type: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
- case ir.OCLOSURE:
- // Visit the closure body and add all relevant entries to the
- // dictionary of the outer function (closure will just use
- // the dictionary of the outer function).
- cfunc := n.(*ir.ClosureExpr).Func
- for _, n1 := range cfunc.Body {
- ir.Visit(n1, visitFunc)
- }
- for _, n := range cfunc.Dcl {
- n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
- }
- case ir.OSWITCH:
- ss := n.(*ir.SwitchStmt)
- if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW &&
- !ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
- for _, cc := range ss.Cases {
- for _, c := range cc.List {
- if c.Op() == ir.OTYPE && c.Type().HasShape() {
- // Type switch from a non-empty interface - might need an itab.
- infoPrint(" Itab for type switch: %v\n", c)
- info.itabConvs = append(info.itabConvs, c)
- if info.type2switchType == nil {
- info.type2switchType = map[ir.Node]*types.Type{}
- }
- info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type()
- }
- }
- }
- }
- }
- addType(info, n, n.Type())
- }
-
- for _, stmt := range st.Body {
- ir.Visit(stmt, visitFunc)
- }
- if infoPrintMode {
- for _, t := range info.derivedTypes {
- fmt.Printf(" Derived type %v\n", t)
- }
- fmt.Printf(">>> Done Instinfo\n")
- }
- info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
- info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
- info.startMethodExprClosures = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
- info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) + len(info.methodExprClosures)
-}
-
-// isBoundMethod returns true if the selection indicated by se is a bound method of
-// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If
-// isBoundMethod returns false, then the selection must be a field access of a
-// structural type.
-func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool {
- bound := info.shapeToBound[se.X.Type()]
- return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil
-}
-
-func shapeIndex(info *dictInfo, t *types.Type) int {
- for i, s := range info.shapeParams {
- if s == t {
- return i
- }
- }
- base.Fatalf("can't find type %v in shape params", t)
- return -1
-}
-
-// addMethodExprClosure adds the T.M method expression to the list of bound method expressions
-// used in the generic body.
-// isBoundMethod must have returned true on the same arguments.
-func addMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) {
- idx := shapeIndex(info, se.X.Type())
- name := se.Sel.Name
- for _, b := range info.methodExprClosures {
- if idx == b.idx && name == b.name {
- return
- }
- }
- infoPrint(" Method expression closure for %v.%s\n", info.shapeParams[idx], name)
- info.methodExprClosures = append(info.methodExprClosures, methodExprClosure{idx: idx, name: name})
-}
-
-// findMethodExprClosure finds the entry in the dictionary to use for the T.M
-// method expression encoded in se.
-// isBoundMethod must have returned true on the same arguments.
-func findMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) int {
- idx := shapeIndex(info, se.X.Type())
- name := se.Sel.Name
- for i, b := range info.methodExprClosures {
- if idx == b.idx && name == b.name {
- return i
- }
- }
- base.Fatalf("can't find method expression closure for %s %s", se.X.Type(), name)
- return -1
-}
-
-// addType adds t to info.derivedTypes if it is parameterized type (which is not
-// just a simple shape) that is different from any existing type on
-// info.derivedTypes.
-func addType(info *dictInfo, n ir.Node, t *types.Type) {
- if t == nil || !t.HasShape() {
- return
- }
- if t.IsShape() {
- return
- }
- if t.Kind() == types.TFUNC && n != nil &&
- (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
- // Don't use the type of a named generic function or method,
- // since that is parameterized by other typeparams.
- // (They all come from arguments of a FUNCINST node.)
- return
- }
- if doubleCheck && !parameterizedBy(t, info.shapeParams) {
- base.Fatalf("adding type with invalid parameters %+v", t)
- }
- if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
- // Multiple return values are not a relevant new type (?).
- return
- }
- // Ignore a derived type we've already added.
- for _, et := range info.derivedTypes {
- if types.IdenticalStrict(t, et) {
- return
- }
- }
- info.derivedTypes = append(info.derivedTypes, t)
-}
-
-// parameterizedBy returns true if t is parameterized by (at most) params.
-func parameterizedBy(t *types.Type, params []*types.Type) bool {
- return parameterizedBy1(t, params, map[*types.Type]bool{})
-}
-func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
- if visited[t] {
- return true
- }
- visited[t] = true
-
- if t.Sym() != nil && len(t.RParams()) > 0 {
- // This defined type is instantiated. Check the instantiating types.
- for _, r := range t.RParams() {
- if !parameterizedBy1(r, params, visited) {
- return false
- }
- }
- return true
- }
- if t.IsShape() {
- // Check if t is one of the allowed parameters in scope.
- for _, p := range params {
- if p == t {
- return true
- }
- }
- // Couldn't find t in the list of allowed parameters.
- return false
-
- }
- switch t.Kind() {
- case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
- return parameterizedBy1(t.Elem(), params, visited)
-
- case types.TMAP:
- return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)
-
- case types.TFUNC:
- return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)
-
- case types.TSTRUCT:
- for _, f := range t.Fields().Slice() {
- if !parameterizedBy1(f.Type, params, visited) {
- return false
- }
- }
- return true
-
- case types.TINTER:
- for _, f := range t.Methods().Slice() {
- if !parameterizedBy1(f.Type, params, visited) {
- return false
- }
- }
- return true
-
- 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:
- return true
-
- case types.TUNION:
- for i := 0; i < t.NumTerms(); i++ {
- tt, _ := t.Term(i)
- if !parameterizedBy1(tt, params, visited) {
- return false
- }
- }
- return true
-
- default:
- base.Fatalf("bad type kind %+v", t)
- return true
- }
-}
-
-// startClosure starts creation of a closure that has the function type typ. It
-// creates all the formal params and results according to the type typ. On return,
-// the body and closure variables of the closure must still be filled in, and
-// ir.UseClosure() called.
-func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) {
- // Make a new internal function.
- fn := ir.NewClosureFunc(pos, outer != nil)
- ir.NameClosure(fn.OClosure, outer)
-
- // Build formal argument and return lists.
- var formalParams []*types.Field // arguments of closure
- var formalResults []*types.Field // returns of closure
- for i := 0; i < typ.NumParams(); i++ {
- t := typ.Params().Field(i).Type
- arg := ir.NewNameAt(pos, closureSym(outer, "a", i))
- arg.Class = ir.PPARAM
- typed(t, arg)
- arg.Curfn = fn
- fn.Dcl = append(fn.Dcl, arg)
- f := types.NewField(pos, arg.Sym(), t)
- f.Nname = arg
- f.SetIsDDD(typ.Params().Field(i).IsDDD())
- formalParams = append(formalParams, f)
- }
- for i := 0; i < typ.NumResults(); i++ {
- t := typ.Results().Field(i).Type
- result := ir.NewNameAt(pos, closureSym(outer, "r", i)) // TODO: names not needed?
- result.Class = ir.PPARAMOUT
- typed(t, result)
- result.Curfn = fn
- fn.Dcl = append(fn.Dcl, result)
- f := types.NewField(pos, result.Sym(), t)
- f.Nname = result
- formalResults = append(formalResults, f)
- }
-
- // Build an internal function with the right signature.
- closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults)
- typed(closureType, fn.Nname)
- typed(typ, fn.OClosure)
- fn.SetTypecheck(1)
- return fn, formalParams, formalResults
-
-}
-
-// closureSym returns outer.Sym().Pkg.LookupNum(prefix, n).
-// If outer is nil, then types.LocalPkg is used instead.
-func closureSym(outer *ir.Func, prefix string, n int) *types.Sym {
- pkg := types.LocalPkg
- if outer != nil {
- pkg = outer.Sym().Pkg
- }
- return pkg.LookupNum(prefix, n)
-}
-
-// assertToBound returns a new node that converts a node rcvr with interface type to
-// the 'dst' interface type.
-func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
- if !dst.HasShape() {
- return typed(dst, ir.NewTypeAssertExpr(pos, rcvr, nil))
- }
-
- ix := findDictType(info, dst)
- assert(ix >= 0)
- rt := getDictionaryType(info, dictVar, pos, ix)
- return typed(dst, ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt))
-}
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{
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.
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) {
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)
-}
+++ /dev/null
-// 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.
-
-// This file contains transformation functions on nodes, which are the
-// transformations that the typecheck package does that are distinct from the
-// typechecking functionality. These transform functions are pared-down copies of
-// the original typechecking functions, with all code removed that is related to:
-//
-// - Detecting compile-time errors (already done by types2)
-// - Setting the actual type of existing nodes (already done based on
-// type info from types2)
-// - Dealing with untyped constants (which types2 has already resolved)
-//
-// Each of the transformation functions requires that node passed in has its type
-// and typecheck flag set. If the transformation function replaces or adds new
-// nodes, it will set the type and typecheck flag for those new nodes.
-
-package noder
-
-import (
- "cmd/compile/internal/base"
- "cmd/compile/internal/ir"
- "cmd/compile/internal/typecheck"
- "cmd/compile/internal/types"
- "fmt"
- "go/constant"
-)
-
-// Transformation functions for expressions
-
-// transformAdd transforms an addition operation (currently just addition of
-// strings). Corresponds to the "binary operators" case in typecheck.typecheck1.
-func transformAdd(n *ir.BinaryExpr) ir.Node {
- assert(n.Type() != nil && n.Typecheck() == 1)
- l := n.X
- if l.Type().IsString() {
- var add *ir.AddStringExpr
- if l.Op() == ir.OADDSTR {
- add = l.(*ir.AddStringExpr)
- add.SetPos(n.Pos())
- } else {
- add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
- }
- r := n.Y
- if r.Op() == ir.OADDSTR {
- r := r.(*ir.AddStringExpr)
- add.List.Append(r.List.Take()...)
- } else {
- add.List.Append(r)
- }
- typed(l.Type(), add)
- return add
- }
- return n
-}
-
-// Corresponds to typecheck.stringtoruneslit.
-func stringtoruneslit(n *ir.ConvExpr) ir.Node {
- if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
- base.Fatalf("stringtoarraylit %v", n)
- }
-
- var list []ir.Node
- i := 0
- eltType := n.Type().Elem()
- for _, r := range ir.StringVal(n.X) {
- elt := ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r)))
- // Change from untyped int to the actual element type determined
- // by types2. No need to change elt.Key, since the array indexes
- // are just used for setting up the element ordering.
- elt.Value.SetType(eltType)
- list = append(list, elt)
- i++
- }
-
- nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, n.Type(), list)
- typed(n.Type(), nn)
- // Need to transform the OCOMPLIT.
- return transformCompLit(nn)
-}
-
-// transformConv transforms an OCONV node as needed, based on the types involved,
-// etc. Corresponds to typecheck.tcConv.
-func transformConv(n *ir.ConvExpr) ir.Node {
- t := n.X.Type()
- op, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
- if op == ir.OXXX {
- // types2 currently ignores pragmas, so a 'notinheap' mismatch is the
- // one type-related error that it does not catch. This error will be
- // caught here by Convertop (see two checks near beginning of
- // Convertop) and reported at the end of noding.
- base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why)
- return n
- }
- n.SetOp(op)
- switch n.Op() {
- case ir.OCONVNOP:
- if t.Kind() == n.Type().Kind() {
- switch t.Kind() {
- case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
- // Floating point casts imply rounding and
- // so the conversion must be kept.
- n.SetOp(ir.OCONV)
- }
- }
-
- // Do not convert to []byte literal. See CL 125796.
- // Generated code and compiler memory footprint is better without it.
- case ir.OSTR2BYTES:
- // ok
-
- case ir.OSTR2RUNES:
- if n.X.Op() == ir.OLITERAL {
- return stringtoruneslit(n)
- }
-
- case ir.OBYTES2STR:
- assert(t.IsSlice())
- assert(t.Elem().Kind() == types.TUINT8)
- if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] {
- // If t is a slice of a user-defined byte type B (not uint8
- // or byte), then add an extra CONVNOP from []B to []byte, so
- // that the call to slicebytetostring() added in walk will
- // typecheck correctly.
- n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X)
- n.X.SetTypecheck(1)
- }
-
- case ir.ORUNES2STR:
- assert(t.IsSlice())
- assert(t.Elem().Kind() == types.TINT32)
- if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] {
- // If t is a slice of a user-defined rune type B (not uint32
- // or rune), then add an extra CONVNOP from []B to []rune, so
- // that the call to slicerunetostring() added in walk will
- // typecheck correctly.
- n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X)
- n.X.SetTypecheck(1)
- }
-
- }
- return n
-}
-
-// transformConvCall transforms a conversion call. Corresponds to the OTYPE part of
-// typecheck.tcCall.
-func transformConvCall(n *ir.CallExpr) ir.Node {
- assert(n.Type() != nil && n.Typecheck() == 1)
- arg := n.Args[0]
- n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
- typed(n.X.Type(), n1)
- return transformConv(n1)
-}
-
-// transformCall transforms a normal function/method call. Corresponds to last half
-// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even
-// in the case of OCALL/OFUNCINST.
-func transformCall(n *ir.CallExpr) {
- // Set base.Pos, since transformArgs below may need it, but transformCall
- // is called in some passes that don't set base.Pos.
- ir.SetPos(n)
- // n.Type() can be nil for calls with no return value
- assert(n.Typecheck() == 1)
- typecheck.RewriteNonNameCall(n)
- transformArgs(n)
- l := n.X
- t := l.Type()
-
- switch l.Op() {
- case ir.ODOTINTER:
- n.SetOp(ir.OCALLINTER)
-
- case ir.ODOTMETH:
- l := l.(*ir.SelectorExpr)
- n.SetOp(ir.OCALLMETH)
-
- tp := t.Recv().Type
-
- if l.X == nil || !types.Identical(l.X.Type(), tp) {
- base.Fatalf("method receiver")
- }
-
- default:
- n.SetOp(ir.OCALLFUNC)
- }
-
- typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
- if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 {
- typecheck.FixMethodCall(n)
- }
- if t.NumResults() == 1 {
- if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
- if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
- // Emit code for runtime.getg() directly instead of calling function.
- // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
- // so that the ordering pass can make sure to preserve the semantics of the original code
- // (in particular, the exact time of the function call) by introducing temporaries.
- // In this case, we know getg() always returns the same result within a given function
- // and we want to avoid the temporaries, so we do the rewrite earlier than is typical.
- n.SetOp(ir.OGETG)
- }
- }
- return
- }
-}
-
-// transformEarlyCall transforms the arguments of a call with an OFUNCINST node.
-func transformEarlyCall(n *ir.CallExpr) {
- transformArgs(n)
- typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args)
-}
-
-// transformCompare transforms a compare operation (currently just equals/not
-// equals). Corresponds to the "comparison operators" case in
-// typecheck.typecheck1, including tcArith.
-func transformCompare(n *ir.BinaryExpr) {
- assert(n.Type() != nil && n.Typecheck() == 1)
- if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
- // Comparison is okay as long as one side is assignable to the
- // other. The only allowed case where the conversion is not CONVNOP is
- // "concrete == interface". In that case, check comparability of
- // the concrete type. The conversion allocates, so only do it if
- // the concrete type is huge.
- l, r := n.X, n.Y
- lt, rt := l.Type(), r.Type()
- converted := false
- if rt.Kind() != types.TBLANK {
- aop, _ := typecheck.Assignop(lt, rt)
- if aop != ir.OXXX {
- types.CalcSize(lt)
- if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 {
- l = ir.NewConvExpr(base.Pos, aop, rt, l)
- l.SetTypecheck(1)
- }
-
- converted = true
- }
- }
-
- if !converted && lt.Kind() != types.TBLANK {
- aop, _ := typecheck.Assignop(rt, lt)
- if aop != ir.OXXX {
- types.CalcSize(rt)
- if rt.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 {
- r = ir.NewConvExpr(base.Pos, aop, lt, r)
- r.SetTypecheck(1)
- }
- }
- }
- n.X, n.Y = l, r
- }
-}
-
-// Corresponds to typecheck.implicitstar.
-func implicitstar(n ir.Node) ir.Node {
- // insert implicit * if needed for fixed array
- t := n.Type()
- if !t.IsPtr() {
- return n
- }
- t = t.Elem()
- if !t.IsArray() {
- return n
- }
- star := ir.NewStarExpr(base.Pos, n)
- star.SetImplicit(true)
- return typed(t, star)
-}
-
-// transformIndex transforms an index operation. Corresponds to typecheck.tcIndex.
-func transformIndex(n *ir.IndexExpr) {
- assert(n.Type() != nil && n.Typecheck() == 1)
- n.X = implicitstar(n.X)
- l := n.X
- t := l.Type()
- if t.Kind() == types.TMAP {
- n.Index = assignconvfn(n.Index, t.Key())
- n.SetOp(ir.OINDEXMAP)
- // Set type to just the map value, not (value, bool). This is
- // different from types2, but fits the later stages of the
- // compiler better.
- n.SetType(t.Elem())
- n.Assigned = false
- }
-}
-
-// transformSlice transforms a slice operation. Corresponds to typecheck.tcSlice.
-func transformSlice(n *ir.SliceExpr) {
- assert(n.Type() != nil && n.Typecheck() == 1)
- l := n.X
- if l.Type().IsArray() {
- addr := typecheck.NodAddr(n.X)
- addr.SetImplicit(true)
- typed(types.NewPtr(n.X.Type()), addr)
- n.X = addr
- l = addr
- }
- t := l.Type()
- if t.IsString() {
- n.SetOp(ir.OSLICESTR)
- } else if t.IsPtr() && t.Elem().IsArray() {
- if n.Op().IsSlice3() {
- n.SetOp(ir.OSLICE3ARR)
- } else {
- n.SetOp(ir.OSLICEARR)
- }
- }
-}
-
-// Transformation functions for statements
-
-// Corresponds to typecheck.checkassign.
-func transformCheckAssign(stmt ir.Node, n ir.Node) {
- if n.Op() == ir.OINDEXMAP {
- n := n.(*ir.IndexExpr)
- n.Assigned = true
- return
- }
-}
-
-// Corresponds to typecheck.assign.
-func transformAssign(stmt ir.Node, lhs, rhs []ir.Node) {
- checkLHS := func(i int, typ *types.Type) {
- transformCheckAssign(stmt, lhs[i])
- }
-
- cr := len(rhs)
- if len(rhs) == 1 {
- if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
- cr = rtyp.NumFields()
- }
- }
-
- // x, ok = y
-assignOK:
- for len(lhs) == 2 && cr == 1 {
- stmt := stmt.(*ir.AssignListStmt)
- r := rhs[0]
-
- switch r.Op() {
- case ir.OINDEXMAP:
- stmt.SetOp(ir.OAS2MAPR)
- case ir.ORECV:
- stmt.SetOp(ir.OAS2RECV)
- case ir.ODOTTYPE:
- r := r.(*ir.TypeAssertExpr)
- stmt.SetOp(ir.OAS2DOTTYPE)
- r.SetOp(ir.ODOTTYPE2)
- case ir.ODYNAMICDOTTYPE:
- r := r.(*ir.DynamicTypeAssertExpr)
- stmt.SetOp(ir.OAS2DOTTYPE)
- r.SetOp(ir.ODYNAMICDOTTYPE2)
- default:
- break assignOK
- }
- checkLHS(0, r.Type())
- checkLHS(1, types.UntypedBool)
- t := lhs[0].Type()
- if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) {
- // This is a multi-value assignment (map, channel, or dot-type)
- // where the main result is converted to an interface during the
- // assignment. Normally, the needed CONVIFACE is not created
- // until (*orderState).as2ok(), because the AS2* ops and their
- // sub-ops are so tightly intertwined. But we need to create the
- // CONVIFACE now to enable dictionary lookups. So, assign the
- // results first to temps, so that we can manifest the CONVIFACE
- // in assigning the first temp to lhs[0]. If we added the
- // CONVIFACE into rhs[0] directly, we would break a lot of later
- // code that depends on the tight coupling between the AS2* ops
- // and their sub-ops. (Issue #50642).
- v := typecheck.Temp(rhs[0].Type())
- ok := typecheck.Temp(types.Types[types.TBOOL])
- as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r})
- as.Def = true
- as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v))
- as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok))
- as.SetTypecheck(1)
- // Change stmt to be a normal assignment of the temps to the final
- // left-hand-sides. We re-create the original multi-value assignment
- // so that it assigns to the temps and add it as an init of stmt.
- //
- // TODO: fix the order of evaluation, so that the lval of lhs[0]
- // is evaluated before rhs[0] (similar to problem in #50672).
- stmt.SetOp(ir.OAS2)
- stmt.PtrInit().Append(as)
- // assignconvfn inserts the CONVIFACE.
- stmt.Rhs = []ir.Node{assignconvfn(v, t), ok}
- }
- return
- }
-
- if len(lhs) != cr {
- for i := range lhs {
- checkLHS(i, nil)
- }
- return
- }
-
- // x,y,z = f()
- if cr > len(rhs) {
- stmt := stmt.(*ir.AssignListStmt)
- stmt.SetOp(ir.OAS2FUNC)
- r := rhs[0].(*ir.CallExpr)
- rtyp := r.Type()
-
- mismatched := false
- failed := false
- for i := range lhs {
- result := rtyp.Field(i).Type
- checkLHS(i, result)
-
- if lhs[i].Type() == nil || result == nil {
- failed = true
- } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
- mismatched = true
- }
- }
- if mismatched && !failed {
- typecheck.RewriteMultiValueCall(stmt, r)
- }
- return
- }
-
- for i, r := range rhs {
- checkLHS(i, r.Type())
- if lhs[i].Type() != nil {
- rhs[i] = assignconvfn(r, lhs[i].Type())
- }
- }
-}
-
-// Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls.
-func transformArgs(n ir.InitNode) {
- var list []ir.Node
- switch n := n.(type) {
- default:
- base.Fatalf("transformArgs %+v", n.Op())
- case *ir.CallExpr:
- list = n.Args
- if n.IsDDD {
- return
- }
- case *ir.ReturnStmt:
- list = n.Results
- }
- if len(list) != 1 {
- return
- }
-
- t := list[0].Type()
- if t == nil || !t.IsFuncArgStruct() {
- return
- }
-
- // Save n as n.Orig for fmt.go.
- if ir.Orig(n) == n {
- n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
- }
-
- // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
- typecheck.RewriteMultiValueCall(n, list[0])
-}
-
-// assignconvfn converts node n for assignment to type t. Corresponds to
-// typecheck.assignconvfn.
-func assignconvfn(n ir.Node, t *types.Type) ir.Node {
- if t.Kind() == types.TBLANK {
- return n
- }
-
- if n.Op() == ir.OPAREN {
- n = n.(*ir.ParenExpr).X
- }
-
- if types.IdenticalStrict(n.Type(), t) {
- return n
- }
-
- op, why := Assignop(n.Type(), t)
- if op == ir.OXXX {
- base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why)
- }
-
- r := ir.NewConvExpr(base.Pos, op, t, n)
- r.SetTypecheck(1)
- r.SetImplicit(true)
- return r
-}
-
-func Assignop(src, dst *types.Type) (ir.Op, string) {
- if src == dst {
- return ir.OCONVNOP, ""
- }
- if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
- return ir.OXXX, ""
- }
-
- // 1. src type is identical to dst (taking shapes into account)
- if types.Identical(src, dst) {
- // We already know from assignconvfn above that IdenticalStrict(src,
- // dst) is false, so the types are not exactly the same and one of
- // src or dst is a shape. If dst is an interface (which means src is
- // an interface too), we need a real OCONVIFACE op; otherwise we need a
- // OCONVNOP. See issue #48453.
- if dst.IsInterface() {
- return ir.OCONVIFACE, ""
- } else {
- return ir.OCONVNOP, ""
- }
- }
- return typecheck.Assignop1(src, dst)
-}
-
-// Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly
-// only. If convifaceOnly is true, we only do interface conversion. We use this to do
-// early insertion of CONVIFACE nodes during noder2, when the function or args may
-// have typeparams.
-func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
- var t *types.Type
- var i int
-
- lno := base.Pos
- defer func() { base.Pos = lno }()
-
- var n ir.Node
- if len(nl) == 1 {
- n = nl[0]
- }
-
- i = 0
- for _, tl := range tstruct.Fields().Slice() {
- t = tl.Type
- if tl.IsDDD() {
- if isddd {
- n = nl[i]
- ir.SetPos(n)
- if n.Type() != nil {
- nl[i] = assignconvfn(n, t)
- }
- return
- }
-
- // TODO(mdempsky): Make into ... call with implicit slice.
- for ; i < len(nl); i++ {
- n = nl[i]
- ir.SetPos(n)
- if n.Type() != nil {
- nl[i] = assignconvfn(n, t.Elem())
- }
- }
- return
- }
-
- n = nl[i]
- ir.SetPos(n)
- if n.Type() != nil {
- nl[i] = assignconvfn(n, t)
- }
- i++
- }
-}
-
-// transformSend transforms a send statement, converting the value to appropriate
-// type for the channel, as needed. Corresponds of typecheck.tcSend.
-func transformSend(n *ir.SendStmt) {
- n.Value = assignconvfn(n.Value, n.Chan.Type().Elem())
-}
-
-// transformReturn transforms a return node, by doing the needed assignments and
-// any necessary conversions. Corresponds to typecheck.tcReturn()
-func transformReturn(rs *ir.ReturnStmt) {
- transformArgs(rs)
- nl := rs.Results
- if ir.HasNamedResults(ir.CurFunc) && len(nl) == 0 {
- return
- }
-
- typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl)
-}
-
-// transformSelect transforms a select node, creating an assignment list as needed
-// for each case. Corresponds to typecheck.tcSelect().
-func transformSelect(sel *ir.SelectStmt) {
- for _, ncase := range sel.Cases {
- if ncase.Comm != nil {
- n := ncase.Comm
- oselrecv2 := func(dst, recv ir.Node, def bool) {
- selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
- if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n {
- // Must fix Defn for dst, since we are
- // completely changing the node.
- dst.(*ir.Name).Defn = selrecv
- }
- selrecv.Def = def
- selrecv.SetTypecheck(1)
- selrecv.SetInit(n.Init())
- ncase.Comm = selrecv
- }
- switch n.Op() {
- case ir.OAS:
- // convert x = <-c into x, _ = <-c
- // remove implicit conversions; the eventual assignment
- // will reintroduce them.
- n := n.(*ir.AssignStmt)
- if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
- r := r.(*ir.ConvExpr)
- if r.Implicit() {
- n.Y = r.X
- }
- }
- oselrecv2(n.X, n.Y, n.Def)
-
- case ir.OAS2RECV:
- n := n.(*ir.AssignListStmt)
- n.SetOp(ir.OSELRECV2)
-
- case ir.ORECV:
- // convert <-c into _, _ = <-c
- n := n.(*ir.UnaryExpr)
- oselrecv2(ir.BlankNode, n, false)
-
- case ir.OSEND:
- break
- }
- }
- }
-}
-
-// transformAsOp transforms an AssignOp statement. Corresponds to OASOP case in
-// typecheck1.
-func transformAsOp(n *ir.AssignOpStmt) {
- transformCheckAssign(n, n.X)
-}
-
-// transformDot transforms an OXDOT (or ODOT) or ODOT, ODOTPTR, ODOTMETH,
-// ODOTINTER, or OMETHVALUE, as appropriate. It adds in extra nodes as needed to
-// access embedded fields. Corresponds to typecheck.tcDot.
-func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node {
- assert(n.Type() != nil && n.Typecheck() == 1)
- if n.Op() == ir.OXDOT {
- n = typecheck.AddImplicitDots(n)
- n.SetOp(ir.ODOT)
-
- // Set the Selection field and typecheck flag for any new ODOT nodes
- // added by AddImplicitDots(), and also transform to ODOTPTR if
- // needed. Equivalent to 'n.X = typecheck(n.X, ctxExpr|ctxType)' in
- // tcDot.
- for n1 := n; n1.X.Op() == ir.ODOT; {
- n1 = n1.X.(*ir.SelectorExpr)
- if !n1.Implicit() {
- break
- }
- t1 := n1.X.Type()
- if t1.IsPtr() && !t1.Elem().IsInterface() {
- t1 = t1.Elem()
- n1.SetOp(ir.ODOTPTR)
- }
- typecheck.Lookdot(n1, t1, 0)
- n1.SetTypecheck(1)
- }
- }
-
- t := n.X.Type()
-
- if n.X.Op() == ir.OTYPE {
- return transformMethodExpr(n)
- }
-
- if t.IsPtr() && !t.Elem().IsInterface() {
- t = t.Elem()
- n.SetOp(ir.ODOTPTR)
- }
-
- f := typecheck.Lookdot(n, t, 0)
- assert(f != nil)
-
- if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall {
- n.SetOp(ir.OMETHVALUE)
- // This converts a method type to a function type. See issue 47775.
- n.SetType(typecheck.NewMethodType(n.Type(), nil))
- }
- return n
-}
-
-// Corresponds to typecheck.typecheckMethodExpr.
-func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
- t := n.X.Type()
-
- // Compute the method set for t.
- var ms *types.Fields
- if t.IsInterface() {
- ms = t.AllMethods()
- } else {
- mt := types.ReceiverBaseType(t)
- typecheck.CalcMethods(mt)
- ms = mt.AllMethods()
-
- // The method expression T.m requires a wrapper when T
- // is different from m's declared receiver type. We
- // normally generate these wrappers while writing out
- // runtime type descriptors, which is always done for
- // types declared at package scope. However, we need
- // to make sure to generate wrappers for anonymous
- // receiver types too.
- if mt.Sym() == nil {
- typecheck.NeedRuntimeType(t)
- }
- }
-
- s := n.Sel
- m := typecheck.Lookdot1(n, s, t, ms, 0)
- if !t.HasShape() {
- // It's OK to not find the method if t is instantiated by shape types,
- // because we will use the methods on the generic type anyway.
- assert(m != nil)
- }
-
- n.SetOp(ir.OMETHEXPR)
- n.Selection = m
- n.SetType(typecheck.NewMethodType(m.Type, n.X.Type()))
- return n
-}
-
-// Corresponds to typecheck.tcAppend.
-func transformAppend(n *ir.CallExpr) ir.Node {
- transformArgs(n)
- args := n.Args
- t := args[0].Type()
- assert(t.IsSlice())
-
- if n.IsDDD {
- // assignconvfn is of args[1] not required here, as the
- // types of args[0] and args[1] don't need to match
- // (They will both have an underlying type which are
- // slices of identical base types, or be []byte and string.)
- // See issue 53888.
- return n
- }
-
- as := args[1:]
- for i, n := range as {
- assert(n.Type() != nil)
- as[i] = assignconvfn(n, t.Elem())
- }
- return n
-}
-
-// Corresponds to typecheck.tcComplex.
-func transformComplex(n *ir.BinaryExpr) ir.Node {
- l := n.X
- r := n.Y
-
- assert(types.Identical(l.Type(), r.Type()))
-
- var t *types.Type
- switch l.Type().Kind() {
- case types.TFLOAT32:
- t = types.Types[types.TCOMPLEX64]
- case types.TFLOAT64:
- t = types.Types[types.TCOMPLEX128]
- default:
- panic(fmt.Sprintf("transformComplex: unexpected type %v", l.Type()))
- }
-
- // Must set the type here for generics, because this can't be determined
- // by substitution of the generic types.
- typed(t, n)
- return n
-}
-
-// Corresponds to typecheck.tcDelete.
-func transformDelete(n *ir.CallExpr) ir.Node {
- transformArgs(n)
- args := n.Args
- assert(len(args) == 2)
-
- l := args[0]
- r := args[1]
-
- args[1] = assignconvfn(r, l.Type().Key())
- return n
-}
-
-// Corresponds to typecheck.tcMake.
-func transformMake(n *ir.CallExpr) ir.Node {
- args := n.Args
-
- n.Args = nil
- l := args[0]
- t := l.Type()
- assert(t != nil)
-
- i := 1
- var nn ir.Node
- switch t.Kind() {
- case types.TSLICE:
- l = args[i]
- i++
- var r ir.Node
- if i < len(args) {
- r = args[i]
- i++
- }
- nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r)
-
- case types.TMAP:
- if i < len(args) {
- l = args[i]
- i++
- } else {
- l = ir.NewInt(0)
- }
- nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil)
- nn.SetEsc(n.Esc())
-
- case types.TCHAN:
- l = nil
- if i < len(args) {
- l = args[i]
- i++
- } else {
- l = ir.NewInt(0)
- }
- nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil)
- default:
- panic(fmt.Sprintf("transformMake: unexpected type %v", t))
- }
-
- assert(i == len(args))
- typed(n.Type(), nn)
- return nn
-}
-
-// Corresponds to typecheck.tcPanic.
-func transformPanic(n *ir.UnaryExpr) ir.Node {
- n.X = assignconvfn(n.X, types.Types[types.TINTER])
- return n
-}
-
-// Corresponds to typecheck.tcPrint.
-func transformPrint(n *ir.CallExpr) ir.Node {
- transformArgs(n)
- return n
-}
-
-// Corresponds to typecheck.tcRealImag.
-func transformRealImag(n *ir.UnaryExpr) ir.Node {
- l := n.X
- var t *types.Type
-
- // Determine result type.
- switch l.Type().Kind() {
- case types.TCOMPLEX64:
- t = types.Types[types.TFLOAT32]
- case types.TCOMPLEX128:
- t = types.Types[types.TFLOAT64]
- default:
- panic(fmt.Sprintf("transformRealImag: unexpected type %v", l.Type()))
- }
-
- // Must set the type here for generics, because this can't be determined
- // by substitution of the generic types.
- typed(t, n)
- return n
-}
-
-// Corresponds to typecheck.tcLenCap.
-func transformLenCap(n *ir.UnaryExpr) ir.Node {
- n.X = implicitstar(n.X)
- return n
-}
-
-// Corresponds to Builtin part of tcCall.
-func transformBuiltin(n *ir.CallExpr) ir.Node {
- // n.Type() can be nil for builtins with no return value
- assert(n.Typecheck() == 1)
- fun := n.X.(*ir.Name)
- op := fun.BuiltinOp
-
- switch op {
- case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
- n.SetOp(op)
- n.X = nil
- switch op {
- case ir.OAPPEND:
- return transformAppend(n)
- case ir.ODELETE:
- return transformDelete(n)
- case ir.OMAKE:
- return transformMake(n)
- case ir.OPRINT, ir.OPRINTN:
- return transformPrint(n)
- case ir.ORECOVER:
- // nothing more to do
- return n
- }
-
- case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL:
- transformArgs(n)
- fallthrough
-
- case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRINGDATA:
- u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0])
- u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init
- switch op {
- case ir.OCAP, ir.OLEN:
- return transformLenCap(u1.(*ir.UnaryExpr))
- case ir.OREAL, ir.OIMAG:
- return transformRealImag(u1.(*ir.UnaryExpr))
- case ir.OPANIC:
- return transformPanic(u1.(*ir.UnaryExpr))
- case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
- // This corresponds to the EvalConst() call near end of typecheck().
- return typecheck.EvalConst(u1)
- case ir.OCLOSE, ir.ONEW, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
- // nothing more to do
- return u1
- }
-
- case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
- transformArgs(n)
- b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1])
- n1 := typed(n.Type(), ir.InitExpr(n.Init(), b))
- if op != ir.OCOMPLEX {
- // nothing more to do
- return n1
- }
- return transformComplex(n1.(*ir.BinaryExpr))
-
- default:
- panic(fmt.Sprintf("transformBuiltin: unexpected op %v", op))
- }
-
- return n
-}
-
-func hasKeys(l ir.Nodes) bool {
- for _, n := range l {
- if n.Op() == ir.OKEY || n.Op() == ir.OSTRUCTKEY {
- return true
- }
- }
- return false
-}
-
-// transformArrayLit runs assignconvfn on each array element and returns the
-// length of the slice/array that is needed to hold all the array keys/indexes
-// (one more than the highest index). Corresponds to typecheck.typecheckarraylit.
-func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64 {
- var key, length int64
- for i, elt := range elts {
- ir.SetPos(elt)
- r := elts[i]
- var kv *ir.KeyExpr
- if elt.Op() == ir.OKEY {
- elt := elt.(*ir.KeyExpr)
- key = typecheck.IndexConst(elt.Key)
- assert(key >= 0)
- kv = elt
- r = elt.Value
- }
-
- r = assignconvfn(r, elemType)
- if kv != nil {
- kv.Value = r
- } else {
- elts[i] = r
- }
-
- key++
- if key > length {
- length = key
- }
- }
-
- return length
-}
-
-// transformCompLit transforms n to an OARRAYLIT, OSLICELIT, OMAPLIT, or
-// OSTRUCTLIT node, with any needed conversions. Corresponds to
-// typecheck.tcCompLit (and includes parts corresponding to tcStructLitKey).
-func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
- assert(n.Type() != nil && n.Typecheck() == 1)
- lno := base.Pos
- defer func() {
- base.Pos = lno
- }()
-
- // Save original node (including n.Right)
- n.SetOrig(ir.Copy(n))
-
- ir.SetPos(n)
-
- t := n.Type()
-
- switch t.Kind() {
- default:
- base.Fatalf("transformCompLit %v", t.Kind())
-
- case types.TARRAY:
- transformArrayLit(t.Elem(), t.NumElem(), n.List)
- n.SetOp(ir.OARRAYLIT)
-
- case types.TSLICE:
- length := transformArrayLit(t.Elem(), -1, n.List)
- n.SetOp(ir.OSLICELIT)
- n.Len = length
-
- case types.TMAP:
- for _, l := range n.List {
- ir.SetPos(l)
- assert(l.Op() == ir.OKEY)
- l := l.(*ir.KeyExpr)
-
- r := l.Key
- l.Key = assignconvfn(r, t.Key())
-
- r = l.Value
- l.Value = assignconvfn(r, t.Elem())
- }
-
- n.SetOp(ir.OMAPLIT)
-
- case types.TSTRUCT:
- // Need valid field offsets for Xoffset below.
- types.CalcSize(t)
-
- if len(n.List) != 0 && !hasKeys(n.List) {
- // simple list of values
- ls := n.List
- for i, n1 := range ls {
- ir.SetPos(n1)
-
- f := t.Field(i)
- n1 = assignconvfn(n1, f.Type)
- ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1)
- }
- assert(len(ls) >= t.NumFields())
- } else {
- // keyed list
- ls := n.List
- for i, l := range ls {
- ir.SetPos(l)
-
- kv := l.(*ir.KeyExpr)
- key := kv.Key
-
- s := key.Sym()
- if types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
- // Exported field names should always have
- // local pkg. We only need to do this
- // adjustment for generic functions that are
- // being transformed after being imported
- // from another package.
- s = typecheck.Lookup(s.Name)
- }
-
- // An OXDOT uses the Sym field to hold
- // the field to the right of the dot,
- // so s will be non-nil, but an OXDOT
- // is never a valid struct literal key.
- assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank()))
-
- f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0)
- l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value)
- ls[i] = l
-
- l.Value = assignconvfn(l.Value, f.Type)
- }
- }
-
- n.SetOp(ir.OSTRUCTLIT)
- }
-
- return n
-}
-
-// transformAddr corresponds to typecheck.tcAddr.
-func transformAddr(n *ir.AddrExpr) {
- switch n.X.Op() {
- case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
- n.SetOp(ir.OPTRLIT)
- }
-}
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],
+++ /dev/null
-// 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
-}
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()
package typecheck
-import "cmd/compile/internal/types"
-
-// ----------------------------------------------------------------------------
-// Export format
-
// Tags. Must be < 0.
const (
// Objects
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
-}
+++ /dev/null
-// 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()
-}
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 {
package typecheck
import (
- "bytes"
- "encoding/binary"
- "fmt"
"go/constant"
- "io"
- "math/big"
- "sort"
"strconv"
"strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
- "cmd/internal/goobj"
- "cmd/internal/notsha256"
- "cmd/internal/src"
-)
-
-// Current indexed export format version. Increase with each format change.
-// 0: Go1.11 encoding
-// 1: added column details to Pos
-// 2: added information for generic function/types. The export of non-generic
-// functions/types remains largely backward-compatible. Breaking changes include:
-// - a 'kind' byte is added to constant values
-const (
- iexportVersionGo1_11 = 0
- iexportVersionPosCol = 1
- iexportVersionGenerics = 2
- iexportVersionGo1_18 = 2
-
- iexportVersionCurrent = 2
)
// predeclReserved is the number of type offsets reserved for types
magic = 0x6742937dc293105
)
-// WriteExports writes the indexed export format to out. If extensions
-// is true, then the compiler-only extensions are included.
-func WriteExports(out io.Writer, extensions bool) {
- if extensions {
- // If we're exporting inline bodies, invoke the crawler to mark
- // which bodies to include.
- crawlExports(Target.Exports)
- }
-
- p := iexporter{
- allPkgs: map[*types.Pkg]bool{},
- stringIndex: map[string]uint64{},
- declIndex: map[*types.Sym]uint64{},
- inlineIndex: map[*types.Sym]uint64{},
- typIndex: map[*types.Type]uint64{},
- extensions: extensions,
- }
-
- for i, pt := range predeclared() {
- p.typIndex[pt] = uint64(i)
- }
- if len(p.typIndex) > predeclReserved {
- base.Fatalf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)
- }
-
- // Initialize work queue with exported declarations.
- for _, n := range Target.Exports {
- p.pushDecl(n)
- }
-
- // Loop until no more work. We use a queue because while
- // writing out inline bodies, we may discover additional
- // declarations that are needed.
- for !p.declTodo.Empty() {
- p.doDecl(p.declTodo.PopLeft())
- }
-
- // Append indices to data0 section.
- dataLen := uint64(p.data0.Len())
- w := p.newWriter()
- w.writeIndex(p.declIndex, true)
- w.writeIndex(p.inlineIndex, false)
- w.flush()
-
- if *base.Flag.LowerV {
- fmt.Printf("export: hdr strings %v, data %v, index %v\n", p.strings.Len(), dataLen, p.data0.Len())
- }
-
- // Assemble header.
- var hdr intWriter
- hdr.WriteByte('i')
- hdr.uint64(iexportVersionCurrent)
- hdr.uint64(uint64(p.strings.Len()))
- hdr.uint64(dataLen)
-
- // Flush output.
- h := notsha256.New()
- wr := io.MultiWriter(out, h)
- io.Copy(wr, &hdr)
- io.Copy(wr, &p.strings)
- io.Copy(wr, &p.data0)
-
- // Add fingerprint (used by linker object file).
- // Attach this to the end, so tools (e.g. gcimporter) don't care.
- copy(base.Ctxt.Fingerprint[:], h.Sum(nil)[:])
- out.Write(base.Ctxt.Fingerprint[:])
-}
-
-// writeIndex writes out a symbol index. mainIndex indicates whether
-// we're writing out the main index, which is also read by
-// non-compiler tools and includes a complete package description
-// (i.e., name and height).
-func (w *exportWriter) writeIndex(index map[*types.Sym]uint64, mainIndex bool) {
- // Build a map from packages to symbols from that package.
- pkgSyms := map[*types.Pkg][]*types.Sym{}
-
- // For the main index, make sure to include every package that
- // we reference, even if we're not exporting (or reexporting)
- // any symbols from it.
- if mainIndex {
- pkgSyms[types.LocalPkg] = nil
- for pkg := range w.p.allPkgs {
- pkgSyms[pkg] = nil
- }
- }
-
- // Group symbols by package.
- for sym := range index {
- pkgSyms[sym.Pkg] = append(pkgSyms[sym.Pkg], sym)
- }
-
- // Sort packages by path.
- var pkgs []*types.Pkg
- for pkg := range pkgSyms {
- pkgs = append(pkgs, pkg)
- }
- sort.Slice(pkgs, func(i, j int) bool {
- return exportPath(pkgs[i]) < exportPath(pkgs[j])
- })
- if mainIndex {
- base.Assertf(pkgs[0] == types.LocalPkg, "LocalPkg must be first")
- }
-
- w.uint64(uint64(len(pkgs)))
- for _, pkg := range pkgs {
- w.string(exportPath(pkg))
- if mainIndex {
- w.string(pkg.Name)
- w.uint64(0) // was package height, but not necessary anymore.
- }
-
- // Sort symbols within a package by name.
- syms := pkgSyms[pkg]
- sort.Slice(syms, func(i, j int) bool {
- return syms[i].Name < syms[j].Name
- })
-
- w.uint64(uint64(len(syms)))
- for _, sym := range syms {
- w.string(sym.Name)
- w.uint64(index[sym])
- }
- }
-}
-
-type iexporter struct {
- // allPkgs tracks all packages that have been referenced by
- // the export data, so we can ensure to include them in the
- // main index.
- allPkgs map[*types.Pkg]bool
-
- declTodo ir.NameQueue
-
- strings intWriter
- stringIndex map[string]uint64
-
- data0 intWriter
- declIndex map[*types.Sym]uint64
- inlineIndex map[*types.Sym]uint64
- typIndex map[*types.Type]uint64
-
- extensions bool
-}
-
-// stringOff returns the offset of s within the string section.
-// If not already present, it's added to the end.
-func (p *iexporter) stringOff(s string) uint64 {
- off, ok := p.stringIndex[s]
- if !ok {
- off = uint64(p.strings.Len())
- p.stringIndex[s] = off
-
- if *base.Flag.LowerV {
- fmt.Printf("export: str %v %.40q\n", off, s)
- }
-
- p.strings.uint64(uint64(len(s)))
- p.strings.WriteString(s)
- }
- return off
-}
-
-// pushDecl adds n to the declaration work queue, if not already present.
-func (p *iexporter) pushDecl(n *ir.Name) {
- if n.Sym() == nil || n.Sym().Def != n && n.Op() != ir.OTYPE {
- base.Fatalf("weird Sym: %v, %v", n, n.Sym())
- }
-
- // Don't export predeclared declarations.
- if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == types.UnsafePkg {
- return
- }
-
- if _, ok := p.declIndex[n.Sym()]; ok {
- return
- }
-
- p.declIndex[n.Sym()] = ^uint64(0) // mark n present in work queue
- p.declTodo.PushRight(n)
-}
-
-// exportWriter handles writing out individual data section chunks.
-type exportWriter struct {
- p *iexporter
-
- data intWriter
- currPkg *types.Pkg
- prevFile string
- prevLine int64
- prevColumn int64
-
- // dclIndex maps function-scoped declarations to an int used to refer to
- // them later in the function. For local variables/params, the int is
- // non-negative and in order of the appearance in the Func's Dcl list. For
- // closure variables, the index is negative starting at -2.
- dclIndex map[*ir.Name]int
- maxDclIndex int
- maxClosureVarIndex int
-}
-
-func (p *iexporter) doDecl(n *ir.Name) {
- w := p.newWriter()
- w.setPkg(n.Sym().Pkg, false)
-
- switch n.Op() {
- case ir.ONAME:
- switch n.Class {
- case ir.PEXTERN:
- // Variable.
- w.tag('V')
- w.pos(n.Pos())
- w.typ(n.Type())
- if w.p.extensions {
- w.varExt(n)
- }
-
- case ir.PFUNC:
- if ir.IsMethod(n) {
- base.Fatalf("unexpected method: %v", n)
- }
-
- // Function.
- if n.Type().TParams().NumFields() == 0 {
- w.tag('F')
- } else {
- w.tag('G')
- }
- w.pos(n.Pos())
- // The tparam list of the function type is the
- // declaration of the type params. So, write out the type
- // params right now. Then those type params will be
- // referenced via their type offset (via typOff) in all
- // other places in the signature and function that they
- // are used.
- if n.Type().TParams().NumFields() > 0 {
- w.tparamList(n.Type().TParams().FieldSlice())
- }
- w.signature(n.Type())
- if w.p.extensions {
- w.funcExt(n)
- }
-
- default:
- base.Fatalf("unexpected class: %v, %v", n, n.Class)
- }
-
- case ir.OLITERAL:
- // TODO(mdempsky): Extend check to all declarations.
- if n.Typecheck() == 0 {
- base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
- }
-
- // Constant.
- w.tag('C')
- w.pos(n.Pos())
- w.value(n.Type(), n.Val())
- if w.p.extensions {
- w.constExt(n)
- }
-
- case ir.OTYPE:
- if n.Type().IsTypeParam() && n.Type().Underlying() == n.Type() {
- // Even though it has local scope, a typeparam requires a
- // declaration via its package and unique name, because it
- // may be referenced within its type bound during its own
- // definition.
- w.tag('P')
- // A typeparam has a name, and has a type bound rather
- // than an underlying type.
- w.pos(n.Pos())
- if iexportVersionCurrent >= iexportVersionGo1_18 {
- implicit := n.Type().Bound().IsImplicit()
- w.bool(implicit)
- }
- w.typ(n.Type().Bound())
- break
- }
-
- if n.Alias() {
- // Alias.
- w.tag('A')
- w.pos(n.Pos())
- w.typ(n.Type())
- break
- }
-
- // Defined type.
- if len(n.Type().RParams()) == 0 {
- w.tag('T')
- } else {
- w.tag('U')
- }
- w.pos(n.Pos())
-
- if len(n.Type().RParams()) > 0 {
- // Export type parameters, if any, needed for this type
- w.typeList(n.Type().RParams())
- }
-
- underlying := n.Type().Underlying()
- if underlying == types.ErrorType.Underlying() {
- // For "type T error", use error as the
- // underlying type instead of error's own
- // underlying anonymous interface. This
- // ensures consistency with how importers may
- // declare error (e.g., go/types uses nil Pkg
- // for predeclared objects).
- underlying = types.ErrorType
- }
- if underlying == types.ComparableType.Underlying() {
- // Do same for ComparableType as for ErrorType.
- underlying = types.ComparableType
- }
- if underlying == types.AnyType.Underlying() {
- // Do same for AnyType as for ErrorType.
- underlying = types.AnyType
- }
- w.typ(underlying)
-
- t := n.Type()
- if t.IsInterface() {
- if w.p.extensions {
- w.typeExt(t)
- }
- break
- }
-
- methods := t.Methods().Slice()
- w.uint64(uint64(len(methods)))
- for _, m := range methods {
- w.pos(m.Pos)
- w.selector(m.Sym)
- w.param(m.Type.Recv())
- w.signature(m.Type)
- }
-
- if w.p.extensions {
- w.typeExt(t)
- for _, m := range methods {
- w.methExt(m)
- }
- }
-
- default:
- base.Fatalf("unexpected node: %v", n)
- }
-
- w.finish("dcl", p.declIndex, n.Sym())
-}
-
-func (w *exportWriter) tag(tag byte) {
- w.data.WriteByte(tag)
-}
-
-func (w *exportWriter) finish(what string, index map[*types.Sym]uint64, sym *types.Sym) {
- off := w.flush()
- if *base.Flag.LowerV {
- fmt.Printf("export: %v %v %v\n", what, off, sym)
- }
- index[sym] = off
-}
-
-func (p *iexporter) doInline(f *ir.Name) {
- w := p.newWriter()
- w.setPkg(fnpkg(f), false)
-
- w.dclIndex = make(map[*ir.Name]int, len(f.Func.Inl.Dcl))
- w.funcBody(f.Func)
-
- w.finish("inl", p.inlineIndex, f.Sym())
-}
-
-func (w *exportWriter) pos(pos src.XPos) {
- p := base.Ctxt.PosTable.Pos(pos)
- file := p.Base().AbsFilename()
- line := int64(p.RelLine())
- column := int64(p.RelCol())
-
- // Encode position relative to the last position: column
- // delta, then line delta, then file name. We reserve the
- // bottom bit of the column and line deltas to encode whether
- // the remaining fields are present.
- //
- // Note: Because data objects may be read out of order (or not
- // at all), we can only apply delta encoding within a single
- // object. This is handled implicitly by tracking prevFile,
- // prevLine, and prevColumn as fields of exportWriter.
-
- deltaColumn := (column - w.prevColumn) << 1
- deltaLine := (line - w.prevLine) << 1
-
- if file != w.prevFile {
- deltaLine |= 1
- }
- if deltaLine != 0 {
- deltaColumn |= 1
- }
-
- w.int64(deltaColumn)
- if deltaColumn&1 != 0 {
- w.int64(deltaLine)
- if deltaLine&1 != 0 {
- w.string(file)
- }
- }
-
- w.prevFile = file
- w.prevLine = line
- w.prevColumn = column
-}
-
-func (w *exportWriter) pkg(pkg *types.Pkg) {
- // TODO(mdempsky): Add flag to types.Pkg to mark pseudo-packages.
- if pkg == ir.Pkgs.Go {
- base.Fatalf("export of pseudo-package: %q", pkg.Path)
- }
-
- // Ensure any referenced packages are declared in the main index.
- w.p.allPkgs[pkg] = true
-
- w.string(exportPath(pkg))
-}
-
// exportPath returns the path for pkg as it appears in the iexport
// file format. For historical reasons (before cmd/compile required
// the -p flag), the local package is represented as the empty string,
return pkg.Path
}
-func (w *exportWriter) qualifiedIdent(n *ir.Name) {
- // Ensure any referenced declarations are written out too.
- w.p.pushDecl(n)
-
- s := n.Sym()
- w.string(s.Name)
- w.pkg(s.Pkg)
-}
-
const blankMarker = "$"
// TparamExportName creates a unique name for type param in a method or a generic
return name
}
-func (w *exportWriter) selector(s *types.Sym) {
- if w.currPkg == nil {
- base.Fatalf("missing currPkg")
- }
-
- // If the selector being written is unexported, it comes with a package qualifier.
- // If the selector being written is exported, it is not package-qualified.
- // See the spec: https://golang.org/ref/spec#Uniqueness_of_identifiers
- // As an optimization, we don't actually write the package every time - instead we
- // call setPkg before a group of selectors (all of which must have the same package qualifier).
- pkg := w.currPkg
- if types.IsExported(s.Name) {
- pkg = types.LocalPkg
- }
- if s.Pkg != pkg {
- base.Fatalf("package mismatch in selector: %v in package %q, but want %q", s, s.Pkg.Path, pkg.Path)
- }
-
- w.string(s.Name)
-}
-
-func (w *exportWriter) typ(t *types.Type) {
- w.data.uint64(w.p.typOff(t))
-}
-
-// The "exotic" functions in this section encode a wider range of
-// items than the standard encoding functions above. These include
-// types that do not appear in declarations, only in code, such as
-// method types. These methods need to be separate from the standard
-// encoding functions because we don't want to modify the encoding
-// generated by the standard functions (because that exported
-// information is read by tools besides the compiler).
-
-// exoticType exports a type to the writer.
-func (w *exportWriter) exoticType(t *types.Type) {
- switch {
- case t == nil:
- // Calls-as-statements have no type.
- w.data.uint64(exoticTypeNil)
- case t.IsStruct() && t.StructType().Funarg != types.FunargNone:
- // These are weird structs for representing tuples of types returned
- // by multi-return functions.
- // They don't fit the standard struct type mold. For instance,
- // they don't have any package info.
- w.data.uint64(exoticTypeTuple)
- w.uint64(uint64(t.StructType().Funarg))
- w.uint64(uint64(t.NumFields()))
- for _, f := range t.FieldSlice() {
- w.pos(f.Pos)
- s := f.Sym
- if s == nil {
- w.uint64(0)
- } else if s.Pkg == nil {
- w.uint64(exoticTypeSymNoPkg)
- w.string(s.Name)
- } else {
- w.uint64(exoticTypeSymWithPkg)
- w.pkg(s.Pkg)
- w.string(s.Name)
- }
- w.typ(f.Type)
- if f.Embedded != 0 || f.Note != "" {
- panic("extra info in funarg struct field")
- }
- }
- case t.Kind() == types.TFUNC && t.Recv() != nil:
- w.data.uint64(exoticTypeRecv)
- // interface method types have a fake receiver type.
- isFakeRecv := t.Recv().Type == types.FakeRecvType()
- w.bool(isFakeRecv)
- if !isFakeRecv {
- w.exoticParam(t.Recv())
- }
- w.exoticSignature(t)
-
- default:
- // A regular type.
- w.data.uint64(exoticTypeRegular)
- w.typ(t)
- }
-}
-
-const (
- exoticTypeNil = iota
- exoticTypeTuple
- exoticTypeRecv
- exoticTypeRegular
-)
-const (
- exoticTypeSymNil = iota
- exoticTypeSymNoPkg
- exoticTypeSymWithPkg
-)
-
-// Export a selector, but one whose package may not match
-// the package being compiled. This is a separate function
-// because the standard selector() serialization format is fixed
-// by the go/types reader. This one can only be used during
-// inline/generic body exporting.
-func (w *exportWriter) exoticSelector(s *types.Sym) {
- pkg := w.currPkg
- if types.IsExported(s.Name) {
- pkg = types.LocalPkg
- }
-
- w.string(s.Name)
- if s.Pkg == pkg {
- w.uint64(0)
- } else {
- w.uint64(1)
- w.pkg(s.Pkg)
- }
-}
-
-func (w *exportWriter) exoticSignature(t *types.Type) {
- hasPkg := t.Pkg() != nil
- w.bool(hasPkg)
- if hasPkg {
- w.pkg(t.Pkg())
- }
- w.exoticParamList(t.Params().FieldSlice())
- w.exoticParamList(t.Results().FieldSlice())
-}
-
-func (w *exportWriter) exoticParamList(fs []*types.Field) {
- w.uint64(uint64(len(fs)))
- for _, f := range fs {
- w.exoticParam(f)
- }
-
-}
-func (w *exportWriter) exoticParam(f *types.Field) {
- w.pos(f.Pos)
- w.exoticSym(f.Sym)
- w.uint64(uint64(f.Offset))
- w.exoticType(f.Type)
- w.bool(f.IsDDD())
-}
-
-func (w *exportWriter) exoticField(f *types.Field) {
- w.pos(f.Pos)
- w.exoticSym(f.Sym)
- w.uint64(uint64(f.Offset))
- w.exoticType(f.Type)
- w.string(f.Note)
-}
-
-func (w *exportWriter) exoticSym(s *types.Sym) {
- if s == nil {
- w.string("")
- return
- }
- if s.Name == "" {
- base.Fatalf("empty symbol name")
- }
- w.string(s.Name)
- if !types.IsExported(s.Name) {
- w.pkg(s.Pkg)
- }
-}
-
-func (p *iexporter) newWriter() *exportWriter {
- return &exportWriter{p: p}
-}
-
-func (w *exportWriter) flush() uint64 {
- off := uint64(w.p.data0.Len())
- io.Copy(&w.p.data0, &w.data)
- return off
-}
-
-func (p *iexporter) typOff(t *types.Type) uint64 {
- off, ok := p.typIndex[t]
- if !ok {
- w := p.newWriter()
- w.doTyp(t)
- rawOff := w.flush()
- if *base.Flag.LowerV {
- fmt.Printf("export: typ %v %v\n", rawOff, t)
- }
- off = predeclReserved + rawOff
- p.typIndex[t] = off
- }
- return off
-}
-
-func (w *exportWriter) startType(k itag) {
- w.data.uint64(uint64(k))
-}
-
-func (w *exportWriter) doTyp(t *types.Type) {
- s := t.Sym()
- if s != nil && t.OrigType() != nil {
- // This is an instantiated type - could be a re-instantiation like
- // Value[T2] or a full instantiation like Value[int].
- if strings.Index(s.Name, "[") < 0 {
- base.Fatalf("incorrect name for instantiated type")
- }
- w.startType(instanceType)
- w.pos(t.Pos())
- // Export the type arguments for the instantiated type. The
- // instantiated type could be in a method header (e.g. "func (v
- // *Value[T2]) set (...) { ... }"), so the type args are "new"
- // typeparams. Or the instantiated type could be in a
- // function/method body, so the type args are either concrete
- // types or existing typeparams from the function/method header.
- w.typeList(t.RParams())
- // Export a reference to the base type.
- baseType := t.OrigType()
- w.typ(baseType)
- return
- }
-
- // The 't.Underlying() == t' check is to confirm this is a base typeparam
- // type, rather than a defined type with typeparam underlying type, like:
- // type orderedAbs[T any] T
- if t.IsTypeParam() && t.Underlying() == t {
- if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg {
- base.Fatalf("builtin type missing from typIndex: %v", t)
- }
- // Write out the first use of a type param as a qualified ident.
- // This will force a "declaration" of the type param.
- w.startType(typeParamType)
- w.qualifiedIdent(t.Obj().(*ir.Name))
- return
- }
-
- if s != nil {
- if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg {
- base.Fatalf("builtin type missing from typIndex: %v", t)
- }
-
- w.startType(definedType)
- w.qualifiedIdent(t.Obj().(*ir.Name))
- return
- }
-
- switch t.Kind() {
- case types.TPTR:
- w.startType(pointerType)
- w.typ(t.Elem())
-
- case types.TSLICE:
- w.startType(sliceType)
- w.typ(t.Elem())
-
- case types.TARRAY:
- w.startType(arrayType)
- w.uint64(uint64(t.NumElem()))
- w.typ(t.Elem())
-
- case types.TCHAN:
- w.startType(chanType)
- w.uint64(uint64(t.ChanDir()))
- w.typ(t.Elem())
-
- case types.TMAP:
- w.startType(mapType)
- w.typ(t.Key())
- w.typ(t.Elem())
-
- case types.TFUNC:
- w.startType(signatureType)
- w.setPkg(t.Pkg(), true)
- w.signature(t)
-
- case types.TSTRUCT:
- w.startType(structType)
- w.setPkg(t.Pkg(), true)
-
- w.uint64(uint64(t.NumFields()))
- for _, f := range t.FieldSlice() {
- w.pos(f.Pos)
- w.selector(f.Sym)
- w.typ(f.Type)
- w.bool(f.Embedded != 0)
- w.string(f.Note)
- }
-
- case types.TINTER:
- var embeddeds, methods []*types.Field
- for _, m := range t.Methods().Slice() {
- if m.Sym != nil {
- methods = append(methods, m)
- } else {
- embeddeds = append(embeddeds, m)
- }
- }
-
- w.startType(interfaceType)
- w.setPkg(t.Pkg(), true)
-
- w.uint64(uint64(len(embeddeds)))
- for _, f := range embeddeds {
- w.pos(f.Pos)
- w.typ(f.Type)
- }
-
- w.uint64(uint64(len(methods)))
- for _, f := range methods {
- w.pos(f.Pos)
- w.selector(f.Sym)
- w.signature(f.Type)
- }
-
- case types.TUNION:
- // TODO(danscales): possibly put out the tilde bools in more
- // compact form.
- w.startType(unionType)
- nt := t.NumTerms()
- w.uint64(uint64(nt))
- for i := 0; i < nt; i++ {
- typ, tilde := t.Term(i)
- w.bool(tilde)
- w.typ(typ)
- }
-
- default:
- base.Fatalf("unexpected type: %v", t)
- }
-}
-
-func (w *exportWriter) setPkg(pkg *types.Pkg, write bool) {
- if pkg == types.NoPkg {
- base.Fatalf("missing pkg")
- }
-
- if write {
- w.pkg(pkg)
- }
-
- w.currPkg = pkg
-}
-
-func (w *exportWriter) signature(t *types.Type) {
- w.paramList(t.Params().FieldSlice())
- w.paramList(t.Results().FieldSlice())
- if n := t.Params().NumFields(); n > 0 {
- w.bool(t.Params().Field(n - 1).IsDDD())
- }
-}
-
-func (w *exportWriter) typeList(ts []*types.Type) {
- w.uint64(uint64(len(ts)))
- for _, rparam := range ts {
- w.typ(rparam)
- }
-}
-
-func (w *exportWriter) tparamList(fs []*types.Field) {
- w.uint64(uint64(len(fs)))
- for _, f := range fs {
- if !f.Type.IsTypeParam() {
- base.Fatalf("unexpected non-typeparam")
- }
- w.typ(f.Type)
- }
-}
-
-func (w *exportWriter) paramList(fs []*types.Field) {
- w.uint64(uint64(len(fs)))
- for _, f := range fs {
- w.param(f)
- }
-}
-
-func (w *exportWriter) param(f *types.Field) {
- w.pos(f.Pos)
- w.localIdent(types.OrigSym(f.Sym))
- w.typ(f.Type)
-}
-
func constTypeOf(typ *types.Type) constant.Kind {
switch typ {
case types.UntypedInt, types.UntypedRune:
return 0
}
-func (w *exportWriter) value(typ *types.Type, v constant.Value) {
- w.typ(typ)
-
- if iexportVersionCurrent >= iexportVersionGo1_18 {
- w.int64(int64(v.Kind()))
- }
-
- var kind constant.Kind
- var valType *types.Type
-
- if typ.IsTypeParam() {
- kind = v.Kind()
- if iexportVersionCurrent < iexportVersionGo1_18 {
- // A constant will have a TYPEPARAM type if it appears in a place
- // where it must match that typeparam type (e.g. in a binary
- // operation with a variable of that typeparam type). If so, then
- // we must write out its actual constant kind as well, so its
- // constant val can be read in properly during import.
- w.int64(int64(kind))
- }
-
- switch kind {
- case constant.Int:
- valType = types.Types[types.TINT64]
- case constant.Float:
- valType = types.Types[types.TFLOAT64]
- case constant.Complex:
- valType = types.Types[types.TCOMPLEX128]
- }
- } else {
- ir.AssertValidTypeForConst(typ, v)
- kind = constTypeOf(typ)
- valType = typ
- }
-
- // Each type has only one admissible constant representation, so we could
- // type switch directly on v.Kind() here. However, switching on the type
- // (in the non-typeparam case) increases symmetry with import logic and
- // provides a useful consistency check.
-
- switch kind {
- case constant.Bool:
- w.bool(constant.BoolVal(v))
- case constant.String:
- w.string(constant.StringVal(v))
- case constant.Int:
- w.mpint(v, valType)
- case constant.Float:
- w.mpfloat(v, valType)
- case constant.Complex:
- w.mpfloat(constant.Real(v), valType)
- w.mpfloat(constant.Imag(v), valType)
- }
-}
-
func intSize(typ *types.Type) (signed bool, maxBytes uint) {
if typ.IsUntyped() {
return true, ir.ConstPrec / 8
return
}
-// mpint exports a multi-precision integer.
-//
-// For unsigned types, small values are written out as a single
-// byte. Larger values are written out as a length-prefixed big-endian
-// byte string, where the length prefix is encoded as its complement.
-// For example, bytes 0, 1, and 2 directly represent the integer
-// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-,
-// 2-, and 3-byte big-endian string follow.
-//
-// Encoding for signed types use the same general approach as for
-// unsigned types, except small values use zig-zag encoding and the
-// bottom bit of length prefix byte for large values is reserved as a
-// sign bit.
-//
-// The exact boundary between small and large encodings varies
-// according to the maximum number of bytes needed to encode a value
-// of type typ. As a special case, 8-bit types are always encoded as a
-// single byte.
-func (w *exportWriter) mpint(x constant.Value, typ *types.Type) {
- signed, maxBytes := intSize(typ)
-
- negative := constant.Sign(x) < 0
- if !signed && negative {
- base.Fatalf("negative unsigned integer; type %v, value %v", typ, x)
- }
-
- b := constant.Bytes(x) // little endian
- for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
- b[i], b[j] = b[j], b[i]
- }
-
- if len(b) > 0 && b[0] == 0 {
- base.Fatalf("leading zeros")
- }
- if uint(len(b)) > maxBytes {
- base.Fatalf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)
- }
-
- maxSmall := 256 - maxBytes
- if signed {
- maxSmall = 256 - 2*maxBytes
- }
- if maxBytes == 1 {
- maxSmall = 256
- }
-
- // Check if x can use small value encoding.
- if len(b) <= 1 {
- var ux uint
- if len(b) == 1 {
- ux = uint(b[0])
- }
- if signed {
- ux <<= 1
- if negative {
- ux--
- }
- }
- if ux < maxSmall {
- w.data.WriteByte(byte(ux))
- return
- }
- }
-
- n := 256 - uint(len(b))
- if signed {
- n = 256 - 2*uint(len(b))
- if negative {
- n |= 1
- }
- }
- if n < maxSmall || n >= 256 {
- base.Fatalf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)
- }
-
- w.data.WriteByte(byte(n))
- w.data.Write(b)
-}
-
-// mpfloat exports a multi-precision floating point number.
-//
-// The number's value is decomposed into mantissa × 2**exponent, where
-// mantissa is an integer. The value is written out as mantissa (as a
-// multi-precision integer) and then the exponent, except exponent is
-// omitted if mantissa is zero.
-func (w *exportWriter) mpfloat(v constant.Value, typ *types.Type) {
- f := ir.BigFloat(v)
- if f.IsInf() {
- base.Fatalf("infinite constant")
- }
-
- // Break into f = mant × 2**exp, with 0.5 <= mant < 1.
- var mant big.Float
- exp := int64(f.MantExp(&mant))
-
- // Scale so that mant is an integer.
- prec := mant.MinPrec()
- mant.SetMantExp(&mant, int(prec))
- exp -= int64(prec)
-
- manti, acc := mant.Int(nil)
- if acc != big.Exact {
- base.Fatalf("mantissa scaling failed for %f (%s)", f, acc)
- }
- w.mpint(constant.Make(manti), typ)
- if manti.Sign() != 0 {
- w.int64(exp)
- }
-}
-
-func (w *exportWriter) mprat(v constant.Value) {
- r, ok := constant.Val(v).(*big.Rat)
- if !w.bool(ok) {
- return
- }
- // TODO(mdempsky): Come up with a more efficient binary
- // encoding before bumping iexportVersion to expose to
- // gcimporter.
- w.string(r.String())
-}
-
-func (w *exportWriter) bool(b bool) bool {
- var x uint64
- if b {
- x = 1
- }
- w.uint64(x)
- return b
-}
-
-func (w *exportWriter) int64(x int64) { w.data.int64(x) }
-func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) }
-func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
-
-// Compiler-specific extensions.
-
-func (w *exportWriter) constExt(n *ir.Name) {
- // Internally, we now represent untyped float and complex
- // constants with infinite-precision rational numbers using
- // go/constant, but the "public" export data format known to
- // gcimporter only supports 512-bit floating point constants.
- // In case rationals turn out to be a bad idea and we want to
- // switch back to fixed-precision constants, for now we
- // continue writing out the 512-bit truncation in the public
- // data section, and write the exact, rational constant in the
- // compiler's extension data. Also, we only need to worry
- // about exporting rationals for declared constants, because
- // constants that appear in an expression will already have
- // been coerced to a concrete, fixed-precision type.
- //
- // Eventually, assuming we stick with using rationals, we
- // should bump iexportVersion to support rationals, and do the
- // whole gcimporter update song-and-dance.
- //
- // TODO(mdempsky): Prepare vocals for that.
-
- switch n.Type() {
- case types.UntypedFloat:
- w.mprat(n.Val())
- case types.UntypedComplex:
- v := n.Val()
- w.mprat(constant.Real(v))
- w.mprat(constant.Imag(v))
- }
-}
-
-func (w *exportWriter) varExt(n *ir.Name) {
- w.linkname(n.Sym())
- w.symIdx(n.Sym())
-}
-
-func (w *exportWriter) funcExt(n *ir.Name) {
- w.linkname(n.Sym())
- w.symIdx(n.Sym())
-
- // Record definition ABI so cross-ABI calls can be direct.
- // This is important for the performance of calling some
- // common functions implemented in assembly (e.g., bytealg).
- w.uint64(uint64(n.Func.ABI))
-
- w.uint64(uint64(n.Func.Pragma))
-
- // Escape analysis.
- for _, fs := range &types.RecvsParams {
- for _, f := range fs(n.Type()).FieldSlice() {
- w.string(f.Note)
- }
- }
-
- // Write out inline body or body of a generic function/method.
- if n.Type().HasTParam() && n.Func.Body != nil && n.Func.Inl == nil {
- base.FatalfAt(n.Pos(), "generic function is not marked inlineable")
- }
- if n.Func.Inl != nil {
- w.uint64(1 + uint64(n.Func.Inl.Cost))
- w.bool(n.Func.Inl.CanDelayResults)
- if n.Func.ExportInline() || n.Type().HasTParam() {
- if n.Type().HasTParam() {
- // If this generic function/method is from another
- // package, but we didn't use for instantiation in
- // this package, we may not yet have imported it.
- ImportedBody(n.Func)
- }
- w.p.doInline(n)
- }
-
- // Endlineno for inlined function.
- w.pos(n.Func.Endlineno)
- } else {
- w.uint64(0)
- }
-}
-
-func (w *exportWriter) methExt(m *types.Field) {
- w.bool(m.Nointerface())
- w.funcExt(m.Nname.(*ir.Name))
-}
-
-func (w *exportWriter) linkname(s *types.Sym) {
- w.string(s.Linkname)
-}
-
-func (w *exportWriter) symIdx(s *types.Sym) {
- lsym := s.Linksym()
- if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
- // Don't export index for non-package symbols, linkname'd symbols,
- // and symbols without an index. They can only be referenced by
- // name.
- w.int64(-1)
- } else {
- // For a defined symbol, export its index.
- // For re-exporting an imported symbol, pass its index through.
- w.int64(int64(lsym.SymIdx))
- }
-}
-
-func (w *exportWriter) typeExt(t *types.Type) {
- // Export whether this type is marked notinheap.
- w.bool(t.NotInHeap())
- // For type T, export the index of type descriptor symbols of T and *T.
- if i, ok := typeSymIdx[t]; ok {
- w.int64(i[0])
- w.int64(i[1])
- return
- }
- w.symIdx(types.TypeSym(t))
- w.symIdx(types.TypeSym(t.PtrTo()))
-}
-
-// Inline bodies.
-
-func (w *exportWriter) writeNames(dcl []*ir.Name) {
- w.int64(int64(len(dcl)))
- for i, n := range dcl {
- w.pos(n.Pos())
- w.localIdent(n.Sym())
- w.typ(n.Type())
- w.dclIndex[n] = w.maxDclIndex + i
- }
- w.maxDclIndex += len(dcl)
-}
-
-func (w *exportWriter) funcBody(fn *ir.Func) {
- //fmt.Printf("Exporting %s\n", fn.Nname.Sym().Name)
- w.writeNames(fn.Inl.Dcl)
-
- w.stmtList(fn.Inl.Body)
-}
-
-func (w *exportWriter) stmtList(list []ir.Node) {
- for _, n := range list {
- w.node(n)
- }
- w.op(ir.OEND)
-}
-
-func (w *exportWriter) node(n ir.Node) {
- if ir.OpPrec[n.Op()] < 0 {
- w.stmt(n)
- } else {
- w.expr(n)
- }
-}
-
func isNonEmptyAssign(n ir.Node) bool {
switch n.Op() {
case ir.OAS:
}
return false
}
-
-// Caution: stmt will emit more than one node for statement nodes n that have a
-// non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init
-// section (such as in "if", "for", etc.).
-func (w *exportWriter) stmt(n ir.Node) {
- if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE {
- // can't use stmtList here since we don't want the final OEND
- for _, n := range n.Init() {
- w.stmt(n)
- }
- }
-
- switch n.Op() {
- case ir.OBLOCK:
- // No OBLOCK in export data.
- // Inline content into this statement list,
- // like the init list above.
- // (At the moment neither the parser nor the typechecker
- // generate OBLOCK nodes except to denote an empty
- // function body, although that may change.)
- n := n.(*ir.BlockStmt)
- for _, n := range n.List {
- w.stmt(n)
- }
-
- case ir.ODCL:
- n := n.(*ir.Decl)
- if ir.IsBlank(n.X) {
- return // blank declarations not useful to importers
- }
- w.op(ir.ODCL)
- w.localName(n.X)
-
- case ir.OAS:
- // Don't export "v = <N>" initializing statements, hope they're always
- // preceded by the DCL which will be re-parsed and typecheck to reproduce
- // the "v = <N>" again.
- n := n.(*ir.AssignStmt)
- if n.Y != nil {
- w.op(ir.OAS)
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.expr(n.X)
- w.expr(n.Y)
- w.bool(n.Def)
- }
-
- case ir.OASOP:
- n := n.(*ir.AssignOpStmt)
- w.op(ir.OASOP)
- w.pos(n.Pos())
- w.op(n.AsOp)
- w.expr(n.X)
- if w.bool(!n.IncDec) {
- w.expr(n.Y)
- }
-
- case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
- n := n.(*ir.AssignListStmt)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprList(n.Lhs)
- w.exprList(n.Rhs)
- w.bool(n.Def)
-
- case ir.ORETURN:
- n := n.(*ir.ReturnStmt)
- w.op(ir.ORETURN)
- w.pos(n.Pos())
- w.exprList(n.Results)
-
- // case ORETJMP:
- // unreachable - generated by compiler for trampoline routines
-
- case ir.OGO, ir.ODEFER:
- n := n.(*ir.GoDeferStmt)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.Call)
-
- case ir.OIF:
- n := n.(*ir.IfStmt)
- w.op(ir.OIF)
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.expr(n.Cond)
- w.stmtList(n.Body)
- w.stmtList(n.Else)
-
- case ir.OFOR:
- n := n.(*ir.ForStmt)
- w.op(ir.OFOR)
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprsOrNil(n.Cond, n.Post)
- w.stmtList(n.Body)
-
- case ir.ORANGE:
- n := n.(*ir.RangeStmt)
- w.op(ir.ORANGE)
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprsOrNil(n.Key, n.Value)
- w.expr(n.X)
- w.stmtList(n.Body)
-
- case ir.OSELECT:
- n := n.(*ir.SelectStmt)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.commList(n.Cases)
-
- case ir.OSWITCH:
- n := n.(*ir.SwitchStmt)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprsOrNil(n.Tag, nil)
- w.caseList(n.Cases, isNamedTypeSwitch(n.Tag))
-
- // case OCASE:
- // handled by caseList
-
- case ir.OFALL:
- n := n.(*ir.BranchStmt)
- w.op(ir.OFALL)
- w.pos(n.Pos())
-
- case ir.OBREAK, ir.OCONTINUE, ir.OGOTO, ir.OLABEL:
- w.op(n.Op())
- w.pos(n.Pos())
- label := ""
- if sym := n.Sym(); sym != nil {
- label = sym.Name
- }
- w.string(label)
-
- default:
- base.Fatalf("exporter: CANNOT EXPORT: %v\nPlease notify gri@\n", n.Op())
- }
-}
-
func isNamedTypeSwitch(x ir.Node) bool {
guard, ok := x.(*ir.TypeSwitchGuard)
return ok && guard.Tag != nil
}
-func (w *exportWriter) caseList(cases []*ir.CaseClause, namedTypeSwitch bool) {
- w.uint64(uint64(len(cases)))
- for _, cas := range cases {
- w.pos(cas.Pos())
- w.stmtList(cas.List)
- if namedTypeSwitch {
- w.localName(cas.Var)
- }
- w.stmtList(cas.Body)
- }
-}
-
-func (w *exportWriter) commList(cases []*ir.CommClause) {
- w.uint64(uint64(len(cases)))
- for _, cas := range cases {
- w.pos(cas.Pos())
- defaultCase := cas.Comm == nil
- w.bool(defaultCase)
- if !defaultCase {
- // Only call w.node for non-default cause (cas.Comm is non-nil)
- w.node(cas.Comm)
- }
- w.stmtList(cas.Body)
- }
-}
-
-func (w *exportWriter) exprList(list ir.Nodes) {
- for _, n := range list {
- w.expr(n)
- }
- w.op(ir.OEND)
-}
-
func simplifyForExport(n ir.Node) ir.Node {
switch n.Op() {
case ir.OPAREN:
return n
}
-func (w *exportWriter) expr(n ir.Node) {
- n = simplifyForExport(n)
- switch n.Op() {
- // expressions
- // (somewhat closely following the structure of exprfmt in fmt.go)
- case ir.ONIL:
- n := n.(*ir.NilExpr)
- // If n is a typeparam, it will have already been checked
- // for proper use by the types2 typechecker.
- if !n.Type().IsTypeParam() && !n.Type().HasNil() {
- base.Fatalf("unexpected type for nil: %v", n.Type())
- }
- w.op(ir.ONIL)
- w.pos(n.Pos())
- w.typ(n.Type())
-
- case ir.OLITERAL:
- w.op(ir.OLITERAL)
- if ir.HasUniquePos(n) {
- w.pos(n.Pos())
- } else {
- w.pos(src.NoXPos)
- }
- w.value(n.Type(), n.Val())
-
- case ir.ONAME:
- // Package scope name.
- n := n.(*ir.Name)
- if (n.Class == ir.PEXTERN || n.Class == ir.PFUNC) && !ir.IsBlank(n) {
- w.op(ir.ONONAME)
- // Indicate that this is not an OKEY entry.
- w.bool(false)
- w.qualifiedIdent(n)
- w.typ(n.Type())
- break
- }
-
- // Function scope name.
- // We don't need a type here, as the type will be provided at the
- // declaration of n.
- w.op(ir.ONAME)
-
- // This handles the case where we haven't yet transformed a call
- // to a builtin, so we must write out the builtin as a name in the
- // builtin package.
- isBuiltin := n.BuiltinOp != ir.OXXX
- w.bool(isBuiltin)
- if isBuiltin {
- w.bool(n.Sym().Pkg == types.UnsafePkg)
- w.string(n.Sym().Name)
- break
- }
- w.localName(n)
-
- case ir.ONONAME:
- w.op(ir.ONONAME)
- // This can only be for OKEY nodes in generic functions. Mark it
- // as a key entry.
- w.bool(true)
- s := n.Sym()
- w.string(s.Name)
- w.pkg(s.Pkg)
- w.typ(n.Type())
-
- // case OPACK:
- // should have been resolved by typechecking - handled by default case
-
- case ir.OTYPE:
- w.op(ir.OTYPE)
- w.typ(n.Type())
-
- case ir.ODYNAMICTYPE:
- n := n.(*ir.DynamicType)
- w.op(ir.ODYNAMICTYPE)
- w.pos(n.Pos())
- w.expr(n.RType)
- if w.bool(n.ITab != nil) {
- w.expr(n.ITab)
- }
- w.typ(n.Type())
-
- case ir.OTYPESW:
- n := n.(*ir.TypeSwitchGuard)
- w.op(ir.OTYPESW)
- w.pos(n.Pos())
- var s *types.Sym
- if n.Tag != nil {
- if n.Tag.Op() != ir.ONONAME {
- base.Fatalf("expected ONONAME, got %v", n.Tag)
- }
- s = n.Tag.Sym()
- }
- w.localIdent(s) // declared pseudo-variable, if any
- w.expr(n.X)
-
- // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
- // should have been resolved by typechecking - handled by default case
-
- case ir.OCLOSURE:
- n := n.(*ir.ClosureExpr)
- w.op(ir.OCLOSURE)
- w.pos(n.Pos())
- old := w.currPkg
- w.setPkg(n.Type().Pkg(), true)
- w.signature(n.Type())
- w.setPkg(old, true)
-
- // Write out id for the Outer of each conditional variable. The
- // conditional variable itself for this closure will be re-created
- // during import.
- w.int64(int64(len(n.Func.ClosureVars)))
- for i, cv := range n.Func.ClosureVars {
- w.pos(cv.Pos())
- w.localName(cv.Outer)
- // Closure variable (which will be re-created during
- // import) is given via a negative id, starting at -2,
- // which is used to refer to it later in the function
- // during export. -1 represents blanks.
- w.dclIndex[cv] = -(i + 2) - w.maxClosureVarIndex
- }
- w.maxClosureVarIndex += len(n.Func.ClosureVars)
-
- // like w.funcBody(n.Func), but not for .Inl
- w.writeNames(n.Func.Dcl)
- w.stmtList(n.Func.Body)
-
- // case OCOMPLIT:
- // should have been resolved by typechecking - handled by default case
-
- case ir.OPTRLIT:
- n := n.(*ir.AddrExpr)
- w.op(ir.OPTRLIT)
- w.pos(n.Pos())
- w.expr(n.X)
- w.typ(n.Type())
-
- case ir.OSTRUCTLIT:
- n := n.(*ir.CompLitExpr)
- w.op(ir.OSTRUCTLIT)
- w.pos(n.Pos())
- w.typ(n.Type())
- w.fieldList(n.List) // special handling of field names
-
- case ir.OCOMPLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
- n := n.(*ir.CompLitExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.typ(n.Type())
- w.exprList(n.List)
- if n.Op() == ir.OSLICELIT {
- w.uint64(uint64(n.Len))
- }
- case ir.OKEY:
- n := n.(*ir.KeyExpr)
- w.op(ir.OKEY)
- w.pos(n.Pos())
- w.expr(n.Key)
- w.expr(n.Value)
-
- // case OSTRUCTKEY:
- // unreachable - handled in case OSTRUCTLIT by elemList
-
- case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
- n := n.(*ir.SelectorExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.exoticSelector(n.Sel)
- w.exoticType(n.Type())
- if n.Op() == ir.OXDOT {
- // n.Selection for method references will be
- // reconstructed during import.
- w.bool(n.Selection != nil)
- } else if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER {
- w.exoticField(n.Selection)
- }
- // n.Selection is not required for OMETHEXPR, ODOTMETH, and OMETHVALUE. It will
- // be reconstructed during import. n.Selection is computed during
- // transformDot() for OXDOT.
-
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- n := n.(*ir.TypeAssertExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.typ(n.Type())
-
- case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
- n := n.(*ir.DynamicTypeAssertExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- if w.bool(n.RType != nil) {
- w.expr(n.RType)
- }
- if w.bool(n.ITab != nil) {
- w.expr(n.ITab)
- }
- w.typ(n.Type())
-
- case ir.OINDEX, ir.OINDEXMAP:
- n := n.(*ir.IndexExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.expr(n.Index)
- w.exoticType(n.Type())
- if n.Op() == ir.OINDEXMAP {
- w.bool(n.Assigned)
- }
-
- case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR:
- n := n.(*ir.SliceExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.exprsOrNil(n.Low, n.High)
- w.typ(n.Type())
-
- case ir.OSLICE3, ir.OSLICE3ARR:
- n := n.(*ir.SliceExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.exprsOrNil(n.Low, n.High)
- w.expr(n.Max)
- w.typ(n.Type())
-
- case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
- // treated like other builtin calls (see e.g., OREAL)
- n := n.(*ir.BinaryExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.expr(n.X)
- w.expr(n.Y)
- w.typ(n.Type())
-
- case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARR, ir.OSLICE2ARRPTR:
- n := n.(*ir.ConvExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.typ(n.Type())
- w.expr(n.X)
- w.bool(n.Implicit())
-
- case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
- n := n.(*ir.UnaryExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- if n.Op() != ir.OPANIC {
- w.typ(n.Type())
- }
-
- case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
- n := n.(*ir.CallExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprList(n.Args) // emits terminating OEND
- // only append() calls may contain '...' arguments
- if n.Op() == ir.OAPPEND {
- w.bool(n.IsDDD)
- } else if n.IsDDD {
- base.Fatalf("exporter: unexpected '...' with %v call", n.Op())
- }
- if n.Op() != ir.ODELETE && n.Op() != ir.OPRINT && n.Op() != ir.OPRINTN {
- w.typ(n.Type())
- }
-
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG:
- n := n.(*ir.CallExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.expr(n.X)
- w.exprList(n.Args)
- w.bool(n.IsDDD)
- w.exoticType(n.Type())
-
- case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
- n := n.(*ir.MakeExpr)
- w.op(n.Op()) // must keep separate from OMAKE for importer
- w.pos(n.Pos())
- w.typ(n.Type())
- switch {
- default:
- // empty list
- w.op(ir.OEND)
- case n.Cap != nil:
- w.expr(n.Len)
- w.expr(n.Cap)
- w.op(ir.OEND)
- case n.Len != nil && (n.Op() == ir.OMAKESLICE || !n.Len.Type().IsUntyped()):
- // Note: the extra conditional exists because make(T) for
- // T a map or chan type, gets an untyped zero added as
- // an argument. Don't serialize that argument here.
- w.expr(n.Len)
- w.op(ir.OEND)
- case n.Len != nil:
- w.expr(n.Len)
- w.op(ir.OEND)
- }
-
- case ir.OLINKSYMOFFSET:
- n := n.(*ir.LinksymOffsetExpr)
- w.op(ir.OLINKSYMOFFSET)
- w.pos(n.Pos())
- w.string(n.Linksym.Name)
- w.uint64(uint64(n.Offset_))
- w.typ(n.Type())
-
- // unary expressions
- case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
- n := n.(*ir.UnaryExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.typ(n.Type())
-
- case ir.OADDR:
- n := n.(*ir.AddrExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.typ(n.Type())
-
- case ir.ODEREF:
- n := n.(*ir.StarExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.typ(n.Type())
-
- case ir.OSEND:
- n := n.(*ir.SendStmt)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.Chan)
- w.expr(n.Value)
-
- // binary expressions
- case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
- ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
- n := n.(*ir.BinaryExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.expr(n.Y)
- w.typ(n.Type())
-
- case ir.OANDAND, ir.OOROR:
- n := n.(*ir.LogicalExpr)
- w.op(n.Op())
- w.pos(n.Pos())
- w.expr(n.X)
- w.expr(n.Y)
- w.typ(n.Type())
-
- case ir.OADDSTR:
- n := n.(*ir.AddStringExpr)
- w.op(ir.OADDSTR)
- w.pos(n.Pos())
- w.exprList(n.List)
- w.typ(n.Type())
-
- case ir.ODCLCONST:
- // if exporting, DCLCONST should just be removed as its usage
- // has already been replaced with literals
-
- case ir.OFUNCINST:
- n := n.(*ir.InstExpr)
- w.op(ir.OFUNCINST)
- w.pos(n.Pos())
- w.expr(n.X)
- w.uint64(uint64(len(n.Targs)))
- for _, targ := range n.Targs {
- w.typ(targ.Type())
- }
- w.typ(n.Type())
-
- case ir.OSELRECV2:
- n := n.(*ir.AssignListStmt)
- w.op(ir.OSELRECV2)
- w.pos(n.Pos())
- w.stmtList(n.Init())
- w.exprList(n.Lhs)
- w.exprList(n.Rhs)
- w.bool(n.Def)
-
- default:
- base.Fatalf("cannot export %v (%d) node\n"+
- "\t==> please file an issue and assign to gri@", n.Op(), int(n.Op()))
- }
-}
-
-func (w *exportWriter) op(op ir.Op) {
- if debug {
- w.uint64(magic)
- }
- w.uint64(uint64(op))
-}
-
-func (w *exportWriter) exprsOrNil(a, b ir.Node) {
- ab := 0
- if a != nil {
- ab |= 1
- }
- if b != nil {
- ab |= 2
- }
- w.uint64(uint64(ab))
- if ab&1 != 0 {
- w.expr(a)
- }
- if ab&2 != 0 {
- w.node(b)
- }
-}
-
-func (w *exportWriter) fieldList(list ir.Nodes) {
- w.uint64(uint64(len(list)))
- for _, n := range list {
- n := n.(*ir.StructKeyExpr)
- w.pos(n.Pos())
- w.exoticField(n.Field)
- w.expr(n.Value)
- }
-}
-
-func (w *exportWriter) localName(n *ir.Name) {
- if ir.IsBlank(n) {
- w.int64(-1)
- return
- }
-
- i, ok := w.dclIndex[n]
- if !ok {
- base.FatalfAt(n.Pos(), "missing from dclIndex: %+v", n)
- }
- w.int64(int64(i))
-}
-
-func (w *exportWriter) localIdent(s *types.Sym) {
- if w.currPkg == nil {
- base.Fatalf("missing currPkg")
- }
-
- // Anonymous parameters.
- if s == nil {
- w.string("")
- return
- }
-
- name := s.Name
- if name == "_" {
- w.string("_")
- return
- }
-
- // The name of autotmp variables isn't important; they just need to
- // be unique. To stabilize the export data, simply write out "$" as
- // a marker and let the importer generate its own unique name.
- if strings.HasPrefix(name, ".autotmp_") {
- w.string("$autotmp")
- return
- }
-
- if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, LocalDictName) && !strings.HasPrefix(name, ".rcvr") {
- base.Fatalf("unexpected dot in identifier: %v", name)
- }
-
- if s.Pkg != w.currPkg {
- base.Fatalf("weird package in name: %v => %v from %q, not %q", s, name, s.Pkg.Path, w.currPkg.Path)
- }
-
- w.string(name)
-}
-
-type intWriter struct {
- bytes.Buffer
-}
-
-func (w *intWriter) int64(x int64) {
- var buf [binary.MaxVarintLen64]byte
- n := binary.PutVarint(buf[:], x)
- w.Write(buf[:n])
-}
-
-func (w *intWriter) uint64(x uint64) {
- var buf [binary.MaxVarintLen64]byte
- n := binary.PutUvarint(buf[:], x)
- w.Write(buf[:n])
-}
-
// The name used for dictionary parameters or local variables.
const LocalDictName = ".dict"
package typecheck
import (
- "encoding/binary"
- "fmt"
- "go/constant"
- "io"
- "math/big"
- "strings"
-
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
)
-// An iimporterAndOffset identifies an importer and an offset within
-// its data section.
-type iimporterAndOffset struct {
- p *iimporter
- off uint64
-}
-
-var (
- // DeclImporter maps from imported identifiers to an importer
- // and offset where that identifier's declaration can be read.
- DeclImporter = map[*types.Sym]iimporterAndOffset{}
-
- // inlineImporter is like DeclImporter, but for inline bodies
- // for function and method symbols.
- inlineImporter = map[*types.Sym]iimporterAndOffset{}
-)
-
-// expandDecl returns immediately if n is already a Name node. Otherwise, n should
-// be an Ident node, and expandDecl reads in the definition of the specified
-// identifier from the appropriate package.
-func expandDecl(n ir.Node) ir.Node {
- if n, ok := n.(*ir.Name); ok {
- return n
- }
-
- id := n.(*ir.Ident)
- if n := id.Sym().PkgDef(); n != nil {
- return n.(*ir.Name)
- }
-
- r := importReaderFor(id.Sym(), DeclImporter)
- if r == nil {
- // Can happen if user tries to reference an undeclared name.
- return n
- }
-
- return r.doDecl(n.Sym())
-}
-
-// ImportBody reads in the dcls and body of an imported function (which should not
-// yet have been read in).
-func ImportBody(fn *ir.Func) {
- if fn.Inl.Body != nil {
- base.Fatalf("%v already has inline body", fn)
- }
-
- r := importReaderFor(fn.Nname.Sym(), inlineImporter)
- if r == nil {
- base.Fatalf("missing import reader for %v", fn)
- }
-
- if inimport {
- base.Fatalf("recursive inimport")
- }
- inimport = true
- r.doInline(fn)
- inimport = false
-}
-
// HaveInlineBody reports whether we have fn's inline body available
// for inlining.
//
panic("unreachable")
}
-func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
- x, ok := importers[sym]
- if !ok {
- return nil
- }
-
- return x.p.newReader(x.off, sym.Pkg)
-}
-
-type intReader struct {
- *strings.Reader
- pkg *types.Pkg
-}
-
-func (r *intReader) int64() int64 {
- i, err := binary.ReadVarint(r.Reader)
- if err != nil {
- base.Errorf("import %q: read error: %v", r.pkg.Path, err)
- base.ErrorExit()
- }
- return i
-}
-
-func (r *intReader) uint64() uint64 {
- i, err := binary.ReadUvarint(r.Reader)
- if err != nil {
- base.Errorf("import %q: read error: %v", r.pkg.Path, err)
- base.ErrorExit()
- }
- return i
-}
-
-func ReadImports(pkg *types.Pkg, data string) {
- ird := &intReader{strings.NewReader(data), pkg}
-
- version := ird.uint64()
- switch version {
- case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
- default:
- base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
- base.ErrorExit()
- }
-
- sLen := int64(ird.uint64())
- dLen := int64(ird.uint64())
-
- whence, _ := ird.Seek(0, io.SeekCurrent)
- stringData := data[whence : whence+sLen]
- declData := data[whence+sLen : whence+sLen+dLen]
- ird.Seek(sLen+dLen, io.SeekCurrent)
-
- p := &iimporter{
- exportVersion: version,
- ipkg: pkg,
-
- pkgCache: map[uint64]*types.Pkg{},
- posBaseCache: map[uint64]*src.PosBase{},
- typCache: map[uint64]*types.Type{},
-
- stringData: stringData,
- declData: declData,
- }
-
- for i, pt := range predeclared() {
- p.typCache[uint64(i)] = pt
- }
-
- // Declaration index.
- for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
- pkg := p.pkgAt(ird.uint64())
- pkgName := p.stringAt(ird.uint64())
- _ = int(ird.uint64()) // was package height, but not necessary anymore.
- if pkg.Name == "" {
- pkg.Name = pkgName
- types.NumImport[pkgName]++
-
- // TODO(mdempsky): This belongs somewhere else.
- pkg.Lookup("_").Def = ir.BlankNode
- } else {
- if pkg.Name != pkgName {
- base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
- }
- }
-
- for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
- s := pkg.Lookup(p.stringAt(ird.uint64()))
- off := ird.uint64()
-
- if _, ok := DeclImporter[s]; !ok {
- DeclImporter[s] = iimporterAndOffset{p, off}
- }
- }
- }
-
- // Inline body index.
- for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
- pkg := p.pkgAt(ird.uint64())
-
- for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
- s := pkg.Lookup(p.stringAt(ird.uint64()))
- off := ird.uint64()
-
- if _, ok := inlineImporter[s]; !ok {
- inlineImporter[s] = iimporterAndOffset{p, off}
- }
- }
- }
-}
-
-type iimporter struct {
- exportVersion uint64
- ipkg *types.Pkg
-
- pkgCache map[uint64]*types.Pkg
- posBaseCache map[uint64]*src.PosBase
- typCache map[uint64]*types.Type
-
- stringData string
- declData string
-}
-
-func (p *iimporter) stringAt(off uint64) string {
- var x [binary.MaxVarintLen64]byte
- n := copy(x[:], p.stringData[off:])
-
- slen, n := binary.Uvarint(x[:n])
- if n <= 0 {
- base.Fatalf("varint failed")
- }
- spos := off + uint64(n)
- return p.stringData[spos : spos+slen]
-}
-
-func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
- if posBase, ok := p.posBaseCache[off]; ok {
- return posBase
- }
-
- file := p.stringAt(off)
- posBase := src.NewFileBase(file, file)
- p.posBaseCache[off] = posBase
- return posBase
-}
-
-func (p *iimporter) pkgAt(off uint64) *types.Pkg {
- if pkg, ok := p.pkgCache[off]; ok {
- return pkg
- }
-
- pkg := p.ipkg
- if pkgPath := p.stringAt(off); pkgPath != "" {
- pkg = types.NewPkg(pkgPath, "")
- }
- p.pkgCache[off] = pkg
- return pkg
-}
-
-// An importReader keeps state for reading an individual imported
-// object (declaration or inline body).
-type importReader struct {
- strings.Reader
- p *iimporter
-
- currPkg *types.Pkg
- prevBase *src.PosBase
- prevLine int64
- prevColumn int64
-
- // curfn is the current function we're importing into.
- curfn *ir.Func
- // Slice of all dcls for function, including any interior closures
- allDcls []*ir.Name
- allClosureVars []*ir.Name
- autotmpgen int
-}
-
-func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
- r := &importReader{
- p: p,
- currPkg: pkg,
- }
- r.Reader.Reset(p.declData[off:])
- return r
-}
-
-func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
-func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
-func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
-
-func (r *importReader) setPkg() {
- r.currPkg = r.pkg()
-}
-
-func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
- tag := r.byte()
- pos := r.pos()
-
- switch tag {
- case 'A':
- typ := r.typ()
-
- return importalias(pos, sym, typ)
-
- case 'C':
- typ := r.typ()
- val := r.value(typ)
-
- n := importconst(pos, sym, typ, val)
- r.constExt(n)
- return n
-
- case 'F', 'G':
- var tparams []*types.Field
- if tag == 'G' {
- tparams = r.tparamList()
- }
- typ := r.signature(nil, tparams)
-
- n := importfunc(pos, sym, typ)
- r.funcExt(n)
- return n
-
- case 'T', 'U':
- // Types can be recursive. We need to setup a stub
- // declaration before recursing.
- n := importtype(pos, sym)
- t := n.Type()
-
- // Because of recursion, we need to defer width calculations and
- // instantiations on intermediate types until the top-level type is
- // fully constructed. Note that we can have recursion via type
- // constraints.
- types.DeferCheckSize()
- deferDoInst()
- if tag == 'U' {
- rparams := r.typeList()
- t.SetRParams(rparams)
- }
-
- underlying := r.typ()
- t.SetUnderlying(underlying)
-
- if underlying.IsInterface() {
- // Finish up all type instantiations and CheckSize calls
- // now that a top-level type is fully constructed.
- resumeDoInst()
- types.ResumeCheckSize()
- r.typeExt(t)
- return n
- }
-
- ms := make([]*types.Field, r.uint64())
- for i := range ms {
- mpos := r.pos()
- msym := r.selector()
- recv := r.param()
- mtyp := r.signature(recv, nil)
-
- // MethodSym already marked m.Sym as a function.
- m := ir.NewNameAt(mpos, ir.MethodSym(recv.Type, msym))
- m.Class = ir.PFUNC
- m.SetType(mtyp)
-
- m.Func = ir.NewFunc(mpos)
- m.Func.Nname = m
-
- f := types.NewField(mpos, msym, mtyp)
- f.Nname = m
- ms[i] = f
- }
- t.Methods().Set(ms)
-
- // Finish up all instantiations and CheckSize calls now
- // that a top-level type is fully constructed.
- resumeDoInst()
- types.ResumeCheckSize()
-
- r.typeExt(t)
- for _, m := range ms {
- r.methExt(m)
- }
- return n
-
- case 'P':
- if r.p.exportVersion < iexportVersionGenerics {
- base.Fatalf("unexpected type param type")
- }
- 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.(*ir.Name)
- }
- n := importsym(pos, sym, ir.OTYPE, ir.PTYPEPARAM)
- // The typeparam index is set at the point where the containing type
- // param list is imported.
- t := types.NewTypeParam(n, 0)
- n.SetType(t)
- implicit := false
- if r.p.exportVersion >= iexportVersionGo1_18 {
- implicit = r.bool()
- }
- bound := r.typ()
- if implicit {
- bound.MarkImplicit()
- }
- t.SetBound(bound)
- return n
-
- case 'V':
- typ := r.typ()
-
- n := importvar(pos, sym, typ)
- r.varExt(n)
- return n
-
- default:
- base.Fatalf("unexpected tag: %v", tag)
- panic("unreachable")
- }
-}
-
-func (r *importReader) value(typ *types.Type) constant.Value {
- var kind constant.Kind
- var valType *types.Type
-
- if r.p.exportVersion >= iexportVersionGo1_18 {
- // TODO: add support for using the kind in the non-typeparam case.
- kind = constant.Kind(r.int64())
- }
-
- if typ.IsTypeParam() {
- if r.p.exportVersion < iexportVersionGo1_18 {
- // If a constant had a typeparam type, then we wrote out its
- // actual constant kind as well.
- kind = constant.Kind(r.int64())
- }
- switch kind {
- case constant.Int:
- valType = types.Types[types.TINT64]
- case constant.Float:
- valType = types.Types[types.TFLOAT64]
- case constant.Complex:
- valType = types.Types[types.TCOMPLEX128]
- }
- } else {
- kind = constTypeOf(typ)
- valType = typ
- }
-
- switch kind {
- case constant.Bool:
- return constant.MakeBool(r.bool())
- case constant.String:
- return constant.MakeString(r.string())
- case constant.Int:
- var i big.Int
- r.mpint(&i, valType)
- return constant.Make(&i)
- case constant.Float:
- return r.float(valType)
- case constant.Complex:
- return makeComplex(r.float(valType), r.float(valType))
- }
-
- base.Fatalf("unexpected value type: %v", typ)
- panic("unreachable")
-}
-
-func (r *importReader) mpint(x *big.Int, typ *types.Type) {
- signed, maxBytes := intSize(typ)
-
- maxSmall := 256 - maxBytes
- if signed {
- maxSmall = 256 - 2*maxBytes
- }
- if maxBytes == 1 {
- maxSmall = 256
- }
-
- n, _ := r.ReadByte()
- if uint(n) < maxSmall {
- v := int64(n)
- if signed {
- v >>= 1
- if n&1 != 0 {
- v = ^v
- }
- }
- x.SetInt64(v)
- return
- }
-
- v := -n
- if signed {
- v = -(n &^ 1) >> 1
- }
- if v < 1 || uint(v) > maxBytes {
- base.Fatalf("weird decoding: %v, %v => %v", n, signed, v)
- }
- b := make([]byte, v)
- r.Read(b)
- x.SetBytes(b)
- if signed && n&1 != 0 {
- x.Neg(x)
- }
-}
-
-func (r *importReader) float(typ *types.Type) constant.Value {
- var mant big.Int
- r.mpint(&mant, typ)
- var f big.Float
- f.SetInt(&mant)
- if f.Sign() != 0 {
- f.SetMantExp(&f, int(r.int64()))
- }
- return constant.Make(&f)
-}
-
-func (r *importReader) mprat(orig constant.Value) constant.Value {
- if !r.bool() {
- return orig
- }
- var rat big.Rat
- rat.SetString(r.string())
- return constant.Make(&rat)
-}
-
-func (r *importReader) ident(selector bool) *types.Sym {
- name := r.string()
- if name == "" {
- return nil
- }
- pkg := r.currPkg
- if selector {
- if types.IsExported(name) {
- pkg = types.LocalPkg
- }
- } else {
- if name == "$autotmp" {
- name = autotmpname(r.autotmpgen)
- r.autotmpgen++
- }
- }
- return pkg.Lookup(name)
-}
-
-func (r *importReader) localIdent() *types.Sym { return r.ident(false) }
-func (r *importReader) selector() *types.Sym { return r.ident(true) }
-
-func (r *importReader) qualifiedIdent() *ir.Ident {
- name := r.string()
- pkg := r.pkg()
- sym := pkg.Lookup(name)
- return ir.NewIdent(src.NoXPos, sym)
-}
-
-func (r *importReader) pos() src.XPos {
- delta := r.int64()
- r.prevColumn += delta >> 1
- if delta&1 != 0 {
- delta = r.int64()
- r.prevLine += delta >> 1
- if delta&1 != 0 {
- r.prevBase = r.posBase()
- }
- }
-
- if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
- // TODO(mdempsky): Remove once we reliably write
- // position information for all nodes.
- return src.NoXPos
- }
-
- if r.prevBase == nil {
- base.Fatalf("missing posbase")
- }
- pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
- return base.Ctxt.PosTable.XPos(pos)
-}
-
-func (r *importReader) typ() *types.Type {
- // If this is a top-level type call, defer type instantiations until the
- // type is fully constructed.
- types.DeferCheckSize()
- deferDoInst()
- t := r.p.typAt(r.uint64())
- resumeDoInst()
- types.ResumeCheckSize()
- return t
-}
-
-func (r *importReader) exoticType() *types.Type {
- switch r.uint64() {
- case exoticTypeNil:
- return nil
- case exoticTypeTuple:
- funarg := types.Funarg(r.uint64())
- n := r.uint64()
- fs := make([]*types.Field, n)
- for i := range fs {
- pos := r.pos()
- var sym *types.Sym
- switch r.uint64() {
- case exoticTypeSymNil:
- sym = nil
- case exoticTypeSymNoPkg:
- sym = types.NoPkg.Lookup(r.string())
- case exoticTypeSymWithPkg:
- pkg := r.pkg()
- sym = pkg.Lookup(r.string())
- default:
- base.Fatalf("unknown symbol kind")
- }
- typ := r.typ()
- f := types.NewField(pos, sym, typ)
- fs[i] = f
- }
- t := types.NewStruct(types.NoPkg, fs)
- t.StructType().Funarg = funarg
- return t
- case exoticTypeRecv:
- var rcvr *types.Field
- if r.bool() { // isFakeRecv
- rcvr = types.FakeRecv()
- } else {
- rcvr = r.exoticParam()
- }
- return r.exoticSignature(rcvr)
- case exoticTypeRegular:
- return r.typ()
- default:
- base.Fatalf("bad kind of call type")
- return nil
- }
-}
-
-func (r *importReader) exoticSelector() *types.Sym {
- name := r.string()
- if name == "" {
- return nil
- }
- pkg := r.currPkg
- if types.IsExported(name) {
- pkg = types.LocalPkg
- }
- if r.uint64() != 0 {
- pkg = r.pkg()
- }
- return pkg.Lookup(name)
-}
-
-func (r *importReader) exoticSignature(recv *types.Field) *types.Type {
- var pkg *types.Pkg
- if r.bool() { // hasPkg
- pkg = r.pkg()
- }
- params := r.exoticParamList()
- results := r.exoticParamList()
- return types.NewSignature(pkg, recv, nil, params, results)
-}
-
-func (r *importReader) exoticParamList() []*types.Field {
- n := r.uint64()
- fs := make([]*types.Field, n)
- for i := range fs {
- fs[i] = r.exoticParam()
- }
- return fs
-}
-
-func (r *importReader) exoticParam() *types.Field {
- pos := r.pos()
- sym := r.exoticSym()
- off := r.uint64()
- typ := r.exoticType()
- ddd := r.bool()
- f := types.NewField(pos, sym, typ)
- f.Offset = int64(off)
- if sym != nil {
- f.Nname = ir.NewNameAt(pos, sym)
- }
- f.SetIsDDD(ddd)
- return f
-}
-
-func (r *importReader) exoticField() *types.Field {
- pos := r.pos()
- sym := r.exoticSym()
- off := r.uint64()
- typ := r.exoticType()
- note := r.string()
- f := types.NewField(pos, sym, typ)
- f.Offset = int64(off)
- if sym != nil {
- f.Nname = ir.NewNameAt(pos, sym)
- }
- f.Note = note
- return f
-}
-
-func (r *importReader) exoticSym() *types.Sym {
- name := r.string()
- if name == "" {
- return nil
- }
- var pkg *types.Pkg
- if types.IsExported(name) {
- pkg = types.LocalPkg
- } else {
- pkg = r.pkg()
- }
- return pkg.Lookup(name)
-}
-
-func (p *iimporter) typAt(off uint64) *types.Type {
- t, ok := p.typCache[off]
- if !ok {
- if off < predeclReserved {
- base.Fatalf("predeclared type missing from cache: %d", off)
- }
- t = p.newReader(off-predeclReserved, nil).typ1()
- // Ensure size is calculated for imported types. Since CL 283313, the compiler
- // does not compile the function immediately when it sees them. Instead, functions
- // are pushed to compile queue, then draining from the queue for compiling.
- // During this process, the size calculation is disabled, so it is not safe for
- // calculating size during SSA generation anymore. See issue #44732.
- //
- // No need to calc sizes for re-instantiated generic types, and
- // they are not necessarily resolved until the top-level type is
- // defined (because of recursive types).
- if t.OrigType() == nil || !t.HasTParam() {
- types.CheckSize(t)
- }
- p.typCache[off] = t
- }
- return t
-}
-
-func (r *importReader) typ1() *types.Type {
- switch k := r.kind(); k {
- default:
- base.Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
- return nil
-
- case definedType:
- // We might be called from within doInline, in which
- // case Sym.Def can point to declared parameters
- // instead of the top-level types. Also, we don't
- // support inlining functions with local defined
- // types. Therefore, this must be a package-scope
- // type.
- n := expandDecl(r.qualifiedIdent())
- if n.Op() != ir.OTYPE {
- base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
- }
- return n.Type()
- case pointerType:
- return types.NewPtr(r.typ())
- case sliceType:
- return types.NewSlice(r.typ())
- case arrayType:
- n := r.uint64()
- return types.NewArray(r.typ(), int64(n))
- case chanType:
- dir := types.ChanDir(r.uint64())
- return types.NewChan(r.typ(), dir)
- case mapType:
- return types.NewMap(r.typ(), r.typ())
-
- case signatureType:
- r.setPkg()
- return r.signature(nil, nil)
-
- case structType:
- r.setPkg()
-
- fs := make([]*types.Field, r.uint64())
- for i := range fs {
- pos := r.pos()
- sym := r.selector()
- typ := r.typ()
- emb := r.bool()
- note := r.string()
-
- f := types.NewField(pos, sym, typ)
- if emb {
- f.Embedded = 1
- }
- f.Note = note
- fs[i] = f
- }
-
- return types.NewStruct(r.currPkg, fs)
-
- case interfaceType:
- r.setPkg()
-
- embeddeds := make([]*types.Field, r.uint64())
- for i := range embeddeds {
- pos := r.pos()
- typ := r.typ()
-
- embeddeds[i] = types.NewField(pos, nil, typ)
- }
-
- methods := make([]*types.Field, r.uint64())
- for i := range methods {
- pos := r.pos()
- sym := r.selector()
- typ := r.signature(types.FakeRecv(), nil)
-
- methods[i] = types.NewField(pos, sym, typ)
- }
-
- if len(embeddeds)+len(methods) == 0 {
- return types.Types[types.TINTER]
- }
-
- t := types.NewInterface(r.currPkg, append(embeddeds, methods...), false)
-
- // Ensure we expand the interface in the frontend (#25055).
- types.CheckSize(t)
- return t
-
- case typeParamType:
- if r.p.exportVersion < iexportVersionGenerics {
- base.Fatalf("unexpected type param type")
- }
- // Similar to code for defined types, since we "declared"
- // typeparams to deal with recursion (typeparam is used within its
- // own type bound).
- ident := r.qualifiedIdent()
- if ident.Sym().Def != nil {
- return ident.Sym().Def.(*ir.Name).Type()
- }
- n := expandDecl(ident)
- if n.Op() != ir.OTYPE {
- base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
- }
- return n.Type()
-
- case instanceType:
- if r.p.exportVersion < iexportVersionGenerics {
- base.Fatalf("unexpected instantiation type")
- }
- pos := r.pos()
- len := r.uint64()
- targs := make([]*types.Type, len)
- for i := range targs {
- targs[i] = r.typ()
- }
- baseType := r.typ()
- t := Instantiate(pos, baseType, targs)
- return t
-
- case unionType:
- if r.p.exportVersion < iexportVersionGenerics {
- base.Fatalf("unexpected instantiation type")
- }
- nt := int(r.uint64())
- terms := make([]*types.Type, nt)
- tildes := make([]bool, nt)
- for i := range terms {
- tildes[i] = r.bool()
- terms[i] = r.typ()
- }
- return types.NewUnion(terms, tildes)
- }
-}
-
-func (r *importReader) kind() itag {
- return itag(r.uint64())
-}
-
-func (r *importReader) signature(recv *types.Field, tparams []*types.Field) *types.Type {
- params := r.paramList()
- results := r.paramList()
- if n := len(params); n > 0 {
- params[n-1].SetIsDDD(r.bool())
- }
- return types.NewSignature(r.currPkg, recv, tparams, params, results)
-}
-
-func (r *importReader) typeList() []*types.Type {
- n := r.uint64()
- if n == 0 {
- return nil
- }
- ts := make([]*types.Type, n)
- for i := range ts {
- ts[i] = r.typ()
- if ts[i].IsTypeParam() {
- ts[i].SetIndex(i)
- }
- }
- return ts
-}
-
-func (r *importReader) tparamList() []*types.Field {
- n := r.uint64()
- if n == 0 {
- return nil
- }
- fs := make([]*types.Field, n)
- for i := range fs {
- typ := r.typ()
- typ.SetIndex(i)
- fs[i] = types.NewField(typ.Pos(), typ.Sym(), typ)
- }
- return fs
-}
-
-func (r *importReader) paramList() []*types.Field {
- fs := make([]*types.Field, r.uint64())
- for i := range fs {
- fs[i] = r.param()
- }
- return fs
-}
-
-func (r *importReader) param() *types.Field {
- return types.NewField(r.pos(), r.localIdent(), r.typ())
-}
-
-func (r *importReader) bool() bool {
- return r.uint64() != 0
-}
-
-func (r *importReader) int64() int64 {
- n, err := binary.ReadVarint(r)
- if err != nil {
- base.Fatalf("readVarint: %v", err)
- }
- return n
-}
-
-func (r *importReader) uint64() uint64 {
- n, err := binary.ReadUvarint(r)
- if err != nil {
- base.Fatalf("readVarint: %v", err)
- }
- return n
-}
-
-func (r *importReader) byte() byte {
- x, err := r.ReadByte()
- if err != nil {
- base.Fatalf("declReader.ReadByte: %v", err)
- }
- return x
-}
-
-// Compiler-specific extensions.
-
-func (r *importReader) constExt(n *ir.Name) {
- switch n.Type() {
- case types.UntypedFloat:
- n.SetVal(r.mprat(n.Val()))
- case types.UntypedComplex:
- v := n.Val()
- re := r.mprat(constant.Real(v))
- im := r.mprat(constant.Imag(v))
- n.SetVal(makeComplex(re, im))
- }
-}
-
-func (r *importReader) varExt(n *ir.Name) {
- r.linkname(n.Sym())
- r.symIdx(n.Sym())
-}
-
-func (r *importReader) funcExt(n *ir.Name) {
- r.linkname(n.Sym())
- r.symIdx(n.Sym())
-
- n.Func.ABI = obj.ABI(r.uint64())
-
- // Make sure //go:noinline pragma is imported (so stenciled functions have
- // same noinline status as the corresponding generic function.)
- n.Func.Pragma = ir.PragmaFlag(r.uint64())
-
- // Escape analysis.
- for _, fs := range &types.RecvsParams {
- for _, f := range fs(n.Type()).FieldSlice() {
- f.Note = r.string()
- }
- }
-
- // Inline body.
- if u := r.uint64(); u > 0 {
- n.Func.Inl = &ir.Inline{
- Cost: int32(u - 1),
- CanDelayResults: r.bool(),
- }
- n.Func.Endlineno = r.pos()
- }
-}
-
-func (r *importReader) methExt(m *types.Field) {
- if r.bool() {
- m.SetNointerface(true)
- }
- r.funcExt(m.Nname.(*ir.Name))
-}
-
-func (r *importReader) linkname(s *types.Sym) {
- s.Linkname = r.string()
-}
-
-func (r *importReader) symIdx(s *types.Sym) {
- lsym := s.Linksym()
- idx := int32(r.int64())
- if idx != -1 {
- if s.Linkname != "" {
- base.Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
- }
- lsym.SymIdx = idx
- lsym.Set(obj.AttrIndexed, true)
- }
-}
-
-func (r *importReader) typeExt(t *types.Type) {
- t.SetNotInHeap(r.bool())
- SetBaseTypeIndex(t, r.int64(), r.int64())
-}
-
func SetBaseTypeIndex(t *types.Type, i, pi int64) {
if t.Obj() == nil {
base.Fatalf("SetBaseTypeIndex on non-defined type %v", t)
}
return i[0]
}
-
-func (r *importReader) doInline(fn *ir.Func) {
- if len(fn.Inl.Body) != 0 {
- base.Fatalf("%v already has inline body", fn)
- }
-
- //fmt.Printf("Importing %s\n", fn.Nname.Sym().Name)
- r.funcBody(fn)
-
- importlist = append(importlist, fn)
-
- if base.Flag.E > 0 && base.Flag.LowerM > 2 {
- if base.Flag.LowerM > 3 {
- fmt.Printf("inl body for %v %v: %+v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
- } else {
- fmt.Printf("inl body for %v %v: %v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
- }
- }
-}
-
-// ----------------------------------------------------------------------------
-// Inlined function bodies
-
-// Approach: Read nodes and use them to create/declare the same data structures
-// as done originally by the (hidden) parser by closely following the parser's
-// original code. In other words, "parsing" the import data (which happens to
-// be encoded in binary rather textual form) is the best way at the moment to
-// re-establish the syntax tree's invariants. At some future point we might be
-// able to avoid this round-about way and create the rewritten nodes directly,
-// possibly avoiding a lot of duplicate work (name resolution, type checking).
-//
-// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
-// unrefined nodes (since this is what the importer uses). The respective case
-// entries are unreachable in the importer.
-
-func (r *importReader) funcBody(fn *ir.Func) {
- outerfn := r.curfn
- r.curfn = fn
-
- // Import local declarations.
- fn.Inl.Dcl = r.readFuncDcls(fn)
-
- // Import function body.
- body := r.stmtList()
- if body == nil {
- // Make sure empty body is not interpreted as
- // no inlineable body (see also parser.fnbody)
- // (not doing so can cause significant performance
- // degradation due to unnecessary calls to empty
- // functions).
- body = []ir.Node{}
- }
- ir.VisitList(body, func(n ir.Node) {
- n.SetTypecheck(1)
- })
- fn.Inl.Body = body
-
- r.curfn = outerfn
- if base.Flag.W >= 3 {
- fmt.Printf("Imported for %v", fn)
- ir.DumpList("", fn.Inl.Body)
- }
-}
-
-func (r *importReader) readNames(fn *ir.Func) []*ir.Name {
- dcls := make([]*ir.Name, r.int64())
- for i := range dcls {
- n := ir.NewDeclNameAt(r.pos(), ir.ONAME, r.localIdent())
- n.Class = ir.PAUTO // overwritten below for parameters/results
- n.Curfn = fn
- n.SetType(r.typ())
- dcls[i] = n
- }
- r.allDcls = append(r.allDcls, dcls...)
- return dcls
-}
-
-func (r *importReader) readFuncDcls(fn *ir.Func) []*ir.Name {
- dcls := r.readNames(fn)
-
- // Fixup parameter classes and associate with their
- // signature's type fields.
- i := 0
- fix := func(f *types.Field, class ir.Class) {
- if class == ir.PPARAM && (f.Sym == nil || f.Sym.Name == "_") {
- return
- }
- n := dcls[i]
- n.Class = class
- f.Nname = n
- i++
- }
-
- typ := fn.Type()
- if recv := typ.Recv(); recv != nil {
- fix(recv, ir.PPARAM)
- }
- for _, f := range typ.Params().FieldSlice() {
- fix(f, ir.PPARAM)
- }
- for _, f := range typ.Results().FieldSlice() {
- fix(f, ir.PPARAMOUT)
- }
- return dcls
-}
-
-func (r *importReader) localName() *ir.Name {
- i := r.int64()
- if i == -1 {
- return ir.BlankNode.(*ir.Name)
- }
- if i < 0 {
- return r.allClosureVars[-i-2]
- }
- return r.allDcls[i]
-}
-
-func (r *importReader) stmtList() []ir.Node {
- var list []ir.Node
- for {
- n := r.node()
- if n == nil {
- break
- }
- // OBLOCK nodes are not written to the import data directly,
- // but the handling of ODCL calls liststmt, which creates one.
- // Inline them into the statement list.
- if n.Op() == ir.OBLOCK {
- n := n.(*ir.BlockStmt)
- list = append(list, n.List...)
- continue
- }
- if len(list) > 0 {
- // check for an optional label that can only immediately
- // precede a for/range/select/switch statement.
- if last := list[len(list)-1]; last.Op() == ir.OLABEL {
- label := last.(*ir.LabelStmt).Label
- switch n.Op() {
- case ir.OFOR:
- n.(*ir.ForStmt).Label = label
- case ir.ORANGE:
- n.(*ir.RangeStmt).Label = label
- case ir.OSELECT:
- n.(*ir.SelectStmt).Label = label
- case ir.OSWITCH:
- n.(*ir.SwitchStmt).Label = label
- }
- }
- }
- list = append(list, n)
- }
- return list
-}
-
-func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
- namedTypeSwitch := isNamedTypeSwitch(switchExpr)
-
- cases := make([]*ir.CaseClause, r.uint64())
- for i := range cases {
- cas := ir.NewCaseStmt(r.pos(), nil, nil)
- cas.List = r.stmtList()
- if namedTypeSwitch {
- cas.Var = r.localName()
- cas.Var.Defn = switchExpr
- }
- cas.Body = r.stmtList()
- cases[i] = cas
- }
- return cases
-}
-
-func (r *importReader) commList() []*ir.CommClause {
- cases := make([]*ir.CommClause, r.uint64())
- for i := range cases {
- pos := r.pos()
- defaultCase := r.bool()
- var comm ir.Node
- if !defaultCase {
- comm = r.node()
- }
- cases[i] = ir.NewCommStmt(pos, comm, r.stmtList())
- }
- return cases
-}
-
-func (r *importReader) exprList() []ir.Node {
- var list []ir.Node
- for {
- n := r.expr()
- if n == nil {
- break
- }
- list = append(list, n)
- }
- return list
-}
-
-func (r *importReader) expr() ir.Node {
- n := r.node()
- if n != nil && n.Op() == ir.OBLOCK {
- n := n.(*ir.BlockStmt)
- base.Fatalf("unexpected block node: %v", n)
- }
- return n
-}
-
-// TODO(gri) split into expr and stmt
-func (r *importReader) node() ir.Node {
- op := r.op()
- switch op {
- // expressions
- // case OPAREN:
- // unreachable - unpacked by exporter
-
- case ir.ONIL:
- pos := r.pos()
- typ := r.typ()
-
- n := ir.NewNilExpr(pos)
- n.SetType(typ)
- return n
-
- case ir.OLITERAL:
- pos := r.pos()
- typ := r.typ()
-
- n := ir.NewBasicLit(pos, r.value(typ))
- n.SetType(typ)
- return n
-
- case ir.ONONAME:
- isKey := r.bool()
- var n ir.Node = r.qualifiedIdent()
- // Key ONONAME entries should not be resolved - they should
- // stay as identifiers.
- if !isKey {
- n = Resolve(n)
- }
- typ := r.typ()
- if n.Type() == nil {
- n.SetType(typ)
- }
- return n
-
- case ir.ONAME:
- isBuiltin := r.bool()
- if isBuiltin {
- pkg := types.BuiltinPkg
- if r.bool() {
- pkg = types.UnsafePkg
- }
- return pkg.Lookup(r.string()).Def.(*ir.Name)
- }
- return r.localName()
-
- // case OPACK, ONONAME:
- // unreachable - should have been resolved by typechecking
-
- case ir.OTYPE:
- return ir.TypeNode(r.typ())
-
- case ir.ODYNAMICTYPE:
- n := ir.NewDynamicType(r.pos(), r.expr())
- if r.bool() {
- n.ITab = r.expr()
- }
- n.SetType(r.typ())
- return n
-
- case ir.OTYPESW:
- pos := r.pos()
- var tag *ir.Ident
- if s := r.localIdent(); s != nil {
- tag = ir.NewIdent(pos, s)
- }
- return ir.NewTypeSwitchGuard(pos, tag, r.expr())
-
- // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
- // unreachable - should have been resolved by typechecking
-
- case ir.OCLOSURE:
- //println("Importing CLOSURE")
- pos := r.pos()
- r.setPkg()
- typ := r.signature(nil, nil)
- r.setPkg()
-
- // All the remaining code below is similar to (*noder).funcLit(), but
- // with Dcls and ClosureVars lists already set up
- fn := ir.NewClosureFunc(pos, true)
- fn.Nname.SetType(typ)
-
- cvars := make([]*ir.Name, r.int64())
- for i := range cvars {
- cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical())
- if cvars[i].Defn == nil {
- base.Fatalf("bad import of closure variable")
- }
- }
- fn.ClosureVars = cvars
- r.allClosureVars = append(r.allClosureVars, cvars...)
-
- fn.Inl = &ir.Inline{}
- // Read in the Dcls and Body of the closure after temporarily
- // setting r.curfn to fn.
- r.funcBody(fn)
- fn.Dcl = fn.Inl.Dcl
- fn.Body = fn.Inl.Body
- if len(fn.Body) == 0 {
- // An empty closure must be represented as a single empty
- // block statement, else it will be dropped.
- fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
- }
- fn.Inl = nil
-
- ir.FinishCaptureNames(pos, r.curfn, fn)
-
- clo := fn.OClosure
- clo.SetType(typ)
- return clo
-
- case ir.OSTRUCTLIT:
- pos := r.pos()
- typ := r.typ()
- list := r.fieldList()
- return ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, typ, list)
-
- case ir.OCOMPLIT:
- pos := r.pos()
- t := r.typ()
- return ir.NewCompLitExpr(pos, ir.OCOMPLIT, t, r.exprList())
-
- case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
- pos := r.pos()
- typ := r.typ()
- list := r.exprList()
- n := ir.NewCompLitExpr(pos, op, typ, list)
- if op == ir.OSLICELIT {
- n.Len = int64(r.uint64())
- }
- return n
-
- case ir.OKEY:
- return ir.NewKeyExpr(r.pos(), r.expr(), r.expr())
-
- // case OSTRUCTKEY:
- // unreachable - handled in case OSTRUCTLIT by elemList
-
- case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
- pos := r.pos()
- expr := r.expr()
- sel := r.exoticSelector()
- n := ir.NewSelectorExpr(pos, op, expr, sel)
- n.SetType(r.exoticType())
- switch op {
- case ir.OXDOT:
- hasSelection := r.bool()
- // We reconstruct n.Selection for method calls on
- // generic types and method calls due to type param
- // bounds. Otherwise, n.Selection is nil.
- if hasSelection {
- n1 := ir.NewSelectorExpr(pos, op, expr, sel)
- AddImplicitDots(n1)
- var m *types.Field
- if n1.X.Type().IsTypeParam() {
- genType := n1.X.Type().Bound()
- m = Lookdot1(n1, sel, genType, genType.AllMethods(), 1)
- } else {
- genType := types.ReceiverBaseType(n1.X.Type())
- if genType.IsInstantiatedGeneric() {
- genType = genType.OrigType()
- }
- m = Lookdot1(n1, sel, genType, genType.Methods(), 1)
- }
- assert(m != nil)
- n.Selection = m
- }
- case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER:
- n.Selection = r.exoticField()
- case ir.OMETHEXPR:
- n = typecheckMethodExpr(n).(*ir.SelectorExpr)
- case ir.ODOTMETH, ir.OMETHVALUE:
- // These require a Lookup to link to the correct declaration.
- rcvrType := expr.Type()
- typ := n.Type()
- n.Selection = Lookdot(n, rcvrType, 1)
- if op == ir.OMETHVALUE {
- // Lookdot clobbers the opcode and type, undo that.
- n.SetOp(op)
- n.SetType(typ)
- }
- }
- return n
-
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- n := ir.NewTypeAssertExpr(r.pos(), r.expr(), r.typ())
- n.SetOp(op)
- return n
-
- case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
- n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), nil)
- if r.bool() {
- n.RType = r.expr()
- }
- if r.bool() {
- n.ITab = r.expr()
- }
- n.SetType(r.typ())
- return n
-
- case ir.OINDEX, ir.OINDEXMAP:
- n := ir.NewIndexExpr(r.pos(), r.expr(), r.expr())
- n.SetOp(op)
- n.SetType(r.exoticType())
- if op == ir.OINDEXMAP {
- n.Assigned = r.bool()
- }
- return n
-
- case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
- pos, x := r.pos(), r.expr()
- low, high := r.exprsOrNil()
- var max ir.Node
- if op.IsSlice3() {
- max = r.expr()
- }
- n := ir.NewSliceExpr(pos, op, x, low, high, max)
- n.SetType(r.typ())
- return n
-
- case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARR, ir.OSLICE2ARRPTR:
- n := ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
- n.SetImplicit(r.bool())
- return n
-
- case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE,
- ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN,
- ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA:
- pos := r.pos()
- switch op {
- case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
- init := r.stmtList()
- n := ir.NewBinaryExpr(pos, op, r.expr(), r.expr())
- n.SetInit(init)
- n.SetType(r.typ())
- return n
- case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
- n := ir.NewUnaryExpr(pos, op, r.expr())
- if op != ir.OPANIC {
- n.SetType(r.typ())
- }
- return n
- case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
- init := r.stmtList()
- n := ir.NewCallExpr(pos, op, nil, r.exprList())
- n.SetInit(init)
- if op == ir.OAPPEND {
- n.IsDDD = r.bool()
- }
- if op == ir.OAPPEND || op == ir.ORECOVER {
- n.SetType(r.typ())
- }
- return n
- }
- // ir.OMAKE
- goto error
-
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG:
- pos := r.pos()
- init := r.stmtList()
- n := ir.NewCallExpr(pos, ir.OCALL, r.expr(), r.exprList())
- n.SetOp(op)
- n.SetInit(init)
- n.IsDDD = r.bool()
- n.SetType(r.exoticType())
- return n
-
- case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
- pos := r.pos()
- typ := r.typ()
- list := r.exprList()
- var len_, cap_ ir.Node
- if len(list) > 0 {
- len_ = list[0]
- }
- if len(list) > 1 {
- cap_ = list[1]
- }
- n := ir.NewMakeExpr(pos, op, len_, cap_)
- n.SetType(typ)
- return n
-
- case ir.OLINKSYMOFFSET:
- pos := r.pos()
- name := r.string()
- off := r.uint64()
- typ := r.typ()
- return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
-
- // unary expressions
- case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
- n := ir.NewUnaryExpr(r.pos(), op, r.expr())
- n.SetType(r.typ())
- return n
-
- case ir.OADDR, ir.OPTRLIT:
- pos := r.pos()
- expr := r.expr()
- expr.SetTypecheck(1) // we do this for all nodes after importing, but do it now so markAddrOf can see it.
- n := NodAddrAt(pos, expr)
- n.SetOp(op)
- n.SetType(r.typ())
- return n
-
- case ir.ODEREF:
- n := ir.NewStarExpr(r.pos(), r.expr())
- n.SetType(r.typ())
- return n
-
- // binary expressions
- case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
- ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
- n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
- n.SetType(r.typ())
- return n
-
- case ir.OANDAND, ir.OOROR:
- n := ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr())
- n.SetType(r.typ())
- return n
-
- case ir.OSEND:
- return ir.NewSendStmt(r.pos(), r.expr(), r.expr())
-
- case ir.OADDSTR:
- pos := r.pos()
- list := r.exprList()
- n := ir.NewAddStringExpr(pos, list)
- n.SetType(r.typ())
- return n
-
- // --------------------------------------------------------------------
- // statements
- case ir.ODCL:
- var stmts ir.Nodes
- n := r.localName()
- stmts.Append(ir.NewDecl(n.Pos(), ir.ODCL, n))
- stmts.Append(ir.NewAssignStmt(n.Pos(), n, nil))
- return ir.NewBlockStmt(n.Pos(), stmts)
-
- // case OASWB:
- // unreachable - never exported
-
- case ir.OAS:
- pos := r.pos()
- init := r.stmtList()
- n := ir.NewAssignStmt(pos, r.expr(), r.expr())
- n.SetInit(init)
- n.Def = r.bool()
- return n
-
- case ir.OASOP:
- n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
- if !r.bool() {
- n.Y = ir.NewInt(1)
- n.IncDec = true
- } else {
- n.Y = r.expr()
- }
- return n
-
- case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
- pos := r.pos()
- init := r.stmtList()
- n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList())
- n.SetInit(init)
- n.Def = r.bool()
- return n
-
- case ir.ORETURN:
- return ir.NewReturnStmt(r.pos(), r.exprList())
-
- // case ORETJMP:
- // unreachable - generated by compiler for trampolin routines (not exported)
-
- case ir.OGO, ir.ODEFER:
- return ir.NewGoDeferStmt(r.pos(), op, r.expr())
-
- case ir.OIF:
- pos, init := r.pos(), r.stmtList()
- n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
- n.SetInit(init)
- return n
-
- case ir.OFOR:
- pos, init := r.pos(), r.stmtList()
- cond, post := r.exprsOrNil()
- n := ir.NewForStmt(pos, nil, cond, post, r.stmtList())
- n.SetInit(init)
- return n
-
- case ir.ORANGE:
- pos, init := r.pos(), r.stmtList()
- k, v := r.exprsOrNil()
- n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
- n.SetInit(init)
- return n
-
- case ir.OSELECT:
- pos := r.pos()
- init := r.stmtList()
- n := ir.NewSelectStmt(pos, r.commList())
- n.SetInit(init)
- return n
-
- case ir.OSWITCH:
- pos := r.pos()
- init := r.stmtList()
- x, _ := r.exprsOrNil()
- n := ir.NewSwitchStmt(pos, x, r.caseList(x))
- n.SetInit(init)
- return n
-
- // case OCASE:
- // handled by caseList
-
- case ir.OFALL:
- return ir.NewBranchStmt(r.pos(), ir.OFALL, nil)
-
- // case OEMPTY:
- // unreachable - not emitted by exporter
-
- case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
- pos := r.pos()
- var sym *types.Sym
- if label := r.string(); label != "" {
- sym = Lookup(label)
- }
- return ir.NewBranchStmt(pos, op, sym)
-
- case ir.OLABEL:
- return ir.NewLabelStmt(r.pos(), Lookup(r.string()))
-
- case ir.OEND:
- return nil
-
- case ir.OFUNCINST:
- pos := r.pos()
- x := r.expr()
- targs := make([]ir.Ntype, r.uint64())
- for i := range targs {
- targs[i] = ir.TypeNode(r.typ())
- }
- n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
- n.SetType(r.typ())
- return n
-
- case ir.OSELRECV2:
- pos := r.pos()
- init := r.stmtList()
- n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList())
- n.SetInit(init)
- n.Def = r.bool()
- return n
-
- default:
- base.Fatalf("cannot import %v (%d) node\n"+
- "\t==> please file an issue and assign to gri@", op, int(op))
- panic("unreachable") // satisfy compiler
- }
-error:
- base.Fatalf("cannot import %v (%d) node\n"+
- "\t==> please file an issue and assign to khr@", op, int(op))
- panic("unreachable") // satisfy compiler
-}
-
-func (r *importReader) op() ir.Op {
- if debug && r.uint64() != magic {
- base.Fatalf("import stream has desynchronized")
- }
- return ir.Op(r.uint64())
-}
-
-func (r *importReader) fieldList() []ir.Node {
- list := make([]ir.Node, r.uint64())
- for i := range list {
- list[i] = ir.NewStructKeyExpr(r.pos(), r.exoticField(), r.expr())
- }
- return list
-}
-
-func (r *importReader) exprsOrNil() (a, b ir.Node) {
- ab := r.uint64()
- if ab&1 != 0 {
- a = r.expr()
- }
- if ab&2 != 0 {
- b = r.node()
- }
- return
-}
-
-// NewIncompleteNamedType returns a TFORW type t with name specified by sym, such
-// that t.nod and sym.Def are set correctly. If there are any RParams for the type,
-// they should be set soon after creating the TFORW type, before creating the
-// underlying type. That ensures that the HasTParam and HasShape flags will be set
-// properly, in case this type is part of some mutually recursive type.
-func NewIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type {
- name := ir.NewDeclNameAt(pos, ir.OTYPE, sym)
- forw := types.NewNamed(name)
- name.SetType(forw)
- sym.Def = name
- return forw
-}
-
-// Instantiate creates a new named type which is the instantiation of the base
-// named generic type, with the specified type args.
-func Instantiate(pos src.XPos, baseType *types.Type, targs []*types.Type) *types.Type {
- baseSym := baseType.Sym()
- if strings.Index(baseSym.Name, "[") >= 0 {
- base.Fatalf("arg to Instantiate is not a base generic type")
- }
- name := InstTypeName(baseSym.Name, targs)
- instSym := baseSym.Pkg.Lookup(name)
- if instSym.Def != nil {
- // May match existing type from previous import or
- // types2-to-types1 conversion.
- t := instSym.Def.Type()
- if t.Kind() != types.TFORW {
- return t
- }
- // Or, we have started creating this type in (*TSubster).Typ, but its
- // underlying type was not completed yet, so we need to add this type
- // to deferredInstStack, if not already there.
- found := false
- for _, t2 := range deferredInstStack {
- if t2 == t {
- found = true
- break
- }
- }
- if !found {
- deferredInstStack = append(deferredInstStack, t)
- }
- return t
- }
-
- t := NewIncompleteNamedType(baseType.Pos(), instSym)
- t.SetRParams(targs)
- t.SetOrigType(baseType)
-
- // baseType may still be TFORW or its methods may not be fully filled in
- // (since we are in the middle of importing it). So, delay call to
- // substInstType until we get back up to the top of the current top-most
- // type import.
- deferredInstStack = append(deferredInstStack, t)
-
- return t
-}
-
-var deferredInstStack []*types.Type
-var deferInst int
-
-// deferDoInst defers substitution on instantiated types until we are at the
-// top-most defined type, so the base types are fully defined.
-func deferDoInst() {
- deferInst++
-}
-
-func resumeDoInst() {
- if deferInst == 1 {
- for len(deferredInstStack) > 0 {
- t := deferredInstStack[0]
- deferredInstStack = deferredInstStack[1:]
- substInstType(t, t.OrigType(), t.RParams())
- }
- }
- deferInst--
-}
-
-// doInst creates a new instantiation type (which will be added to
-// deferredInstStack for completion later) for an incomplete type encountered
-// during a type substitution for an instantiation. This is needed for
-// instantiations of mutually recursive types.
-func doInst(t *types.Type) *types.Type {
- assert(t.Kind() == types.TFORW)
- return Instantiate(t.Pos(), t.OrigType(), t.RParams())
-}
-
-// substInstType completes the instantiation of a generic type by doing a
-// substitution on the underlying type itself and any methods. t is the
-// instantiation being created, baseType is the base generic type, and targs are
-// the type arguments that baseType is being instantiated with.
-func substInstType(t *types.Type, baseType *types.Type, targs []*types.Type) {
- assert(t.Kind() == types.TFORW)
- subst := Tsubster{
- Tparams: baseType.RParams(),
- Targs: targs,
- SubstForwFunc: doInst,
- }
- t.SetUnderlying(subst.Typ(baseType.Underlying()))
-
- newfields := make([]*types.Field, baseType.Methods().Len())
- for i, f := range baseType.Methods().Slice() {
- if !f.IsMethod() || types.IsInterfaceMethod(f.Type) {
- // Do a normal substitution if this is a non-method (which
- // means this must be an interface used as a constraint) or
- // an interface method.
- t2 := subst.Typ(f.Type)
- newfields[i] = types.NewField(f.Pos, f.Sym, t2)
- continue
- }
- recvType := f.Type.Recv().Type
- if recvType.IsPtr() {
- recvType = recvType.Elem()
- }
- // Substitute in the method using the type params used in the
- // method (not the type params in the definition of the generic type).
- msubst := Tsubster{
- Tparams: recvType.RParams(),
- Targs: targs,
- SubstForwFunc: doInst,
- }
- t2 := msubst.Typ(f.Type)
- oldsym := f.Nname.Sym()
- newsym := MakeFuncInstSym(oldsym, targs, true, true)
- 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
- }
- t.Methods().Set(newfields)
- if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TINTER && t.Methods().Len() > 0 {
- // Generate all the methods for a new fully-instantiated,
- // non-interface, non-shape type.
- NeedInstType(t)
- }
-}
package typecheck
import (
- "bytes"
"fmt"
"sort"
"strings"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
- "cmd/internal/objabi"
"cmd/internal/src"
)
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{}
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) {
+++ /dev/null
-// 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)
-}
+++ /dev/null
-// 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)
-}
"cmd/compile/internal/base"
"cmd/internal/src"
"fmt"
- "strings"
"sync"
)
}
}
-// 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.