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