summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/noder
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/compile/internal/noder
parentInitial commit. (diff)
downloadgolang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz
golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/noder')
-rw-r--r--src/cmd/compile/internal/noder/codes.go87
-rw-r--r--src/cmd/compile/internal/noder/decl.go352
-rw-r--r--src/cmd/compile/internal/noder/export.go35
-rw-r--r--src/cmd/compile/internal/noder/expr.go474
-rw-r--r--src/cmd/compile/internal/noder/func.go73
-rw-r--r--src/cmd/compile/internal/noder/helpers.go284
-rw-r--r--src/cmd/compile/internal/noder/import.go389
-rw-r--r--src/cmd/compile/internal/noder/irgen.go516
-rw-r--r--src/cmd/compile/internal/noder/lex.go184
-rw-r--r--src/cmd/compile/internal/noder/lex_test.go122
-rw-r--r--src/cmd/compile/internal/noder/linker.go337
-rw-r--r--src/cmd/compile/internal/noder/noder.go496
-rw-r--r--src/cmd/compile/internal/noder/object.go205
-rw-r--r--src/cmd/compile/internal/noder/posmap.go86
-rw-r--r--src/cmd/compile/internal/noder/quirks.go79
-rw-r--r--src/cmd/compile/internal/noder/reader.go4029
-rw-r--r--src/cmd/compile/internal/noder/scopes.go64
-rw-r--r--src/cmd/compile/internal/noder/sizes.go162
-rw-r--r--src/cmd/compile/internal/noder/stencil.go2334
-rw-r--r--src/cmd/compile/internal/noder/stmt.go353
-rw-r--r--src/cmd/compile/internal/noder/transform.go1085
-rw-r--r--src/cmd/compile/internal/noder/types.go516
-rw-r--r--src/cmd/compile/internal/noder/unified.go441
-rw-r--r--src/cmd/compile/internal/noder/validate.go132
-rw-r--r--src/cmd/compile/internal/noder/writer.go2735
25 files changed, 15570 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go
new file mode 100644
index 0000000..c1ee8d1
--- /dev/null
+++ b/src/cmd/compile/internal/noder/codes.go
@@ -0,0 +1,87 @@
+// 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 "internal/pkgbits"
+
+// A codeStmt distinguishes among statement encodings.
+type codeStmt int
+
+func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 }
+func (c codeStmt) Value() int { return int(c) }
+
+const (
+ stmtEnd codeStmt = iota
+ stmtLabel
+ stmtBlock
+ stmtExpr
+ stmtSend
+ stmtAssign
+ stmtAssignOp
+ stmtIncDec
+ stmtBranch
+ stmtCall
+ stmtReturn
+ stmtIf
+ stmtFor
+ stmtSwitch
+ stmtSelect
+)
+
+// A codeExpr distinguishes among expression encodings.
+type codeExpr int
+
+func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr }
+func (c codeExpr) Value() int { return int(c) }
+
+// TODO(mdempsky): Split expr into addr, for lvalues.
+const (
+ exprConst codeExpr = iota
+ exprLocal // local variable
+ exprGlobal // global variable or function
+ exprCompLit
+ exprFuncLit
+ exprFieldVal
+ exprMethodVal
+ exprMethodExpr
+ exprIndex
+ exprSlice
+ exprAssert
+ exprUnaryOp
+ exprBinaryOp
+ exprCall
+ exprConvert
+ exprNew
+ exprMake
+ exprNil
+ exprFuncInst
+ exprRecv
+ exprReshape
+)
+
+type codeAssign int
+
+func (c codeAssign) Marker() pkgbits.SyncMarker { return pkgbits.SyncAssign }
+func (c codeAssign) Value() int { return int(c) }
+
+const (
+ assignBlank codeAssign = iota
+ assignDef
+ assignExpr
+)
+
+// A codeDecl distinguishes among declaration encodings.
+type codeDecl int
+
+func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl }
+func (c codeDecl) Value() int { return int(c) }
+
+const (
+ declEnd codeDecl = iota
+ declFunc
+ declMethod
+ declVar
+ declOther
+)
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
new file mode 100644
index 0000000..07353cc
--- /dev/null
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -0,0 +1,352 @@
+// 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/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// TODO(mdempsky): Skip blank declarations? Probably only safe
+// for declarations without pragmas.
+
+func (g *irgen) decls(res *ir.Nodes, decls []syntax.Decl) {
+ for _, decl := range decls {
+ switch decl := decl.(type) {
+ case *syntax.ConstDecl:
+ g.constDecl(res, decl)
+ case *syntax.FuncDecl:
+ g.funcDecl(res, decl)
+ case *syntax.TypeDecl:
+ if ir.CurFunc == nil {
+ continue // already handled in irgen.generate
+ }
+ g.typeDecl(res, decl)
+ case *syntax.VarDecl:
+ g.varDecl(res, decl)
+ default:
+ g.unhandled("declaration", decl)
+ }
+ }
+}
+
+func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
+ g.pragmaFlags(decl.Pragma, 0)
+
+ // Get the imported package's path, as resolved already by types2
+ // and gcimporter. This is the same path as would be computed by
+ // parseImportPath.
+ switch pkgNameOf(g.info, decl).Imported().Path() {
+ case "unsafe":
+ p.importedUnsafe = true
+ case "embed":
+ p.importedEmbed = true
+ }
+}
+
+// pkgNameOf returns the PkgName associated with the given ImportDecl.
+func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName {
+ if name := decl.LocalPkgName; name != nil {
+ return info.Defs[name].(*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/export.go b/src/cmd/compile/internal/noder/export.go
new file mode 100644
index 0000000..263cdc2
--- /dev/null
+++ b/src/cmd/compile/internal/noder/export.go
@@ -0,0 +1,35 @@
+// 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 (
+ "bytes"
+ "fmt"
+ "io"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/typecheck"
+ "cmd/internal/bio"
+)
+
+func WriteExports(out *bio.Writer) {
+ var data bytes.Buffer
+
+ if base.Debug.Unified != 0 {
+ data.WriteByte('u')
+ writeUnifiedExport(&data)
+ } else {
+ typecheck.WriteExports(&data, true)
+ }
+
+ // The linker also looks for the $$ marker - use char after $$ to distinguish format.
+ out.WriteString("\n$$B\n") // indicate binary export format
+ io.Copy(out, &data)
+ out.WriteString("\n$$\n")
+
+ if base.Debug.Export != 0 {
+ fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, data.Len())
+ }
+}
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
new file mode 100644
index 0000000..f391339
--- /dev/null
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -0,0 +1,474 @@
+// 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 (
+ "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 substition, 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:
+ return nil
+ case *syntax.ListExpr:
+ return expr.ElemList
+ default:
+ return []syntax.Expr{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.
+func constExprOp(expr syntax.Expr) ir.Op {
+ switch expr := expr.(type) {
+ default:
+ panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr))
+
+ case *syntax.BasicLit:
+ return ir.OLITERAL
+ case *syntax.Name, *syntax.SelectorExpr:
+ return ir.ONAME
+ case *syntax.CallExpr:
+ return ir.OCALL
+ case *syntax.Operation:
+ if expr.Y == nil {
+ return unOps[expr.Op]
+ }
+ return binOps[expr.Op]
+ }
+}
+
+func unparen(expr syntax.Expr) syntax.Expr {
+ for {
+ paren, ok := expr.(*syntax.ParenExpr)
+ if !ok {
+ return expr
+ }
+ expr = paren.X
+ }
+}
diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go
new file mode 100644
index 0000000..6077b34
--- /dev/null
+++ b/src/cmd/compile/internal/noder/func.go
@@ -0,0 +1,73 @@
+// 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/helpers.go b/src/cmd/compile/internal/noder/helpers.go
new file mode 100644
index 0000000..4ef46a4
--- /dev/null
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -0,0 +1,284 @@
+// 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/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+// Helpers for constructing typed IR nodes.
+//
+// TODO(mdempsky): Move into their own package so they can be easily
+// reused by iimport and frontend optimizations.
+
+type ImplicitNode interface {
+ ir.Node
+ SetImplicit(x bool)
+}
+
+// Implicit returns n after marking it as Implicit.
+func Implicit(n ImplicitNode) ImplicitNode {
+ n.SetImplicit(true)
+ return n
+}
+
+// typed returns n after setting its type to typ.
+func typed(typ *types.Type, n ir.Node) ir.Node {
+ n.SetType(typ)
+ n.SetTypecheck(1)
+ return n
+}
+
+// Values
+
+func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node {
+ orig := ir.NewRawOrigExpr(pos, op, raw)
+ return ir.NewConstExpr(val, typed(typ, orig))
+}
+
+// FixValue returns val after converting and truncating it as
+// appropriate for typ.
+func FixValue(typ *types.Type, val constant.Value) constant.Value {
+ assert(typ.Kind() != types.TFORW)
+ switch {
+ case typ.IsInteger():
+ val = constant.ToInt(val)
+ case typ.IsFloat():
+ val = constant.ToFloat(val)
+ case typ.IsComplex():
+ val = constant.ToComplex(val)
+ }
+ if !typ.IsUntyped() {
+ val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val()
+ }
+ if !typ.IsTypeParam() {
+ ir.AssertValidTypeForConst(typ, val)
+ }
+ return val
+}
+
+func Nil(pos src.XPos, typ *types.Type) ir.Node {
+ return typed(typ, ir.NewNilExpr(pos))
+}
+
+// Expressions
+
+func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
+ n := typecheck.NodAddrAt(pos, x)
+ typed(types.NewPtr(x.Type()), n)
+ return n
+}
+
+func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
+ return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
+}
+
+func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExpr {
+ switch op {
+ case ir.OADD:
+ n := ir.NewBinaryExpr(pos, op, x, y)
+ typed(typ, n)
+ return n
+ default:
+ n := ir.NewBinaryExpr(pos, op, x, y)
+ typed(x.Type(), n)
+ return n
+ }
+}
+
+func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr {
+ n := ir.NewBinaryExpr(pos, op, x, y)
+ typed(typ, n)
+ return n
+}
+
+func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr {
+ n := ir.NewStarExpr(pos, x)
+ typed(typ, n)
+ return n
+}
+
+func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
+ op, typ := ir.ODOT, x.Type()
+ if typ.IsPtr() {
+ op, typ = ir.ODOTPTR, typ.Elem()
+ }
+ if !typ.IsStruct() {
+ base.FatalfAt(pos, "DotField of non-struct: %L", x)
+ }
+
+ // TODO(mdempsky): This is the backend's responsibility.
+ types.CalcSize(typ)
+
+ field := typ.Field(index)
+ return dot(pos, field.Type, op, x, field)
+}
+
+func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
+ method := method(x.Type(), index)
+
+ // Method value.
+ typ := typecheck.NewMethodType(method.Type, nil)
+ return dot(pos, typ, ir.OMETHVALUE, x, method)
+}
+
+// MethodExpr returns a OMETHEXPR node with the indicated index into the methods
+// of typ. The receiver type is set from recv, which is different from typ if the
+// method was accessed via embedded fields. Similarly, the X value of the
+// ir.SelectorExpr is recv, the original OTYPE node before passing through the
+// embedded fields.
+func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr {
+ method := method(embed, index)
+ typ := typecheck.NewMethodType(method.Type, recv.Type())
+ // 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 recv.Sym() == nil {
+ typecheck.NeedRuntimeType(recv.Type())
+ }
+ return dot(pos, typ, ir.OMETHEXPR, recv, method)
+}
+
+func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
+ n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
+ n.Selection = selection
+ typed(typ, n)
+ return n
+}
+
+// TODO(mdempsky): Move to package types.
+func method(typ *types.Type, index int) *types.Field {
+ if typ.IsInterface() {
+ return typ.AllMethods().Index(index)
+ }
+ return types.ReceiverBaseType(typ).Methods().Index(index)
+}
+
+func Index(pos src.XPos, typ *types.Type, x, index ir.Node) *ir.IndexExpr {
+ n := ir.NewIndexExpr(pos, x, index)
+ typed(typ, n)
+ return n
+}
+
+func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) *ir.SliceExpr {
+ op := ir.OSLICE
+ if max != nil {
+ op = ir.OSLICE3
+ }
+ n := ir.NewSliceExpr(pos, op, x, low, high, max)
+ typed(typ, n)
+ return n
+}
+
+func Unary(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node) ir.Node {
+ switch op {
+ case ir.OADDR:
+ return Addr(pos, x)
+ case ir.ODEREF:
+ return Deref(pos, typ, x)
+ }
+
+ if op == ir.ORECV {
+ if typ.IsFuncArgStruct() && typ.NumFields() == 2 {
+ // Remove the second boolean type (if provided by type2),
+ // since that works better with the rest of the compiler
+ // (which will add it back in later).
+ assert(typ.Field(1).Type.Kind() == types.TBOOL)
+ typ = typ.Field(0).Type
+ }
+ }
+ return typed(typ, ir.NewUnaryExpr(pos, op, x))
+}
+
+// Statements
+
+var one = constant.MakeInt64(1)
+
+func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
+ assert(x.Type() != nil)
+ bl := ir.NewBasicLit(pos, one)
+ if x.Type().HasTParam() {
+ // If the operand is generic, then types2 will have proved it must be
+ // a type that fits with increment/decrement, so just set the type of
+ // "one" to n.Type(). This works even for types that are eventually
+ // float or complex.
+ typed(x.Type(), bl)
+ } else {
+ bl = typecheck.DefaultLit(bl, x.Type())
+ }
+ return ir.NewAssignOpStmt(pos, op, x, bl)
+}
+
+func idealType(tv syntax.TypeAndValue) types2.Type {
+ // The gc backend expects all expressions to have a concrete type, and
+ // types2 mostly satisfies this expectation already. But there are a few
+ // cases where the Go spec doesn't require converting to concrete type,
+ // and so types2 leaves them untyped. So we need to fix those up here.
+ typ := tv.Type
+ if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
+ switch basic.Kind() {
+ case types2.UntypedNil:
+ // ok; can appear in type switch case clauses
+ // TODO(mdempsky): Handle as part of type switches instead?
+ case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
+ // Untyped rhs of non-constant shift, e.g. x << 1.0.
+ // If we have a constant value, it must be an int >= 0.
+ if tv.Value != nil {
+ s := constant.ToInt(tv.Value)
+ assert(s.Kind() == constant.Int && constant.Sign(s) >= 0)
+ }
+ typ = types2.Typ[types2.Uint]
+ case types2.UntypedBool:
+ typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
+ case types2.UntypedString:
+ typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
+ default:
+ return nil
+ }
+ }
+ return typ
+}
+
+func isTypeParam(t types2.Type) bool {
+ _, ok := t.(*types2.TypeParam)
+ return ok
+}
+
+// isNotInHeap reports whether typ is or contains an element of type
+// runtime/internal/sys.NotInHeap.
+func isNotInHeap(typ types2.Type) bool {
+ if named, ok := typ.(*types2.Named); ok {
+ if obj := named.Obj(); obj.Name() == "nih" && obj.Pkg().Path() == "runtime/internal/sys" {
+ return true
+ }
+ typ = named.Underlying()
+ }
+
+ switch typ := typ.(type) {
+ case *types2.Array:
+ return isNotInHeap(typ.Elem())
+ case *types2.Struct:
+ for i := 0; i < typ.NumFields(); i++ {
+ if isNotInHeap(typ.Field(i).Type()) {
+ return true
+ }
+ }
+ return false
+ default:
+ return false
+ }
+}
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
new file mode 100644
index 0000000..8b017ec
--- /dev/null
+++ b/src/cmd/compile/internal/noder/import.go
@@ -0,0 +1,389 @@
+// Copyright 2009 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 (
+ "errors"
+ "fmt"
+ "internal/buildcfg"
+ "internal/pkgbits"
+ "os"
+ pathpkg "path"
+ "runtime"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/importer"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/archive"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+)
+
+type gcimports struct {
+ ctxt *types2.Context
+ packages map[string]*types2.Package
+}
+
+func (m *gcimports) Import(path string) (*types2.Package, error) {
+ return m.ImportFrom(path, "" /* no vendoring */, 0)
+}
+
+func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
+ if mode != 0 {
+ panic("mode must be 0")
+ }
+
+ _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages)
+ return pkg, err
+}
+
+func isDriveLetter(b byte) bool {
+ return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
+}
+
+// is this path a local name? begins with ./ or ../ or /
+func islocalname(name string) bool {
+ return strings.HasPrefix(name, "/") ||
+ runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
+ strings.HasPrefix(name, "./") || name == "." ||
+ strings.HasPrefix(name, "../") || name == ".."
+}
+
+func openPackage(path string) (*os.File, error) {
+ if islocalname(path) {
+ if base.Flag.NoLocalImports {
+ return nil, errors.New("local imports disallowed")
+ }
+
+ if base.Flag.Cfg.PackageFile != nil {
+ return os.Open(base.Flag.Cfg.PackageFile[path])
+ }
+
+ // try .a before .o. important for building libraries:
+ // if there is an array.o in the array.a library,
+ // want to find all of array.a, not just array.o.
+ if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
+ return file, nil
+ }
+ return nil, errors.New("file not found")
+ }
+
+ // local imports should be canonicalized already.
+ // don't want to see "encoding/../encoding/base64"
+ // as different from "encoding/base64".
+ if q := pathpkg.Clean(path); q != path {
+ return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
+ }
+
+ if base.Flag.Cfg.PackageFile != nil {
+ return os.Open(base.Flag.Cfg.PackageFile[path])
+ }
+
+ for _, dir := range base.Flag.Cfg.ImportDirs {
+ if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
+ return file, nil
+ }
+ }
+
+ if buildcfg.GOROOT != "" {
+ suffix := ""
+ if base.Flag.InstallSuffix != "" {
+ suffix = "_" + base.Flag.InstallSuffix
+ } else if base.Flag.Race {
+ suffix = "_race"
+ } else if base.Flag.MSan {
+ suffix = "_msan"
+ } else if base.Flag.ASan {
+ suffix = "_asan"
+ }
+
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
+ return file, nil
+ }
+ }
+ return nil, errors.New("file not found")
+}
+
+// resolveImportPath resolves an import path as it appears in a Go
+// source file to the package's full path.
+func resolveImportPath(path string) (string, error) {
+ // The package name main is no longer reserved,
+ // but we reserve the import path "main" to identify
+ // the main package, just as we reserve the import
+ // path "math" to identify the standard math package.
+ if path == "main" {
+ return "", errors.New("cannot import \"main\"")
+ }
+
+ if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
+ return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
+ }
+
+ if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
+ path = mapped
+ }
+
+ if islocalname(path) {
+ if path[0] == '/' {
+ return "", errors.New("import path cannot be absolute path")
+ }
+
+ prefix := base.Flag.D
+ if prefix == "" {
+ // Questionable, but when -D isn't specified, historically we
+ // resolve local import paths relative to the directory the
+ // compiler's current directory, not the respective source
+ // file's directory.
+ prefix = base.Ctxt.Pathname
+ }
+ path = pathpkg.Join(prefix, path)
+
+ if err := checkImportPath(path, true); err != nil {
+ return "", err
+ }
+ }
+
+ return path, nil
+}
+
+// readImportFile reads the import file for the given package path and
+// returns its types.Pkg representation. If packages is non-nil, the
+// types2.Package representation is also returned.
+func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
+ path, err = resolveImportPath(path)
+ if err != nil {
+ return
+ }
+
+ if path == "unsafe" {
+ pkg1, pkg2 = types.UnsafePkg, types2.Unsafe
+
+ // TODO(mdempsky): Investigate if this actually matters. Why would
+ // the linker or runtime care whether a package imported unsafe?
+ if !pkg1.Direct {
+ pkg1.Direct = true
+ target.Imports = append(target.Imports, pkg1)
+ }
+
+ return
+ }
+
+ pkg1 = types.NewPkg(path, "")
+ if packages != nil {
+ pkg2 = packages[path]
+ assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete()))
+ }
+
+ if pkg1.Direct {
+ return
+ }
+ pkg1.Direct = true
+ target.Imports = append(target.Imports, pkg1)
+
+ f, err := openPackage(path)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ r, end, err := findExportData(f)
+ if err != nil {
+ return
+ }
+
+ if base.Debug.Export != 0 {
+ fmt.Printf("importing %s (%s)\n", path, f.Name())
+ }
+
+ c, err := r.ReadByte()
+ if err != nil {
+ return
+ }
+
+ pos := r.Offset()
+
+ // Map export data section into memory as a single large
+ // string. This reduces heap fragmentation and allows returning
+ // individual substrings very efficiently.
+ var data string
+ data, err = base.MapFile(r.File(), pos, end-pos)
+ if err != nil {
+ return
+ }
+
+ switch c {
+ case 'u':
+ if !buildcfg.Experiment.Unified {
+ base.Fatalf("unexpected export data format")
+ }
+
+ // TODO(mdempsky): This seems a bit clunky.
+ data = strings.TrimSuffix(data, "\n$$\n")
+
+ pr := pkgbits.NewPkgDecoder(pkg1.Path, data)
+
+ // Read package descriptors for both types2 and compiler backend.
+ readPackage(newPkgReader(pr), pkg1, false)
+ pkg2 = importer.ReadPackage(env, packages, pr)
+
+ case 'i':
+ if buildcfg.Experiment.Unified {
+ base.Fatalf("unexpected export data format")
+ }
+
+ typecheck.ReadImports(pkg1, data)
+
+ if packages != nil {
+ pkg2, err = importer.ImportData(packages, data, path)
+ if err != nil {
+ return
+ }
+ }
+
+ default:
+ // Indexed format is distinguished by an 'i' byte,
+ // whereas previous export formats started with 'c', 'd', or 'v'.
+ err = fmt.Errorf("unexpected package format byte: %v", c)
+ return
+ }
+
+ err = addFingerprint(path, f, end)
+ return
+}
+
+// findExportData returns a *bio.Reader positioned at the start of the
+// binary export data section, and a file offset for where to stop
+// reading.
+func findExportData(f *os.File) (r *bio.Reader, end int64, err error) {
+ r = bio.NewReader(f)
+
+ // check object header
+ line, err := r.ReadString('\n')
+ if err != nil {
+ return
+ }
+
+ if line == "!<arch>\n" { // package archive
+ // package export block should be first
+ sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF"))
+ if sz <= 0 {
+ err = errors.New("not a package file")
+ return
+ }
+ end = r.Offset() + sz
+ line, err = r.ReadString('\n')
+ if err != nil {
+ return
+ }
+ } else {
+ // Not an archive; provide end of file instead.
+ // TODO(mdempsky): I don't think this happens anymore.
+ var fi os.FileInfo
+ fi, err = f.Stat()
+ if err != nil {
+ return
+ }
+ end = fi.Size()
+ }
+
+ if !strings.HasPrefix(line, "go object ") {
+ err = fmt.Errorf("not a go object file: %s", line)
+ return
+ }
+ if expect := objabi.HeaderString(); line != expect {
+ err = fmt.Errorf("object is [%s] expected [%s]", line, expect)
+ return
+ }
+
+ // process header lines
+ for !strings.HasPrefix(line, "$$") {
+ line, err = r.ReadString('\n')
+ if err != nil {
+ return
+ }
+ }
+
+ // Expect $$B\n to signal binary import format.
+ if line != "$$B\n" {
+ err = errors.New("old export format no longer supported (recompile library)")
+ return
+ }
+
+ return
+}
+
+// addFingerprint reads the linker fingerprint included at the end of
+// the exportdata.
+func addFingerprint(path string, f *os.File, end int64) error {
+ const eom = "\n$$\n"
+ var fingerprint goobj.FingerprintType
+
+ var buf [len(fingerprint) + len(eom)]byte
+ if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil {
+ return err
+ }
+
+ // Caller should have given us the end position of the export data,
+ // which should end with the "\n$$\n" marker. As a consistency check
+ // to make sure we're reading at the right offset, make sure we
+ // found the marker.
+ if s := string(buf[len(fingerprint):]); s != eom {
+ return fmt.Errorf("expected $$ marker, but found %q", s)
+ }
+
+ copy(fingerprint[:], buf[:])
+ base.Ctxt.AddImport(path, fingerprint)
+
+ return nil
+}
+
+func checkImportPath(path string, allowSpace bool) error {
+ if path == "" {
+ return errors.New("import path is empty")
+ }
+
+ if strings.Contains(path, "\x00") {
+ return errors.New("import path contains NUL")
+ }
+
+ for ri := range base.ReservedImports {
+ if path == ri {
+ return fmt.Errorf("import path %q is reserved and cannot be used", path)
+ }
+ }
+
+ for _, r := range path {
+ switch {
+ case r == utf8.RuneError:
+ return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
+ case r < 0x20 || r == 0x7f:
+ return fmt.Errorf("import path contains control character: %q", path)
+ case r == '\\':
+ return fmt.Errorf("import path contains backslash; use slash: %q", path)
+ case !allowSpace && unicode.IsSpace(r):
+ return fmt.Errorf("import path contains space character: %q", path)
+ case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
+ return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
+ }
+ }
+
+ return nil
+}
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
new file mode 100644
index 0000000..d034926
--- /dev/null
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -0,0 +1,516 @@
+// 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 (
+ "fmt"
+ "regexp"
+ "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"
+)
+
+var versionErrorRx = regexp.MustCompile(`requires go[0-9]+\.[0-9]+ or later`)
+
+// checkFiles configures and runs the types2 checker on the given
+// parsed source files and then returns the result.
+func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
+ if base.SyntaxErrors() != 0 {
+ base.ErrorExit()
+ }
+
+ // setup and syntax error reporting
+ var m posMap
+ files := make([]*syntax.File, len(noders))
+ for i, p := range noders {
+ m.join(&p.posMap)
+ files[i] = p.file
+ }
+
+ // typechecking
+ ctxt := types2.NewContext()
+ importer := gcimports{
+ ctxt: ctxt,
+ packages: make(map[string]*types2.Package),
+ }
+ conf := types2.Config{
+ Context: ctxt,
+ GoVersion: base.Flag.Lang,
+ IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode
+ Error: func(err error) {
+ terr := err.(types2.Error)
+ msg := terr.Msg
+ // if we have a version error, hint at the -lang setting
+ if versionErrorRx.MatchString(msg) {
+ msg = fmt.Sprintf("%s (-lang was set to %s; check go.mod)", msg, base.Flag.Lang)
+ }
+ base.ErrorfAt(m.makeXPos(terr.Pos), "%s", msg)
+ },
+ Importer: &importer,
+ Sizes: &gcSizes{},
+ OldComparableSemantics: base.Flag.OldComparable, // default is new comparable semantics
+ }
+ info := &types2.Info{
+ StoreTypesInSyntax: true,
+ Defs: make(map[*syntax.Name]types2.Object),
+ Uses: make(map[*syntax.Name]types2.Object),
+ Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
+ Implicits: make(map[syntax.Node]types2.Object),
+ Scopes: make(map[syntax.Node]*types2.Scope),
+ Instances: make(map[*syntax.Name]types2.Instance),
+ // expand as needed
+ }
+
+ pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
+
+ // Check for anonymous interface cycles (#56103).
+ if base.Debug.InterfaceCycles == 0 {
+ var f cycleFinder
+ for _, file := range files {
+ syntax.Inspect(file, func(n syntax.Node) bool {
+ if n, ok := n.(*syntax.InterfaceType); ok {
+ if f.hasCycle(n.GetTypeInfo().Type.(*types2.Interface)) {
+ base.ErrorfAt(m.makeXPos(n.Pos()), "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)")
+
+ for typ := range f.cyclic {
+ f.cyclic[typ] = false // suppress duplicate errors
+ }
+ }
+ return false
+ }
+ return true
+ })
+ }
+ }
+
+ // Implementation restriction: we don't allow not-in-heap types to
+ // be used as type arguments (#54765).
+ {
+ type nihTarg struct {
+ pos src.XPos
+ typ types2.Type
+ }
+ var nihTargs []nihTarg
+
+ for name, inst := range info.Instances {
+ for i := 0; i < inst.TypeArgs.Len(); i++ {
+ if targ := inst.TypeArgs.At(i); isNotInHeap(targ) {
+ nihTargs = append(nihTargs, nihTarg{m.makeXPos(name.Pos()), targ})
+ }
+ }
+ }
+ sort.Slice(nihTargs, func(i, j int) bool {
+ ti, tj := nihTargs[i], nihTargs[j]
+ return ti.pos.Before(tj.pos)
+ })
+ for _, targ := range nihTargs {
+ base.ErrorfAt(targ.pos, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ)
+ }
+ }
+
+ base.ExitIfErrors()
+ if err != nil {
+ base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
+ }
+
+ 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.Crawl(p.file, func(n syntax.Node) bool {
+ g.validate(n)
+ return false
+ })
+ }
+
+ 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
+}
+
+// hasCycle reports whether typ is part of an anonymous interface cycle.
+func (f *cycleFinder) hasCycle(typ *types2.Interface) bool {
+ // We use Method instead of ExplicitMethod to implicitly expand any
+ // embedded interfaces. Then we just need to walk any anonymous
+ // types, keeping track of *types2.Interface types we visit along
+ // the way.
+ for i := 0; i < typ.NumMethods(); i++ {
+ if f.visit(typ.Method(i).Type()) {
+ return true
+ }
+ }
+ return false
+}
+
+// visit recursively walks typ0 to check any referenced interface types.
+func (f *cycleFinder) visit(typ0 types2.Type) bool {
+ for { // loop for tail recursion
+ switch typ := typ0.(type) {
+ default:
+ base.Fatalf("unexpected type: %T", typ)
+
+ case *types2.Basic, *types2.Named, *types2.TypeParam:
+ return false // named types cannot be part of an anonymous cycle
+ case *types2.Pointer:
+ typ0 = typ.Elem()
+ case *types2.Array:
+ typ0 = typ.Elem()
+ case *types2.Chan:
+ typ0 = typ.Elem()
+ case *types2.Map:
+ if f.visit(typ.Key()) {
+ return true
+ }
+ typ0 = typ.Elem()
+ case *types2.Slice:
+ typ0 = typ.Elem()
+
+ case *types2.Struct:
+ for i := 0; i < typ.NumFields(); i++ {
+ if f.visit(typ.Field(i).Type()) {
+ return true
+ }
+ }
+ return false
+
+ case *types2.Interface:
+ // The empty interface (e.g., "any") cannot be part of a cycle.
+ if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 0 {
+ return false
+ }
+
+ // As an optimization, we wait to allocate cyclic here, after
+ // we've found at least one other (non-empty) anonymous
+ // interface. This means when a cycle is present, we need to
+ // make an extra recursive call to actually detect it. But for
+ // most packages, it allows skipping the map allocation
+ // entirely.
+ if x, ok := f.cyclic[typ]; ok {
+ return x
+ }
+ if f.cyclic == nil {
+ f.cyclic = make(map[*types2.Interface]bool)
+ }
+ f.cyclic[typ] = true
+ if f.hasCycle(typ) {
+ return true
+ }
+ f.cyclic[typ] = false
+ return false
+
+ case *types2.Signature:
+ return f.visit(typ.Params()) || f.visit(typ.Results())
+ case *types2.Tuple:
+ for i := 0; i < typ.Len(); i++ {
+ if f.visit(typ.At(i).Type()) {
+ return true
+ }
+ }
+ return false
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go
new file mode 100644
index 0000000..c964eca
--- /dev/null
+++ b/src/cmd/compile/internal/noder/lex.go
@@ -0,0 +1,184 @@
+// Copyright 2009 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 (
+ "fmt"
+ "internal/buildcfg"
+ "strings"
+
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+)
+
+func isSpace(c rune) bool {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
+func isQuoted(s string) bool {
+ return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
+}
+
+const (
+ funcPragmas = ir.Nointerface |
+ ir.Noescape |
+ ir.Norace |
+ ir.Nosplit |
+ ir.Noinline |
+ ir.NoCheckPtr |
+ ir.RegisterParams | // TODO(register args) remove after register abi is working
+ ir.CgoUnsafeArgs |
+ ir.UintptrKeepAlive |
+ ir.UintptrEscapes |
+ ir.Systemstack |
+ ir.Nowritebarrier |
+ ir.Nowritebarrierrec |
+ ir.Yeswritebarrierrec
+)
+
+func pragmaFlag(verb string) ir.PragmaFlag {
+ switch verb {
+ case "go:build":
+ return ir.GoBuildPragma
+ case "go:nointerface":
+ if buildcfg.Experiment.FieldTrack {
+ return ir.Nointerface
+ }
+ case "go:noescape":
+ return ir.Noescape
+ case "go:norace":
+ return ir.Norace
+ case "go:nosplit":
+ return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972)
+ case "go:noinline":
+ return ir.Noinline
+ case "go:nocheckptr":
+ return ir.NoCheckPtr
+ case "go:systemstack":
+ return ir.Systemstack
+ case "go:nowritebarrier":
+ return ir.Nowritebarrier
+ case "go:nowritebarrierrec":
+ return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier
+ case "go:yeswritebarrierrec":
+ return ir.Yeswritebarrierrec
+ case "go:cgo_unsafe_args":
+ return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968)
+ case "go:uintptrkeepalive":
+ return ir.UintptrKeepAlive
+ case "go:uintptrescapes":
+ // This directive extends //go:uintptrkeepalive by forcing
+ // uintptr arguments to escape to the heap, which makes stack
+ // growth safe.
+ return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive
+ case "go:registerparams": // TODO(register args) remove after register abi is working
+ return ir.RegisterParams
+ }
+ return 0
+}
+
+// pragcgo is called concurrently if files are parsed concurrently.
+func (p *noder) pragcgo(pos syntax.Pos, text string) {
+ f := pragmaFields(text)
+
+ verb := strings.TrimPrefix(f[0], "go:")
+ f[0] = verb
+
+ switch verb {
+ case "cgo_export_static", "cgo_export_dynamic":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
+ default:
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)})
+ return
+ }
+ case "cgo_import_dynamic":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
+ case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
+ f[3] = strings.Trim(f[3], `"`)
+ if buildcfg.GOOS == "aix" && f[3] != "" {
+ // On Aix, library pattern must be "lib.a/object.o"
+ // or "lib.a/libname.so.X"
+ n := strings.Split(f[3], "/")
+ if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || (!strings.HasSuffix(n[1], ".o") && !strings.Contains(n[1], ".so.")) {
+ p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`})
+ return
+ }
+ }
+ default:
+ p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`})
+ return
+ }
+ case "cgo_import_static":
+ switch {
+ case len(f) == 2 && !isQuoted(f[1]):
+ default:
+ p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`})
+ return
+ }
+ case "cgo_dynamic_linker":
+ switch {
+ case len(f) == 2 && isQuoted(f[1]):
+ f[1] = strings.Trim(f[1], `"`)
+ default:
+ p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`})
+ return
+ }
+ case "cgo_ldflag":
+ switch {
+ case len(f) == 2 && isQuoted(f[1]):
+ f[1] = strings.Trim(f[1], `"`)
+ default:
+ p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`})
+ return
+ }
+ default:
+ return
+ }
+ p.pragcgobuf = append(p.pragcgobuf, f)
+}
+
+// pragmaFields is similar to strings.FieldsFunc(s, isSpace)
+// but does not split when inside double quoted regions and always
+// splits before the start and after the end of a double quoted region.
+// pragmaFields does not recognize escaped quotes. If a quote in s is not
+// closed the part after the opening quote will not be returned as a field.
+func pragmaFields(s string) []string {
+ var a []string
+ inQuote := false
+ fieldStart := -1 // Set to -1 when looking for start of field.
+ for i, c := range s {
+ switch {
+ case c == '"':
+ if inQuote {
+ inQuote = false
+ a = append(a, s[fieldStart:i+1])
+ fieldStart = -1
+ } else {
+ inQuote = true
+ if fieldStart >= 0 {
+ a = append(a, s[fieldStart:i])
+ }
+ fieldStart = i
+ }
+ case !inQuote && isSpace(c):
+ if fieldStart >= 0 {
+ a = append(a, s[fieldStart:i])
+ fieldStart = -1
+ }
+ default:
+ if fieldStart == -1 {
+ fieldStart = i
+ }
+ }
+ }
+ if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
+ a = append(a, s[fieldStart:])
+ }
+ return a
+}
diff --git a/src/cmd/compile/internal/noder/lex_test.go b/src/cmd/compile/internal/noder/lex_test.go
new file mode 100644
index 0000000..85a3f06
--- /dev/null
+++ b/src/cmd/compile/internal/noder/lex_test.go
@@ -0,0 +1,122 @@
+// Copyright 2016 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 (
+ "reflect"
+ "runtime"
+ "testing"
+
+ "cmd/compile/internal/syntax"
+)
+
+func eq(a, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := 0; i < len(a); i++ {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func TestPragmaFields(t *testing.T) {
+ var tests = []struct {
+ in string
+ want []string
+ }{
+ {"", []string{}},
+ {" \t ", []string{}},
+ {`""""`, []string{`""`, `""`}},
+ {" a'b'c ", []string{"a'b'c"}},
+ {"1 2 3 4", []string{"1", "2", "3", "4"}},
+ {"\n☺\t☹\n", []string{"☺", "☹"}},
+ {`"1 2 " 3 " 4 5"`, []string{`"1 2 "`, `3`, `" 4 5"`}},
+ {`"1""2 3""4"`, []string{`"1"`, `"2 3"`, `"4"`}},
+ {`12"34"`, []string{`12`, `"34"`}},
+ {`12"34 `, []string{`12`}},
+ }
+
+ for _, tt := range tests {
+ got := pragmaFields(tt.in)
+ if !eq(got, tt.want) {
+ t.Errorf("pragmaFields(%q) = %v; want %v", tt.in, got, tt.want)
+ continue
+ }
+ }
+}
+
+func TestPragcgo(t *testing.T) {
+ type testStruct struct {
+ in string
+ want []string
+ }
+
+ var tests = []testStruct{
+ {`go:cgo_export_dynamic local`, []string{`cgo_export_dynamic`, `local`}},
+ {`go:cgo_export_dynamic local remote`, []string{`cgo_export_dynamic`, `local`, `remote`}},
+ {`go:cgo_export_dynamic local' remote'`, []string{`cgo_export_dynamic`, `local'`, `remote'`}},
+ {`go:cgo_export_static local`, []string{`cgo_export_static`, `local`}},
+ {`go:cgo_export_static local remote`, []string{`cgo_export_static`, `local`, `remote`}},
+ {`go:cgo_export_static local' remote'`, []string{`cgo_export_static`, `local'`, `remote'`}},
+ {`go:cgo_import_dynamic local`, []string{`cgo_import_dynamic`, `local`}},
+ {`go:cgo_import_dynamic local remote`, []string{`cgo_import_dynamic`, `local`, `remote`}},
+ {`go:cgo_import_static local`, []string{`cgo_import_static`, `local`}},
+ {`go:cgo_import_static local'`, []string{`cgo_import_static`, `local'`}},
+ {`go:cgo_dynamic_linker "/path/"`, []string{`cgo_dynamic_linker`, `/path/`}},
+ {`go:cgo_dynamic_linker "/p ath/"`, []string{`cgo_dynamic_linker`, `/p ath/`}},
+ {`go:cgo_ldflag "arg"`, []string{`cgo_ldflag`, `arg`}},
+ {`go:cgo_ldflag "a rg"`, []string{`cgo_ldflag`, `a rg`}},
+ }
+
+ if runtime.GOOS != "aix" {
+ tests = append(tests, []testStruct{
+ {`go:cgo_import_dynamic local remote "library"`, []string{`cgo_import_dynamic`, `local`, `remote`, `library`}},
+ {`go:cgo_import_dynamic local' remote' "lib rary"`, []string{`cgo_import_dynamic`, `local'`, `remote'`, `lib rary`}},
+ }...)
+ } else {
+ // cgo_import_dynamic with a library is slightly different on AIX
+ // as the library field must follow the pattern [libc.a/object.o].
+ tests = append(tests, []testStruct{
+ {`go:cgo_import_dynamic local remote "lib.a/obj.o"`, []string{`cgo_import_dynamic`, `local`, `remote`, `lib.a/obj.o`}},
+ // This test must fail.
+ {`go:cgo_import_dynamic local' remote' "library"`, []string{`<unknown position>: usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}},
+ }...)
+
+ }
+
+ var p noder
+ var nopos syntax.Pos
+ for _, tt := range tests {
+
+ p.err = make(chan syntax.Error)
+ gotch := make(chan [][]string, 1)
+ go func() {
+ p.pragcgobuf = nil
+ p.pragcgo(nopos, tt.in)
+ if p.pragcgobuf != nil {
+ gotch <- p.pragcgobuf
+ }
+ }()
+
+ select {
+ case e := <-p.err:
+ want := tt.want[0]
+ if e.Error() != want {
+ t.Errorf("pragcgo(%q) = %q; want %q", tt.in, e, want)
+ continue
+ }
+ case got := <-gotch:
+ want := [][]string{tt.want}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, want)
+ continue
+ }
+ }
+
+ }
+}
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go
new file mode 100644
index 0000000..5d0459d
--- /dev/null
+++ b/src/cmd/compile/internal/noder/linker.go
@@ -0,0 +1,337 @@
+// 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 (
+ "internal/pkgbits"
+ "io"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/types"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+)
+
+// This file implements the unified IR linker, which combines the
+// local package's stub data with imported package data to produce a
+// complete export data file. It also rewrites the compiler's
+// extension data sections based on the results of compilation (e.g.,
+// the function inlining cost and linker symbol index assignments).
+//
+// TODO(mdempsky): Using the name "linker" here is confusing, because
+// readers are likely to mistake references to it for cmd/link. But
+// there's a shortage of good names for "something that combines
+// multiple parts into a cohesive whole"... e.g., "assembler" and
+// "compiler" are also already taken.
+
+// TODO(mdempsky): Should linker go into pkgbits? Probably the
+// low-level linking details can be moved there, but the logic for
+// handling extension data needs to stay in the compiler.
+
+// A linker combines a package's stub export data with any referenced
+// elements from imported packages into a single, self-contained
+// export data file.
+type linker struct {
+ pw pkgbits.PkgEncoder
+
+ pkgs map[string]pkgbits.Index
+ decls map[*types.Sym]pkgbits.Index
+ bodies map[*types.Sym]pkgbits.Index
+}
+
+// relocAll ensures that all elements specified by pr and relocs are
+// copied into the output export data file, and returns the
+// corresponding indices in the output.
+func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt {
+ res := make([]pkgbits.RelocEnt, len(relocs))
+ for i, rent := range relocs {
+ rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx)
+ res[i] = rent
+ }
+ return res
+}
+
+// relocIdx ensures a single element is copied into the output export
+// data file, and returns the corresponding index in the output.
+func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index {
+ assert(pr != nil)
+
+ absIdx := pr.AbsIdx(k, idx)
+
+ if newidx := pr.newindex[absIdx]; newidx != 0 {
+ return ^newidx
+ }
+
+ var newidx pkgbits.Index
+ switch k {
+ case pkgbits.RelocString:
+ newidx = l.relocString(pr, idx)
+ case pkgbits.RelocPkg:
+ newidx = l.relocPkg(pr, idx)
+ case pkgbits.RelocObj:
+ newidx = l.relocObj(pr, idx)
+
+ default:
+ // Generic relocations.
+ //
+ // TODO(mdempsky): Deduplicate more sections? In fact, I think
+ // every section could be deduplicated. This would also be easier
+ // if we do external relocations.
+
+ w := l.pw.NewEncoderRaw(k)
+ l.relocCommon(pr, &w, k, idx)
+ newidx = w.Idx
+ }
+
+ pr.newindex[absIdx] = ^newidx
+
+ return newidx
+}
+
+// relocString copies the specified string from pr into the output
+// export data file, deduplicating it against other strings.
+func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
+ return l.pw.StringIdx(pr.StringIdx(idx))
+}
+
+// relocPkg copies the specified package from pr into the output
+// export data file, rewriting its import path to match how it was
+// imported.
+//
+// TODO(mdempsky): Since CL 391014, we already have the compilation
+// unit's import path, so there should be no need to rewrite packages
+// anymore.
+func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
+ path := pr.PeekPkgPath(idx)
+
+ if newidx, ok := l.pkgs[path]; ok {
+ return newidx
+ }
+
+ r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef)
+ w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
+ l.pkgs[path] = w.Idx
+
+ // TODO(mdempsky): We end up leaving an empty string reference here
+ // from when the package was originally written as "". Probably not
+ // a big deal, but a little annoying. Maybe relocating
+ // cross-references in place is the way to go after all.
+ w.Relocs = l.relocAll(pr, r.Relocs)
+
+ _ = r.String() // original path
+ w.String(path)
+
+ io.Copy(&w.Data, &r.Data)
+
+ return w.Flush()
+}
+
+// relocObj copies the specified object from pr into the output export
+// data file, rewriting its compiler-private extension data (e.g.,
+// adding inlining cost and escape analysis results for functions).
+func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
+ path, name, tag := pr.PeekObj(idx)
+ sym := types.NewPkg(path, "").Lookup(name)
+
+ if newidx, ok := l.decls[sym]; ok {
+ return newidx
+ }
+
+ if tag == pkgbits.ObjStub && path != "builtin" && path != "unsafe" {
+ pri, ok := objReader[sym]
+ if !ok {
+ base.Fatalf("missing reader for %q.%v", path, name)
+ }
+ assert(ok)
+
+ pr = pri.pr
+ idx = pri.idx
+
+ path2, name2, tag2 := pr.PeekObj(idx)
+ sym2 := types.NewPkg(path2, "").Lookup(name2)
+ assert(sym == sym2)
+ assert(tag2 != pkgbits.ObjStub)
+ }
+
+ w := l.pw.NewEncoderRaw(pkgbits.RelocObj)
+ wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt)
+ wname := l.pw.NewEncoderRaw(pkgbits.RelocName)
+ wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict)
+
+ l.decls[sym] = w.Idx
+ assert(wext.Idx == w.Idx)
+ assert(wname.Idx == w.Idx)
+ assert(wdict.Idx == w.Idx)
+
+ l.relocCommon(pr, &w, pkgbits.RelocObj, idx)
+ l.relocCommon(pr, &wname, pkgbits.RelocName, idx)
+ l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx)
+
+ // Generic types and functions won't have definitions, and imported
+ // objects may not either.
+ obj, _ := sym.Def.(*ir.Name)
+ local := sym.Pkg == types.LocalPkg
+
+ if local && obj != nil {
+ wext.Sync(pkgbits.SyncObject1)
+ switch tag {
+ case pkgbits.ObjFunc:
+ l.relocFuncExt(&wext, obj)
+ case pkgbits.ObjType:
+ l.relocTypeExt(&wext, obj)
+ case pkgbits.ObjVar:
+ l.relocVarExt(&wext, obj)
+ }
+ wext.Flush()
+ } else {
+ l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx)
+ }
+
+ // Check if we need to export the inline bodies for functions and
+ // methods.
+ if obj != nil {
+ if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC {
+ l.exportBody(obj, local)
+ }
+
+ if obj.Op() == ir.OTYPE && !obj.Alias() {
+ if typ := obj.Type(); !typ.IsInterface() {
+ for _, method := range typ.Methods().Slice() {
+ l.exportBody(method.Nname.(*ir.Name), local)
+ }
+ }
+ }
+ }
+
+ return w.Idx
+}
+
+// exportBody exports the given function or method's body, if
+// appropriate. local indicates whether it's a local function or
+// method available on a locally declared type. (Due to cross-package
+// type aliases, a method may be imported, but still available on a
+// locally declared type.)
+func (l *linker) exportBody(obj *ir.Name, local bool) {
+ assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC)
+
+ fn := obj.Func
+ if fn.Inl == nil {
+ return // not inlinable anyway
+ }
+
+ // As a simple heuristic, if the function was declared in this
+ // package or we inlined it somewhere in this package, then we'll
+ // (re)export the function body. This isn't perfect, but seems
+ // reasonable in practice. In particular, it has the nice property
+ // that in the worst case, adding a blank import ensures the
+ // function body is available for inlining.
+ //
+ // TODO(mdempsky): Reimplement the reachable method crawling logic
+ // from typecheck/crawler.go.
+ exportBody := local || fn.Inl.Body != nil
+ if !exportBody {
+ return
+ }
+
+ sym := obj.Sym()
+ if _, ok := l.bodies[sym]; ok {
+ // Due to type aliases, we might visit methods multiple times.
+ base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj)
+ return
+ }
+
+ pri, ok := bodyReaderFor(fn)
+ assert(ok)
+ l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)
+}
+
+// relocCommon copies the specified element from pr into w,
+// recursively relocating any referenced elements as well.
+func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) {
+ r := pr.NewDecoderRaw(k, idx)
+ w.Relocs = l.relocAll(pr, r.Relocs)
+ io.Copy(&w.Data, &r.Data)
+ w.Flush()
+}
+
+func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) {
+ w.Sync(pkgbits.SyncPragma)
+ w.Int(int(pragma))
+}
+
+func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) {
+ w.Sync(pkgbits.SyncFuncExt)
+
+ l.pragmaFlag(w, name.Func.Pragma)
+ l.linkname(w, name)
+
+ // Relocated extension data.
+ w.Bool(true)
+
+ // 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(name.Func.ABI))
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(name.Type()).FieldSlice() {
+ w.String(f.Note)
+ }
+ }
+
+ if inl := name.Func.Inl; w.Bool(inl != nil) {
+ w.Len(int(inl.Cost))
+ w.Bool(inl.CanDelayResults)
+ }
+
+ w.Sync(pkgbits.SyncEOF)
+}
+
+func (l *linker) relocTypeExt(w *pkgbits.Encoder, name *ir.Name) {
+ w.Sync(pkgbits.SyncTypeExt)
+
+ typ := name.Type()
+
+ l.pragmaFlag(w, name.Pragma())
+
+ // For type T, export the index of type descriptor symbols of T and *T.
+ l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
+ l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
+
+ if typ.Kind() != types.TINTER {
+ for _, method := range typ.Methods().Slice() {
+ l.relocFuncExt(w, method.Nname.(*ir.Name))
+ }
+ }
+}
+
+func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) {
+ w.Sync(pkgbits.SyncVarExt)
+ l.linkname(w, name)
+}
+
+func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) {
+ w.Sync(pkgbits.SyncLinkname)
+
+ linkname := name.Sym().Linkname
+ if !l.lsymIdx(w, linkname, name.Linksym()) {
+ w.String(linkname)
+ }
+}
+
+func (l *linker) lsymIdx(w *pkgbits.Encoder, linkname string, lsym *obj.LSym) bool {
+ if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
+ w.Int64(-1)
+ return false
+ }
+
+ // For a defined symbol, export its index.
+ // For re-exporting an imported symbol, pass its index through.
+ w.Int64(int64(lsym.SymIdx))
+ return true
+}
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
new file mode 100644
index 0000000..c99c085
--- /dev/null
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -0,0 +1,496 @@
+// Copyright 2016 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 (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+func LoadPackage(filenames []string) {
+ base.Timer.Start("fe", "parse")
+
+ // Limit the number of simultaneously open files.
+ sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
+
+ noders := make([]*noder, len(filenames))
+ for i := range noders {
+ p := noder{
+ err: make(chan syntax.Error),
+ }
+ noders[i] = &p
+ }
+
+ // Move the entire syntax processing logic into a separate goroutine to avoid blocking on the "sem".
+ go func() {
+ for i, filename := range filenames {
+ filename := filename
+ p := noders[i]
+ sem <- struct{}{}
+ go func() {
+ defer func() { <-sem }()
+ defer close(p.err)
+ fbase := syntax.NewFileBase(filename)
+
+ f, err := os.Open(filename)
+ if err != nil {
+ p.error(syntax.Error{Msg: err.Error()})
+ return
+ }
+ defer f.Close()
+
+ p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error
+ }()
+ }
+ }()
+
+ var lines uint
+ for _, p := range noders {
+ for e := range p.err {
+ p.errorAt(e.Pos, "%s", e.Msg)
+ }
+ if p.file == nil {
+ base.ErrorExit()
+ }
+ lines += p.file.EOF.Line()
+ }
+ base.Timer.AddEvent(int64(lines), "lines")
+
+ if base.Debug.Unified != 0 {
+ unified(noders)
+ return
+ }
+
+ // Use types2 to type-check and generate IR.
+ check2(noders)
+}
+
+func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {
+ base.ErrorfAt(p.makeXPos(pos), format, args...)
+}
+
+// trimFilename returns the "trimmed" filename of b, which is the
+// absolute filename after applying -trimpath processing. This
+// filename form is suitable for use in object files and export data.
+//
+// If b's filename has already been trimmed (i.e., because it was read
+// in from an imported package's export data), then the filename is
+// returned unchanged.
+func trimFilename(b *syntax.PosBase) string {
+ filename := b.Filename()
+ if !b.Trimmed() {
+ dir := ""
+ if b.IsFileBase() {
+ dir = base.Ctxt.Pathname
+ }
+ filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath)
+ }
+ return filename
+}
+
+// noder transforms package syntax's AST into a Node tree.
+type noder struct {
+ posMap
+
+ file *syntax.File
+ linknames []linkname
+ pragcgobuf [][]string
+ err chan syntax.Error
+ importedUnsafe bool
+ importedEmbed bool
+}
+
+// linkname records a //go:linkname directive.
+type linkname struct {
+ pos syntax.Pos
+ local string
+ remote string
+}
+
+func (p *noder) processPragmas() {
+ for _, l := range p.linknames {
+ if !p.importedUnsafe {
+ p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
+ continue
+ }
+ n := ir.AsNode(typecheck.Lookup(l.local).Def)
+ if n == nil || n.Op() != ir.ONAME {
+ if types.AllowsGoVersion(1, 18) {
+ p.errorAt(l.pos, "//go:linkname must refer to declared function or variable")
+ }
+ continue
+ }
+ if n.Sym().Linkname != "" {
+ p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local)
+ continue
+ }
+ n.Sym().Linkname = l.remote
+ }
+ typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...)
+}
+
+var unOps = [...]ir.Op{
+ syntax.Recv: ir.ORECV,
+ syntax.Mul: ir.ODEREF,
+ syntax.And: ir.OADDR,
+
+ syntax.Not: ir.ONOT,
+ syntax.Xor: ir.OBITNOT,
+ syntax.Add: ir.OPLUS,
+ syntax.Sub: ir.ONEG,
+}
+
+var binOps = [...]ir.Op{
+ syntax.OrOr: ir.OOROR,
+ syntax.AndAnd: ir.OANDAND,
+
+ syntax.Eql: ir.OEQ,
+ syntax.Neq: ir.ONE,
+ syntax.Lss: ir.OLT,
+ syntax.Leq: ir.OLE,
+ syntax.Gtr: ir.OGT,
+ syntax.Geq: ir.OGE,
+
+ syntax.Add: ir.OADD,
+ syntax.Sub: ir.OSUB,
+ syntax.Or: ir.OOR,
+ syntax.Xor: ir.OXOR,
+
+ syntax.Mul: ir.OMUL,
+ syntax.Div: ir.ODIV,
+ syntax.Rem: ir.OMOD,
+ syntax.And: ir.OAND,
+ syntax.AndNot: ir.OANDNOT,
+ syntax.Shl: ir.OLSH,
+ syntax.Shr: ir.ORSH,
+}
+
+func wrapname(pos src.XPos, x ir.Node) ir.Node {
+ // These nodes do not carry line numbers.
+ // Introduce a wrapper node to give them the correct line.
+ switch x.Op() {
+ case ir.OTYPE, ir.OLITERAL:
+ if x.Sym() == nil {
+ break
+ }
+ fallthrough
+ case ir.ONAME, ir.ONONAME:
+ p := ir.NewParenExpr(pos, x)
+ p.SetImplicit(true)
+ return p
+ }
+ return x
+}
+
+// error is called concurrently if files are parsed concurrently.
+func (p *noder) error(err error) {
+ p.err <- err.(syntax.Error)
+}
+
+// pragmas that are allowed in the std lib, but don't have
+// a syntax.Pragma value (see lex.go) associated with them.
+var allowedStdPragmas = map[string]bool{
+ "go:cgo_export_static": true,
+ "go:cgo_export_dynamic": true,
+ "go:cgo_import_static": true,
+ "go:cgo_import_dynamic": true,
+ "go:cgo_ldflag": true,
+ "go:cgo_dynamic_linker": true,
+ "go:embed": true,
+ "go:generate": true,
+}
+
+// *pragmas is the value stored in a syntax.pragmas during parsing.
+type pragmas struct {
+ Flag ir.PragmaFlag // collected bits
+ Pos []pragmaPos // position of each individual flag
+ Embeds []pragmaEmbed
+}
+
+type pragmaPos struct {
+ Flag ir.PragmaFlag
+ Pos syntax.Pos
+}
+
+type pragmaEmbed struct {
+ Pos syntax.Pos
+ Patterns []string
+}
+
+func (p *noder) checkUnusedDuringParse(pragma *pragmas) {
+ for _, pos := range pragma.Pos {
+ if pos.Flag&pragma.Flag != 0 {
+ p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
+ }
+ }
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"})
+ }
+ }
+}
+
+// pragma is called concurrently if files are parsed concurrently.
+func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma {
+ pragma, _ := old.(*pragmas)
+ if pragma == nil {
+ pragma = new(pragmas)
+ }
+
+ if text == "" {
+ // unused pragma; only called with old != nil.
+ p.checkUnusedDuringParse(pragma)
+ return nil
+ }
+
+ if strings.HasPrefix(text, "line ") {
+ // line directives are handled by syntax package
+ panic("unreachable")
+ }
+
+ if !blankLine {
+ // directive must be on line by itself
+ p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"})
+ return pragma
+ }
+
+ switch {
+ case strings.HasPrefix(text, "go:linkname "):
+ f := strings.Fields(text)
+ if !(2 <= len(f) && len(f) <= 3) {
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"})
+ break
+ }
+ // The second argument is optional. If omitted, we use
+ // the default object symbol name for this and
+ // linkname only serves to mark this symbol as
+ // something that may be referenced via the object
+ // symbol name from another package.
+ var target string
+ if len(f) == 3 {
+ target = f[2]
+ } else if base.Ctxt.Pkgpath != "" {
+ // Use the default object symbol name if the
+ // user didn't provide one.
+ target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1]
+ } else {
+ p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"})
+ break
+ }
+ p.linknames = append(p.linknames, linkname{pos, f[1], target})
+
+ case text == "go:embed", strings.HasPrefix(text, "go:embed "):
+ args, err := parseGoEmbed(text[len("go:embed"):])
+ if err != nil {
+ p.error(syntax.Error{Pos: pos, Msg: err.Error()})
+ }
+ if len(args) == 0 {
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."})
+ break
+ }
+ pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args})
+
+ case strings.HasPrefix(text, "go:cgo_import_dynamic "):
+ // This is permitted for general use because Solaris
+ // code relies on it in golang.org/x/sys/unix and others.
+ fields := pragmaFields(text)
+ if len(fields) >= 4 {
+ lib := strings.Trim(fields[3], `"`)
+ if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
+ }
+ p.pragcgo(pos, text)
+ pragma.Flag |= pragmaFlag("go:cgo_import_dynamic")
+ break
+ }
+ fallthrough
+ case strings.HasPrefix(text, "go:cgo_"):
+ // For security, we disallow //go:cgo_* directives other
+ // than cgo_import_dynamic outside cgo-generated files.
+ // Exception: they are allowed in the standard library, for runtime and syscall.
+ if !isCgoGeneratedFile(pos) && !base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
+ }
+ p.pragcgo(pos, text)
+ fallthrough // because of //go:cgo_unsafe_args
+ default:
+ verb := text
+ if i := strings.Index(text, " "); i >= 0 {
+ verb = verb[:i]
+ }
+ flag := pragmaFlag(verb)
+ const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec
+ if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
+ }
+ if flag == ir.UintptrKeepAlive && !base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)})
+ }
+ if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
+ }
+ pragma.Flag |= flag
+ pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos})
+ }
+
+ return pragma
+}
+
+// isCgoGeneratedFile reports whether pos is in a file
+// generated by cgo, which is to say a file with name
+// beginning with "_cgo_". Such files are allowed to
+// contain cgo directives, and for security reasons
+// (primarily misuse of linker flags), other files are not.
+// See golang.org/issue/23672.
+// Note that cmd/go ignores files whose names start with underscore,
+// so the only _cgo_ files we will see from cmd/go are generated by cgo.
+// It's easy to bypass this check by calling the compiler directly;
+// we only protect against uses by cmd/go.
+func isCgoGeneratedFile(pos syntax.Pos) bool {
+ // We need the absolute file, independent of //line directives,
+ // so we call pos.Base().Pos().
+ return strings.HasPrefix(filepath.Base(trimFilename(pos.Base().Pos().Base())), "_cgo_")
+}
+
+// safeArg reports whether arg is a "safe" command-line argument,
+// meaning that when it appears in a command-line, it probably
+// doesn't have some special meaning other than its own name.
+// This is copied from SafeArg in cmd/go/internal/load/pkg.go.
+func safeArg(name string) bool {
+ if name == "" {
+ return false
+ }
+ c := name[0]
+ return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
+}
+
+// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
+// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
+// go/build/read.go also processes these strings and contains similar logic.
+func parseGoEmbed(args string) ([]string, error) {
+ var list []string
+ for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
+ var path string
+ Switch:
+ switch args[0] {
+ default:
+ i := len(args)
+ for j, c := range args {
+ if unicode.IsSpace(c) {
+ i = j
+ break
+ }
+ }
+ path = args[:i]
+ args = args[i:]
+
+ case '`':
+ i := strings.Index(args[1:], "`")
+ if i < 0 {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ path = args[1 : 1+i]
+ args = args[1+i+1:]
+
+ case '"':
+ i := 1
+ for ; i < len(args); i++ {
+ if args[i] == '\\' {
+ i++
+ continue
+ }
+ if args[i] == '"' {
+ q, err := strconv.Unquote(args[:i+1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
+ }
+ path = q
+ args = args[i+1:]
+ break Switch
+ }
+ }
+ if i >= len(args) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+
+ if args != "" {
+ r, _ := utf8.DecodeRuneInString(args)
+ if !unicode.IsSpace(r) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+ list = append(list, path)
+ }
+ return list, nil
+}
+
+// A function named init is a special case.
+// It is called by the initialization before main is run.
+// To make it unique within a package and also uncallable,
+// the name, normally "pkg.init", is altered to "pkg.init.0".
+var renameinitgen int
+
+func Renameinit() *types.Sym {
+ s := typecheck.LookupNum("init.", renameinitgen)
+ renameinitgen++
+ return s
+}
+
+func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) {
+ pragmaEmbeds := pragma.Embeds
+ pragma.Embeds = nil
+ if len(pragmaEmbeds) == 0 {
+ return
+ }
+
+ if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil {
+ base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err)
+ return
+ }
+
+ var embeds []ir.Embed
+ for _, e := range pragmaEmbeds {
+ embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns})
+ }
+ typecheck.Target.Embeds = append(typecheck.Target.Embeds, name)
+ name.Embed = &embeds
+}
+
+func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error {
+ switch {
+ case !haveEmbed:
+ return errors.New("go:embed only allowed in Go files that import \"embed\"")
+ case len(decl.NameList) > 1:
+ return errors.New("go:embed cannot apply to multiple vars")
+ case decl.Values != nil:
+ return errors.New("go:embed cannot apply to var with initializer")
+ case decl.Type == nil:
+ // Should not happen, since Values == nil now.
+ return errors.New("go:embed cannot apply to var without type")
+ case withinFunc:
+ return errors.New("go:embed cannot apply to var inside func")
+ case !types.AllowsGoVersion(1, 16):
+ return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang)
+
+ default:
+ return nil
+ }
+}
diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go
new file mode 100644
index 0000000..3b60760
--- /dev/null
+++ b/src/cmd/compile/internal/noder/object.go
@@ -0,0 +1,205 @@
+// 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/posmap.go b/src/cmd/compile/internal/noder/posmap.go
new file mode 100644
index 0000000..6c7e57c
--- /dev/null
+++ b/src/cmd/compile/internal/noder/posmap.go
@@ -0,0 +1,86 @@
+// 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/syntax"
+ "cmd/internal/src"
+)
+
+// A posMap handles mapping from syntax.Pos to src.XPos.
+type posMap struct {
+ bases map[*syntax.PosBase]*src.PosBase
+ cache struct {
+ last *syntax.PosBase
+ base *src.PosBase
+ }
+}
+
+type poser interface{ Pos() syntax.Pos }
+type ender interface{ End() syntax.Pos }
+
+func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) }
+func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) }
+
+func (m *posMap) makeXPos(pos syntax.Pos) src.XPos {
+ // Predeclared objects (e.g., the result parameter for error.Error)
+ // do not have a position.
+ if !pos.IsKnown() {
+ return src.NoXPos
+ }
+
+ posBase := m.makeSrcPosBase(pos.Base())
+ return base.Ctxt.PosTable.XPos(src.MakePos(posBase, pos.Line(), pos.Col()))
+}
+
+// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
+func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
+ // fast path: most likely PosBase hasn't changed
+ if m.cache.last == b0 {
+ return m.cache.base
+ }
+
+ b1, ok := m.bases[b0]
+ if !ok {
+ fn := b0.Filename()
+ absfn := trimFilename(b0)
+
+ if b0.IsFileBase() {
+ b1 = src.NewFileBase(fn, absfn)
+ } else {
+ // line directive base
+ p0 := b0.Pos()
+ p0b := p0.Base()
+ if p0b == b0 {
+ panic("infinite recursion in makeSrcPosBase")
+ }
+ p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col())
+ b1 = src.NewLinePragmaBase(p1, fn, absfn, b0.Line(), b0.Col())
+ }
+ if m.bases == nil {
+ m.bases = make(map[*syntax.PosBase]*src.PosBase)
+ }
+ m.bases[b0] = b1
+ }
+
+ // update cache
+ m.cache.last = b0
+ m.cache.base = b1
+
+ return b1
+}
+
+func (m *posMap) join(other *posMap) {
+ if m.bases == nil {
+ m.bases = make(map[*syntax.PosBase]*src.PosBase)
+ }
+ for k, v := range other.bases {
+ if m.bases[k] != nil {
+ base.Fatalf("duplicate posmap bases")
+ }
+ m.bases[k] = v
+ }
+}
diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go
new file mode 100644
index 0000000..a22577f
--- /dev/null
+++ b/src/cmd/compile/internal/noder/quirks.go
@@ -0,0 +1,79 @@
+// 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 (
+ "fmt"
+
+ "cmd/compile/internal/syntax"
+)
+
+// typeExprEndPos returns the position that noder would leave base.Pos
+// after parsing the given type expression.
+//
+// Deprecated: This function exists to emulate position semantics from
+// Go 1.17, necessary for compatibility with the backend DWARF
+// generation logic that assigns variables to their appropriate scope.
+func typeExprEndPos(expr0 syntax.Expr) syntax.Pos {
+ for {
+ switch expr := expr0.(type) {
+ case *syntax.Name:
+ return expr.Pos()
+ case *syntax.SelectorExpr:
+ return expr.X.Pos()
+
+ case *syntax.ParenExpr:
+ expr0 = expr.X
+
+ case *syntax.Operation:
+ assert(expr.Op == syntax.Mul)
+ assert(expr.Y == nil)
+ expr0 = expr.X
+
+ case *syntax.ArrayType:
+ expr0 = expr.Elem
+ case *syntax.ChanType:
+ expr0 = expr.Elem
+ case *syntax.DotsType:
+ expr0 = expr.Elem
+ case *syntax.MapType:
+ expr0 = expr.Value
+ case *syntax.SliceType:
+ expr0 = expr.Elem
+
+ case *syntax.StructType:
+ return expr.Pos()
+
+ case *syntax.InterfaceType:
+ expr0 = lastFieldType(expr.MethodList)
+ if expr0 == nil {
+ return expr.Pos()
+ }
+
+ case *syntax.FuncType:
+ expr0 = lastFieldType(expr.ResultList)
+ if expr0 == nil {
+ expr0 = lastFieldType(expr.ParamList)
+ if expr0 == nil {
+ return expr.Pos()
+ }
+ }
+
+ case *syntax.IndexExpr: // explicit type instantiation
+ targs := unpackListExpr(expr.Index)
+ expr0 = targs[len(targs)-1]
+
+ default:
+ panic(fmt.Sprintf("%s: unexpected type expression %v", expr.Pos(), syntax.String(expr)))
+ }
+ }
+}
+
+func lastFieldType(fields []*syntax.Field) syntax.Expr {
+ if len(fields) == 0 {
+ return nil
+ }
+ return fields[len(fields)-1].Type
+}
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
new file mode 100644
index 0000000..bc2151e
--- /dev/null
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -0,0 +1,4029 @@
+// 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 (
+ "fmt"
+ "go/constant"
+ "internal/buildcfg"
+ "internal/pkgbits"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/deadcode"
+ "cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticinit"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+// This file implements cmd/compile backend's reader for the Unified
+// IR export data.
+
+// A pkgReader reads Unified IR export data.
+type pkgReader struct {
+ pkgbits.PkgDecoder
+
+ // Indices for encoded things; lazily populated as needed.
+ //
+ // Note: Objects (i.e., ir.Names) are lazily instantiated by
+ // populating their types.Sym.Def; see objReader below.
+
+ posBases []*src.PosBase
+ pkgs []*types.Pkg
+ typs []*types.Type
+
+ // offset for rewriting the given (absolute!) index into the output,
+ // but bitwise inverted so we can detect if we're missing the entry
+ // or not.
+ newindex []pkgbits.Index
+}
+
+func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
+ return &pkgReader{
+ PkgDecoder: pr,
+
+ posBases: make([]*src.PosBase, pr.NumElems(pkgbits.RelocPosBase)),
+ pkgs: make([]*types.Pkg, pr.NumElems(pkgbits.RelocPkg)),
+ typs: make([]*types.Type, pr.NumElems(pkgbits.RelocType)),
+
+ newindex: make([]pkgbits.Index, pr.TotalElems()),
+ }
+}
+
+// A pkgReaderIndex compactly identifies an index (and its
+// corresponding dictionary) within a package's export data.
+type pkgReaderIndex struct {
+ pr *pkgReader
+ idx pkgbits.Index
+ dict *readerDict
+ methodSym *types.Sym
+
+ synthetic func(pos src.XPos, r *reader)
+}
+
+func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader {
+ if pri.synthetic != nil {
+ return &reader{synthetic: pri.synthetic}
+ }
+
+ r := pri.pr.newReader(k, pri.idx, marker)
+ r.dict = pri.dict
+ r.methodSym = pri.methodSym
+ return r
+}
+
+func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
+ return &reader{
+ Decoder: pr.NewDecoder(k, idx, marker),
+ p: pr,
+ }
+}
+
+// A reader provides APIs for reading an individual element.
+type reader struct {
+ pkgbits.Decoder
+
+ p *pkgReader
+
+ dict *readerDict
+
+ // TODO(mdempsky): The state below is all specific to reading
+ // function bodies. It probably makes sense to split it out
+ // separately so that it doesn't take up space in every reader
+ // instance.
+
+ curfn *ir.Func
+ locals []*ir.Name
+ closureVars []*ir.Name
+
+ funarghack bool
+
+ // methodSym is the name of method's name, if reading a method.
+ // It's nil if reading a normal function or closure body.
+ methodSym *types.Sym
+
+ // dictParam is the .dict param, if any.
+ dictParam *ir.Name
+
+ // synthetic is a callback function to construct a synthetic
+ // function body. It's used for creating the bodies of function
+ // literals used to curry arguments to shaped functions.
+ synthetic func(pos src.XPos, r *reader)
+
+ // scopeVars is a stack tracking the number of variables declared in
+ // the current function at the moment each open scope was opened.
+ scopeVars []int
+ marker dwarfgen.ScopeMarker
+ lastCloseScopePos src.XPos
+
+ // === details for handling inline body expansion ===
+
+ // If we're reading in a function body because of inlining, this is
+ // the call that we're inlining for.
+ inlCaller *ir.Func
+ inlCall *ir.CallExpr
+ inlFunc *ir.Func
+ inlTreeIndex int
+ inlPosBases map[*src.PosBase]*src.PosBase
+
+ // suppressInlPos tracks whether position base rewriting for
+ // inlining should be suppressed. See funcLit.
+ suppressInlPos int
+
+ delayResults bool
+
+ // Label to return to.
+ retlabel *types.Sym
+
+ // inlvars is the list of variables that the inlinee's arguments are
+ // assigned to, one for each receiver and normal parameter, in order.
+ inlvars ir.Nodes
+
+ // retvars is the list of variables that the inlinee's results are
+ // assigned to, one for each result parameter, in order.
+ retvars ir.Nodes
+}
+
+// A readerDict represents an instantiated "compile-time dictionary,"
+// used for resolving any derived types needed for instantiating a
+// generic object.
+//
+// A compile-time dictionary can either be "shaped" or "non-shaped."
+// Shaped compile-time dictionaries are only used for instantiating
+// shaped type definitions and function bodies, while non-shaped
+// compile-time dictionaries are used for instantiating runtime
+// dictionaries.
+type readerDict struct {
+ shaped bool // whether this is a shaped dictionary
+
+ // baseSym is the symbol for the object this dictionary belongs to.
+ // If the object is an instantiated function or defined type, then
+ // baseSym is the mangled symbol, including any type arguments.
+ baseSym *types.Sym
+
+ // For non-shaped dictionaries, shapedObj is a reference to the
+ // corresponding shaped object (always a function or defined type).
+ shapedObj *ir.Name
+
+ // targs holds the implicit and explicit type arguments in use for
+ // reading the current object. For example:
+ //
+ // func F[T any]() {
+ // type X[U any] struct { t T; u U }
+ // var _ X[string]
+ // }
+ //
+ // var _ = F[int]
+ //
+ // While instantiating F[int], we need to in turn instantiate
+ // X[string]. [int] and [string] are explicit type arguments for F
+ // and X, respectively; but [int] is also the implicit type
+ // arguments for X.
+ //
+ // (As an analogy to function literals, explicits are the function
+ // literal's formal parameters, while implicits are variables
+ // captured by the function literal.)
+ targs []*types.Type
+
+ // implicits counts how many of types within targs are implicit type
+ // arguments; the rest are explicit.
+ implicits int
+
+ derived []derivedInfo // reloc index of the derived type's descriptor
+ derivedTypes []*types.Type // slice of previously computed derived types
+
+ // These slices correspond to entries in the runtime dictionary.
+ typeParamMethodExprs []readerMethodExprInfo
+ subdicts []objInfo
+ rtypes []typeInfo
+ itabs []itabInfo
+}
+
+type readerMethodExprInfo struct {
+ typeParamIdx int
+ method *types.Sym
+}
+
+func setType(n ir.Node, typ *types.Type) {
+ n.SetType(typ)
+ n.SetTypecheck(1)
+}
+
+func setValue(name *ir.Name, val constant.Value) {
+ name.SetVal(val)
+ name.Defn = nil
+}
+
+// @@@ Positions
+
+// pos reads a position from the bitstream.
+func (r *reader) pos() src.XPos {
+ return base.Ctxt.PosTable.XPos(r.pos0())
+}
+
+// origPos reads a position from the bitstream, and returns both the
+// original raw position and an inlining-adjusted position.
+func (r *reader) origPos() (origPos, inlPos src.XPos) {
+ r.suppressInlPos++
+ origPos = r.pos()
+ r.suppressInlPos--
+ inlPos = r.inlPos(origPos)
+ return
+}
+
+func (r *reader) pos0() src.Pos {
+ r.Sync(pkgbits.SyncPos)
+ if !r.Bool() {
+ return src.NoPos
+ }
+
+ posBase := r.posBase()
+ line := r.Uint()
+ col := r.Uint()
+ return src.MakePos(posBase, line, col)
+}
+
+// posBase reads a position base from the bitstream.
+func (r *reader) posBase() *src.PosBase {
+ return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
+}
+
+// posBaseIdx returns the specified position base, reading it first if
+// needed.
+func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
+ if b := pr.posBases[idx]; b != nil {
+ return b
+ }
+
+ r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
+ var b *src.PosBase
+
+ absFilename := r.String()
+ filename := absFilename
+
+ // For build artifact stability, the export data format only
+ // contains the "absolute" filename as returned by objabi.AbsFile.
+ // However, some tests (e.g., test/run.go's asmcheck tests) expect
+ // to see the full, original filename printed out. Re-expanding
+ // "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to
+ // satisfy this.
+ //
+ // TODO(mdempsky): De-duplicate this logic with similar logic in
+ // cmd/link/internal/ld's expandGoroot. However, this will probably
+ // require being more consistent about when we use native vs UNIX
+ // file paths.
+ const dollarGOROOT = "$GOROOT"
+ if buildcfg.GOROOT != "" && strings.HasPrefix(filename, dollarGOROOT) {
+ filename = buildcfg.GOROOT + filename[len(dollarGOROOT):]
+ }
+
+ if r.Bool() {
+ b = src.NewFileBase(filename, absFilename)
+ } else {
+ pos := r.pos0()
+ line := r.Uint()
+ col := r.Uint()
+ b = src.NewLinePragmaBase(pos, filename, absFilename, line, col)
+ }
+
+ pr.posBases[idx] = b
+ return b
+}
+
+// inlPosBase returns the inlining-adjusted src.PosBase corresponding
+// to oldBase, which must be a non-inlined position. When not
+// inlining, this is just oldBase.
+func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
+ if index := oldBase.InliningIndex(); index >= 0 {
+ base.Fatalf("oldBase %v already has inlining index %v", oldBase, index)
+ }
+
+ if r.inlCall == nil || r.suppressInlPos != 0 {
+ return oldBase
+ }
+
+ if newBase, ok := r.inlPosBases[oldBase]; ok {
+ return newBase
+ }
+
+ newBase := src.NewInliningBase(oldBase, r.inlTreeIndex)
+ r.inlPosBases[oldBase] = newBase
+ return newBase
+}
+
+// inlPos returns the inlining-adjusted src.XPos corresponding to
+// xpos, which must be a non-inlined position. When not inlining, this
+// is just xpos.
+func (r *reader) inlPos(xpos src.XPos) src.XPos {
+ pos := base.Ctxt.PosTable.Pos(xpos)
+ pos.SetBase(r.inlPosBase(pos.Base()))
+ return base.Ctxt.PosTable.XPos(pos)
+}
+
+// @@@ Packages
+
+// pkg reads a package reference from the bitstream.
+func (r *reader) pkg() *types.Pkg {
+ r.Sync(pkgbits.SyncPkg)
+ return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
+}
+
+// pkgIdx returns the specified package from the export data, reading
+// it first if needed.
+func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
+ if pkg := pr.pkgs[idx]; pkg != nil {
+ return pkg
+ }
+
+ pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
+ pr.pkgs[idx] = pkg
+ return pkg
+}
+
+// doPkg reads a package definition from the bitstream.
+func (r *reader) doPkg() *types.Pkg {
+ path := r.String()
+ switch path {
+ case "":
+ path = r.p.PkgPath()
+ case "builtin":
+ return types.BuiltinPkg
+ case "unsafe":
+ return types.UnsafePkg
+ }
+
+ name := r.String()
+
+ pkg := types.NewPkg(path, "")
+
+ if pkg.Name == "" {
+ pkg.Name = name
+ } else {
+ base.Assertf(pkg.Name == name, "package %q has name %q, but want %q", pkg.Path, pkg.Name, name)
+ }
+
+ return pkg
+}
+
+// @@@ Types
+
+func (r *reader) typ() *types.Type {
+ return r.typWrapped(true)
+}
+
+// typWrapped is like typ, but allows suppressing generation of
+// unnecessary wrappers as a compile-time optimization.
+func (r *reader) typWrapped(wrapped bool) *types.Type {
+ return r.p.typIdx(r.typInfo(), r.dict, wrapped)
+}
+
+func (r *reader) typInfo() typeInfo {
+ r.Sync(pkgbits.SyncType)
+ if r.Bool() {
+ return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
+ }
+ return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
+}
+
+// typListIdx returns a list of the specified types, resolving derived
+// types within the given dictionary.
+func (pr *pkgReader) typListIdx(infos []typeInfo, dict *readerDict) []*types.Type {
+ typs := make([]*types.Type, len(infos))
+ for i, info := range infos {
+ typs[i] = pr.typIdx(info, dict, true)
+ }
+ return typs
+}
+
+// typIdx returns the specified type. If info specifies a derived
+// type, it's resolved within the given dictionary. If wrapped is
+// true, then method wrappers will be generated, if appropriate.
+func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *types.Type {
+ idx := info.idx
+ var where **types.Type
+ if info.derived {
+ where = &dict.derivedTypes[idx]
+ idx = dict.derived[idx].idx
+ } else {
+ where = &pr.typs[idx]
+ }
+
+ if typ := *where; typ != nil {
+ return typ
+ }
+
+ r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
+ r.dict = dict
+
+ typ := r.doTyp()
+ assert(typ != nil)
+
+ // For recursive type declarations involving interfaces and aliases,
+ // above r.doTyp() call may have already set pr.typs[idx], so just
+ // double check and return the type.
+ //
+ // Example:
+ //
+ // type F = func(I)
+ //
+ // type I interface {
+ // m(F)
+ // }
+ //
+ // The writer writes data types in following index order:
+ //
+ // 0: func(I)
+ // 1: I
+ // 2: interface{m(func(I))}
+ //
+ // The reader resolves it in following index order:
+ //
+ // 0 -> 1 -> 2 -> 0 -> 1
+ //
+ // and can divide in logically 2 steps:
+ //
+ // - 0 -> 1 : first time the reader reach type I,
+ // it creates new named type with symbol I.
+ //
+ // - 2 -> 0 -> 1: the reader ends up reaching symbol I again,
+ // now the symbol I was setup in above step, so
+ // the reader just return the named type.
+ //
+ // Now, the functions called return, the pr.typs looks like below:
+ //
+ // - 0 -> 1 -> 2 -> 0 : [<T> I <T>]
+ // - 0 -> 1 -> 2 : [func(I) I <T>]
+ // - 0 -> 1 : [func(I) I interface { "".m(func("".I)) }]
+ //
+ // The idx 1, corresponding with type I was resolved successfully
+ // after r.doTyp() call.
+
+ if prev := *where; prev != nil {
+ return prev
+ }
+
+ if wrapped {
+ // Only cache if we're adding wrappers, so that other callers that
+ // find a cached type know it was wrapped.
+ *where = typ
+
+ r.needWrapper(typ)
+ }
+
+ if !typ.IsUntyped() {
+ types.CheckSize(typ)
+ }
+
+ return typ
+}
+
+func (r *reader) doTyp() *types.Type {
+ switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag {
+ default:
+ panic(fmt.Sprintf("unexpected type: %v", tag))
+
+ case pkgbits.TypeBasic:
+ return *basics[r.Len()]
+
+ case pkgbits.TypeNamed:
+ obj := r.obj()
+ assert(obj.Op() == ir.OTYPE)
+ return obj.Type()
+
+ case pkgbits.TypeTypeParam:
+ return r.dict.targs[r.Len()]
+
+ case pkgbits.TypeArray:
+ len := int64(r.Uint64())
+ return types.NewArray(r.typ(), len)
+ case pkgbits.TypeChan:
+ dir := dirs[r.Len()]
+ return types.NewChan(r.typ(), dir)
+ case pkgbits.TypeMap:
+ return types.NewMap(r.typ(), r.typ())
+ case pkgbits.TypePointer:
+ return types.NewPtr(r.typ())
+ case pkgbits.TypeSignature:
+ return r.signature(types.LocalPkg, nil)
+ case pkgbits.TypeSlice:
+ return types.NewSlice(r.typ())
+ case pkgbits.TypeStruct:
+ return r.structType()
+ case pkgbits.TypeInterface:
+ return r.interfaceType()
+ case pkgbits.TypeUnion:
+ return r.unionType()
+ }
+}
+
+func (r *reader) unionType() *types.Type {
+ // In the types1 universe, we only need to handle value types.
+ // Impure interfaces (i.e., interfaces with non-trivial type sets
+ // like "int | string") can only appear as type parameter bounds,
+ // and this is enforced by the types2 type checker.
+ //
+ // However, type unions can still appear in pure interfaces if the
+ // type union is equivalent to "any". E.g., typeparam/issue52124.go
+ // declares variables with the type "interface { any | int }".
+ //
+ // To avoid needing to represent type unions in types1 (since we
+ // don't have any uses for that today anyway), we simply fold them
+ // to "any". As a consistency check, we still read the union terms
+ // to make sure this substitution is safe.
+
+ pure := false
+ for i, n := 0, r.Len(); i < n; i++ {
+ _ = r.Bool() // tilde
+ term := r.typ()
+ if term.IsEmptyInterface() {
+ pure = true
+ }
+ }
+ if !pure {
+ base.Fatalf("impure type set used in value type")
+ }
+
+ return types.Types[types.TINTER]
+}
+
+func (r *reader) interfaceType() *types.Type {
+ tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
+
+ nmethods, nembeddeds := r.Len(), r.Len()
+ implicit := nmethods == 0 && nembeddeds == 1 && r.Bool()
+ assert(!implicit) // implicit interfaces only appear in constraints
+
+ fields := make([]*types.Field, nmethods+nembeddeds)
+ methods, embeddeds := fields[:nmethods], fields[nmethods:]
+
+ for i := range methods {
+ pos := r.pos()
+ pkg, sym := r.selector()
+ tpkg = pkg
+ mtyp := r.signature(pkg, types.FakeRecv())
+ methods[i] = types.NewField(pos, sym, mtyp)
+ }
+ for i := range embeddeds {
+ embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ())
+ }
+
+ if len(fields) == 0 {
+ return types.Types[types.TINTER] // empty interface
+ }
+ return types.NewInterface(tpkg, fields, false)
+}
+
+func (r *reader) structType() *types.Type {
+ tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
+ fields := make([]*types.Field, r.Len())
+ for i := range fields {
+ pos := r.pos()
+ pkg, sym := r.selector()
+ tpkg = pkg
+ ftyp := r.typ()
+ tag := r.String()
+ embedded := r.Bool()
+
+ f := types.NewField(pos, sym, ftyp)
+ f.Note = tag
+ if embedded {
+ f.Embedded = 1
+ }
+ fields[i] = f
+ }
+ return types.NewStruct(tpkg, fields)
+}
+
+func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
+ r.Sync(pkgbits.SyncSignature)
+
+ params := r.params(&tpkg)
+ results := r.params(&tpkg)
+ if r.Bool() { // variadic
+ params[len(params)-1].SetIsDDD(true)
+ }
+
+ return types.NewSignature(tpkg, recv, nil, params, results)
+}
+
+func (r *reader) params(tpkg **types.Pkg) []*types.Field {
+ r.Sync(pkgbits.SyncParams)
+ fields := make([]*types.Field, r.Len())
+ for i := range fields {
+ *tpkg, fields[i] = r.param()
+ }
+ return fields
+}
+
+func (r *reader) param() (*types.Pkg, *types.Field) {
+ r.Sync(pkgbits.SyncParam)
+
+ pos := r.pos()
+ pkg, sym := r.localIdent()
+ typ := r.typ()
+
+ return pkg, types.NewField(pos, sym, typ)
+}
+
+// @@@ Objects
+
+// objReader maps qualified identifiers (represented as *types.Sym) to
+// a pkgReader and corresponding index that can be used for reading
+// that object's definition.
+var objReader = map[*types.Sym]pkgReaderIndex{}
+
+// obj reads an instantiated object reference from the bitstream.
+func (r *reader) obj() ir.Node {
+ return r.p.objInstIdx(r.objInfo(), r.dict, false)
+}
+
+// objInfo reads an instantiated object reference from the bitstream
+// and returns the encoded reference to it, without instantiating it.
+func (r *reader) objInfo() objInfo {
+ r.Sync(pkgbits.SyncObject)
+ assert(!r.Bool()) // TODO(mdempsky): Remove; was derived func inst.
+ idx := r.Reloc(pkgbits.RelocObj)
+
+ explicits := make([]typeInfo, r.Len())
+ for i := range explicits {
+ explicits[i] = r.typInfo()
+ }
+
+ return objInfo{idx, explicits}
+}
+
+// objInstIdx returns the encoded, instantiated object. If shaped is
+// true, then the shaped variant of the object is returned instead.
+func (pr *pkgReader) objInstIdx(info objInfo, dict *readerDict, shaped bool) ir.Node {
+ explicits := pr.typListIdx(info.explicits, dict)
+
+ var implicits []*types.Type
+ if dict != nil {
+ implicits = dict.targs
+ }
+
+ return pr.objIdx(info.idx, implicits, explicits, shaped)
+}
+
+// objIdx returns the specified object, instantiated with the given
+// type arguments, if any. If shaped is true, then the shaped variant
+// of the object is returned instead.
+func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) ir.Node {
+ rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+ _, sym := rname.qualifiedIdent()
+ tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
+
+ if tag == pkgbits.ObjStub {
+ assert(!sym.IsBlank())
+ switch sym.Pkg {
+ case types.BuiltinPkg, types.UnsafePkg:
+ return sym.Def.(ir.Node)
+ }
+ if pri, ok := objReader[sym]; ok {
+ return pri.pr.objIdx(pri.idx, nil, explicits, shaped)
+ }
+ base.Fatalf("unresolved stub: %v", sym)
+ }
+
+ dict := pr.objDictIdx(sym, idx, implicits, explicits, shaped)
+
+ sym = dict.baseSym
+ if !sym.IsBlank() && sym.Def != nil {
+ return sym.Def.(*ir.Name)
+ }
+
+ r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
+ rext := pr.newReader(pkgbits.RelocObjExt, idx, pkgbits.SyncObject1)
+
+ r.dict = dict
+ rext.dict = dict
+
+ do := func(op ir.Op, hasTParams bool) *ir.Name {
+ pos := r.pos()
+ setBasePos(pos)
+ if hasTParams {
+ r.typeParamNames()
+ }
+
+ name := ir.NewDeclNameAt(pos, op, sym)
+ name.Class = ir.PEXTERN // may be overridden later
+ if !sym.IsBlank() {
+ if sym.Def != nil {
+ base.FatalfAt(name.Pos(), "already have a definition for %v", name)
+ }
+ assert(sym.Def == nil)
+ sym.Def = name
+ }
+ return name
+ }
+
+ switch tag {
+ default:
+ panic("unexpected object")
+
+ case pkgbits.ObjAlias:
+ name := do(ir.OTYPE, false)
+ setType(name, r.typ())
+ name.SetAlias(true)
+ return name
+
+ case pkgbits.ObjConst:
+ name := do(ir.OLITERAL, false)
+ typ := r.typ()
+ val := FixValue(typ, r.Value())
+ setType(name, typ)
+ setValue(name, val)
+ return name
+
+ case pkgbits.ObjFunc:
+ if sym.Name == "init" {
+ sym = Renameinit()
+ }
+ name := do(ir.ONAME, true)
+ setType(name, r.signature(sym.Pkg, nil))
+
+ name.Func = ir.NewFunc(r.pos())
+ name.Func.Nname = name
+
+ if r.hasTypeParams() {
+ name.Func.SetDupok(true)
+ if r.dict.shaped {
+ setType(name, shapeSig(name.Func, r.dict))
+ } else {
+ todoDicts = append(todoDicts, func() {
+ r.dict.shapedObj = pr.objIdx(idx, implicits, explicits, true).(*ir.Name)
+ })
+ }
+ }
+
+ rext.funcExt(name, nil)
+ return name
+
+ case pkgbits.ObjType:
+ name := do(ir.OTYPE, true)
+ typ := types.NewNamed(name)
+ setType(name, typ)
+ if r.hasTypeParams() && r.dict.shaped {
+ typ.SetHasShape(true)
+ }
+
+ // Important: We need to do this before SetUnderlying.
+ rext.typeExt(name)
+
+ // We need to defer CheckSize until we've called SetUnderlying to
+ // handle recursive types.
+ types.DeferCheckSize()
+ typ.SetUnderlying(r.typWrapped(false))
+ types.ResumeCheckSize()
+
+ if r.hasTypeParams() && !r.dict.shaped {
+ todoDicts = append(todoDicts, func() {
+ r.dict.shapedObj = pr.objIdx(idx, implicits, explicits, true).(*ir.Name)
+ })
+ }
+
+ methods := make([]*types.Field, r.Len())
+ for i := range methods {
+ methods[i] = r.method(rext)
+ }
+ if len(methods) != 0 {
+ typ.Methods().Set(methods)
+ }
+
+ if !r.dict.shaped {
+ r.needWrapper(typ)
+ }
+
+ return name
+
+ case pkgbits.ObjVar:
+ name := do(ir.ONAME, false)
+ setType(name, r.typ())
+ rext.varExt(name)
+ return name
+ }
+}
+
+func (dict *readerDict) mangle(sym *types.Sym) *types.Sym {
+ if !dict.hasTypeParams() {
+ return sym
+ }
+
+ // If sym is a locally defined generic type, we need the suffix to
+ // stay at the end after mangling so that types/fmt.go can strip it
+ // out again when writing the type's runtime descriptor (#54456).
+ base, suffix := types.SplitVargenSuffix(sym.Name)
+
+ var buf strings.Builder
+ buf.WriteString(base)
+ buf.WriteByte('[')
+ for i, targ := range dict.targs {
+ if i > 0 {
+ if i == dict.implicits {
+ buf.WriteByte(';')
+ } else {
+ buf.WriteByte(',')
+ }
+ }
+ buf.WriteString(targ.LinkString())
+ }
+ buf.WriteByte(']')
+ buf.WriteString(suffix)
+ return sym.Pkg.Lookup(buf.String())
+}
+
+// shapify returns the shape type for targ.
+//
+// If basic is true, then the type argument is used to instantiate a
+// type parameter whose constraint is a basic interface.
+func shapify(targ *types.Type, basic bool) *types.Type {
+ if targ.Kind() == types.TFORW {
+ if targ.IsFullyInstantiated() {
+ // For recursive instantiated type argument, it may still be a TFORW
+ // when shapifying happens. If we don't have targ's underlying type,
+ // shapify won't work. The worst case is we end up not reusing code
+ // optimally in some tricky cases.
+ if base.Debug.Shapify != 0 {
+ base.Warn("skipping shaping of recursive type %v", targ)
+ }
+ if targ.HasShape() {
+ return targ
+ }
+ } else {
+ base.Fatalf("%v is missing its underlying type", targ)
+ }
+ }
+
+ // When a pointer type is used to instantiate a type parameter
+ // constrained by a basic interface, we know the pointer's element
+ // type can't matter to the generated code. In this case, we can use
+ // an arbitrary pointer type as the shape type. (To match the
+ // non-unified frontend, we use `*byte`.)
+ //
+ // Otherwise, we simply use the type's underlying type as its shape.
+ //
+ // TODO(mdempsky): It should be possible to do much more aggressive
+ // shaping still; e.g., collapsing all pointer-shaped types into a
+ // common type, collapsing scalars of the same size/alignment into a
+ // common type, recursively shaping the element types of composite
+ // types, and discarding struct field names and tags. However, we'll
+ // need to start tracking how type parameters are actually used to
+ // implement some of these optimizations.
+ under := targ.Underlying()
+ if basic && targ.IsPtr() && !targ.Elem().NotInHeap() {
+ under = types.NewPtr(types.Types[types.TUINT8])
+ }
+
+ sym := types.ShapePkg.Lookup(under.LinkString())
+ if sym.Def == nil {
+ name := ir.NewDeclNameAt(under.Pos(), ir.OTYPE, sym)
+ typ := types.NewNamed(name)
+ typ.SetUnderlying(under)
+ sym.Def = typed(typ, name)
+ }
+ res := sym.Def.Type()
+ assert(res.IsShape())
+ assert(res.HasShape())
+ return res
+}
+
+// objDictIdx reads and returns the specified object dictionary.
+func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) *readerDict {
+ r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
+
+ dict := readerDict{
+ shaped: shaped,
+ }
+
+ nimplicits := r.Len()
+ nexplicits := r.Len()
+
+ if nimplicits > len(implicits) || nexplicits != len(explicits) {
+ base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits))
+ }
+
+ dict.targs = append(implicits[:nimplicits:nimplicits], explicits...)
+ dict.implicits = nimplicits
+
+ // Within the compiler, we can just skip over the type parameters.
+ for range dict.targs[dict.implicits:] {
+ // Skip past bounds without actually evaluating them.
+ r.typInfo()
+ }
+
+ dict.derived = make([]derivedInfo, r.Len())
+ dict.derivedTypes = make([]*types.Type, len(dict.derived))
+ for i := range dict.derived {
+ dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()}
+ }
+
+ // Runtime dictionary information; private to the compiler.
+
+ // If any type argument is already shaped, then we're constructing a
+ // shaped object, even if not explicitly requested (i.e., calling
+ // objIdx with shaped==true). This can happen with instantiating
+ // types that are referenced within a function body.
+ for _, targ := range dict.targs {
+ if targ.HasShape() {
+ dict.shaped = true
+ break
+ }
+ }
+
+ // And if we're constructing a shaped object, then shapify all type
+ // arguments.
+ for i, targ := range dict.targs {
+ basic := r.Bool()
+ if dict.shaped {
+ dict.targs[i] = shapify(targ, basic)
+ }
+ }
+
+ dict.baseSym = dict.mangle(sym)
+
+ dict.typeParamMethodExprs = make([]readerMethodExprInfo, r.Len())
+ for i := range dict.typeParamMethodExprs {
+ typeParamIdx := r.Len()
+ _, method := r.selector()
+
+ dict.typeParamMethodExprs[i] = readerMethodExprInfo{typeParamIdx, method}
+ }
+
+ dict.subdicts = make([]objInfo, r.Len())
+ for i := range dict.subdicts {
+ dict.subdicts[i] = r.objInfo()
+ }
+
+ dict.rtypes = make([]typeInfo, r.Len())
+ for i := range dict.rtypes {
+ dict.rtypes[i] = r.typInfo()
+ }
+
+ dict.itabs = make([]itabInfo, r.Len())
+ for i := range dict.itabs {
+ dict.itabs[i] = itabInfo{typ: r.typInfo(), iface: r.typInfo()}
+ }
+
+ return &dict
+}
+
+func (r *reader) typeParamNames() {
+ r.Sync(pkgbits.SyncTypeParamNames)
+
+ for range r.dict.targs[r.dict.implicits:] {
+ r.pos()
+ r.localIdent()
+ }
+}
+
+func (r *reader) method(rext *reader) *types.Field {
+ r.Sync(pkgbits.SyncMethod)
+ pos := r.pos()
+ pkg, sym := r.selector()
+ r.typeParamNames()
+ _, recv := r.param()
+ typ := r.signature(pkg, recv)
+
+ name := ir.NewNameAt(pos, ir.MethodSym(recv.Type, sym))
+ setType(name, typ)
+
+ name.Func = ir.NewFunc(r.pos())
+ name.Func.Nname = name
+
+ if r.hasTypeParams() {
+ name.Func.SetDupok(true)
+ if r.dict.shaped {
+ typ = shapeSig(name.Func, r.dict)
+ setType(name, typ)
+ }
+ }
+
+ rext.funcExt(name, sym)
+
+ meth := types.NewField(name.Func.Pos(), sym, typ)
+ meth.Nname = name
+ meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0)
+
+ return meth
+}
+
+func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) {
+ r.Sync(pkgbits.SyncSym)
+ pkg = r.pkg()
+ if name := r.String(); name != "" {
+ sym = pkg.Lookup(name)
+ }
+ return
+}
+
+func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) {
+ r.Sync(pkgbits.SyncLocalIdent)
+ pkg = r.pkg()
+ if name := r.String(); name != "" {
+ sym = pkg.Lookup(name)
+ }
+ return
+}
+
+func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) {
+ r.Sync(pkgbits.SyncSelector)
+ origPkg = r.pkg()
+ name := r.String()
+ pkg := origPkg
+ if types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ sym = pkg.Lookup(name)
+ return
+}
+
+func (r *reader) hasTypeParams() bool {
+ return r.dict.hasTypeParams()
+}
+
+func (dict *readerDict) hasTypeParams() bool {
+ return dict != nil && len(dict.targs) != 0
+}
+
+// @@@ Compiler extensions
+
+func (r *reader) funcExt(name *ir.Name, method *types.Sym) {
+ r.Sync(pkgbits.SyncFuncExt)
+
+ name.Class = 0 // so MarkFunc doesn't complain
+ ir.MarkFunc(name)
+
+ fn := name.Func
+
+ // XXX: Workaround because linker doesn't know how to copy Pos.
+ if !fn.Pos().IsKnown() {
+ fn.SetPos(name.Pos())
+ }
+
+ // Normally, we only compile local functions, which saves redundant compilation work.
+ // n.Defn is not nil for local functions, and is nil for imported function. But for
+ // generic functions, we might have an instantiation that no other package has seen before.
+ // So we need to be conservative and compile it again.
+ //
+ // That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function.
+ // TODO(mdempsky,cuonglm): find a cleaner way to handle this.
+ if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() {
+ name.Defn = fn
+ }
+
+ fn.Pragma = r.pragmaFlag()
+ r.linkname(name)
+
+ typecheck.Func(fn)
+
+ if r.Bool() {
+ assert(name.Defn == nil)
+
+ fn.ABI = obj.ABI(r.Uint64())
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(name.Type()).FieldSlice() {
+ f.Note = r.String()
+ }
+ }
+
+ if r.Bool() {
+ fn.Inl = &ir.Inline{
+ Cost: int32(r.Len()),
+ CanDelayResults: r.Bool(),
+ }
+ }
+ } else {
+ r.addBody(name.Func, method)
+ }
+ r.Sync(pkgbits.SyncEOF)
+}
+
+func (r *reader) typeExt(name *ir.Name) {
+ r.Sync(pkgbits.SyncTypeExt)
+
+ typ := name.Type()
+
+ if r.hasTypeParams() {
+ // Set "RParams" (really type arguments here, not parameters) so
+ // this type is treated as "fully instantiated". This ensures the
+ // type descriptor is written out as DUPOK and method wrappers are
+ // generated even for imported types.
+ var targs []*types.Type
+ targs = append(targs, r.dict.targs...)
+ typ.SetRParams(targs)
+ }
+
+ name.SetPragma(r.pragmaFlag())
+
+ typecheck.SetBaseTypeIndex(typ, r.Int64(), r.Int64())
+}
+
+func (r *reader) varExt(name *ir.Name) {
+ r.Sync(pkgbits.SyncVarExt)
+ r.linkname(name)
+}
+
+func (r *reader) linkname(name *ir.Name) {
+ assert(name.Op() == ir.ONAME)
+ r.Sync(pkgbits.SyncLinkname)
+
+ if idx := r.Int64(); idx >= 0 {
+ lsym := name.Linksym()
+ lsym.SymIdx = int32(idx)
+ lsym.Set(obj.AttrIndexed, true)
+ } else {
+ name.Sym().Linkname = r.String()
+ }
+}
+
+func (r *reader) pragmaFlag() ir.PragmaFlag {
+ r.Sync(pkgbits.SyncPragma)
+ return ir.PragmaFlag(r.Int())
+}
+
+// @@@ Function bodies
+
+// bodyReader tracks where the serialized IR for a local or imported,
+// generic function's body can be found.
+var bodyReader = map[*ir.Func]pkgReaderIndex{}
+
+// importBodyReader tracks where the serialized IR for an imported,
+// static (i.e., non-generic) function body can be read.
+var importBodyReader = map[*types.Sym]pkgReaderIndex{}
+
+// bodyReaderFor returns the pkgReaderIndex for reading fn's
+// serialized IR, and whether one was found.
+func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) {
+ if fn.Nname.Defn != nil {
+ pri, ok = bodyReader[fn]
+ base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available
+ } else {
+ pri, ok = importBodyReader[fn.Sym()]
+ }
+ return
+}
+
+// todoDicts holds the list of dictionaries that still need their
+// runtime dictionary objects constructed.
+var todoDicts []func()
+
+// todoBodies holds the list of function bodies that still need to be
+// constructed.
+var todoBodies []*ir.Func
+
+// addBody reads a function body reference from the element bitstream,
+// and associates it with fn.
+func (r *reader) addBody(fn *ir.Func, method *types.Sym) {
+ // addBody should only be called for local functions or imported
+ // generic functions; see comment in funcExt.
+ assert(fn.Nname.Defn != nil)
+
+ idx := r.Reloc(pkgbits.RelocBody)
+
+ pri := pkgReaderIndex{r.p, idx, r.dict, method, nil}
+ bodyReader[fn] = pri
+
+ if r.curfn == nil {
+ todoBodies = append(todoBodies, fn)
+ return
+ }
+
+ pri.funcBody(fn)
+}
+
+func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
+ r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ r.funcBody(fn)
+}
+
+// funcBody reads a function body definition from the element
+// bitstream, and populates fn with it.
+func (r *reader) funcBody(fn *ir.Func) {
+ r.curfn = fn
+ r.closureVars = fn.ClosureVars
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
+
+ ir.WithFunc(fn, func() {
+ r.funcargs(fn)
+
+ if r.syntheticBody(fn.Pos()) {
+ return
+ }
+
+ if !r.Bool() {
+ return
+ }
+
+ body := r.stmts()
+ if body == nil {
+ body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))}
+ }
+ fn.Body = body
+ fn.Endlineno = r.pos()
+ })
+
+ r.marker.WriteTo(fn)
+}
+
+// syntheticBody adds a synthetic body to r.curfn if appropriate, and
+// reports whether it did.
+func (r *reader) syntheticBody(pos src.XPos) bool {
+ if r.synthetic != nil {
+ r.synthetic(pos, r)
+ return true
+ }
+
+ // If this function has type parameters and isn't shaped, then we
+ // just tail call its corresponding shaped variant.
+ if r.hasTypeParams() && !r.dict.shaped {
+ r.callShaped(pos)
+ return true
+ }
+
+ return false
+}
+
+// callShaped emits a tail call to r.shapedFn, passing along the
+// arguments to the current function.
+func (r *reader) callShaped(pos src.XPos) {
+ shapedObj := r.dict.shapedObj
+ assert(shapedObj != nil)
+
+ var shapedFn ir.Node
+ if r.methodSym == nil {
+ // Instantiating a generic function; shapedObj is the shaped
+ // function itself.
+ assert(shapedObj.Op() == ir.ONAME && shapedObj.Class == ir.PFUNC)
+ shapedFn = shapedObj
+ } else {
+ // Instantiating a generic type's method; shapedObj is the shaped
+ // type, so we need to select it's corresponding method.
+ shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym)
+ }
+
+ recvs, params := r.syntheticArgs(pos)
+
+ // Construct the arguments list: receiver (if any), then runtime
+ // dictionary, and finally normal parameters.
+ //
+ // Note: For simplicity, shaped methods are added as normal methods
+ // on their shaped types. So existing code (e.g., packages ir and
+ // typecheck) expects the shaped type to appear as the receiver
+ // parameter (or first parameter, as a method expression). Hence
+ // putting the dictionary parameter after that is the least invasive
+ // solution at the moment.
+ var args ir.Nodes
+ args.Append(recvs...)
+ args.Append(typecheck.Expr(ir.NewAddrExpr(pos, r.p.dictNameOf(r.dict))))
+ args.Append(params...)
+
+ r.syntheticTailCall(pos, shapedFn, args)
+}
+
+// syntheticArgs returns the recvs and params arguments passed to the
+// current function.
+func (r *reader) syntheticArgs(pos src.XPos) (recvs, params ir.Nodes) {
+ sig := r.curfn.Nname.Type()
+
+ inlVarIdx := 0
+ addParams := func(out *ir.Nodes, params []*types.Field) {
+ for _, param := range params {
+ var arg ir.Node
+ if param.Nname != nil {
+ name := param.Nname.(*ir.Name)
+ if !ir.IsBlank(name) {
+ if r.inlCall != nil {
+ // During inlining, we want the respective inlvar where we
+ // assigned the callee's arguments.
+ arg = r.inlvars[inlVarIdx]
+ } else {
+ // Otherwise, we can use the parameter itself directly.
+ base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn)
+ arg = name
+ }
+ }
+ }
+
+ // For anonymous and blank parameters, we don't have an *ir.Name
+ // to use as the argument. However, since we know the shaped
+ // function won't use the value either, we can just pass the
+ // zero value. (Also unfortunately, we don't have an easy
+ // zero-value IR node; so we use a default-initialized temporary
+ // variable.)
+ if arg == nil {
+ tmp := typecheck.TempAt(pos, r.curfn, param.Type)
+ r.curfn.Body.Append(
+ typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)),
+ typecheck.Stmt(ir.NewAssignStmt(pos, tmp, nil)),
+ )
+ arg = tmp
+ }
+
+ out.Append(arg)
+ inlVarIdx++
+ }
+ }
+
+ addParams(&recvs, sig.Recvs().FieldSlice())
+ addParams(&params, sig.Params().FieldSlice())
+ return
+}
+
+// syntheticTailCall emits a tail call to fn, passing the given
+// arguments list.
+func (r *reader) syntheticTailCall(pos src.XPos, fn ir.Node, args ir.Nodes) {
+ // Mark the function as a wrapper so it doesn't show up in stack
+ // traces.
+ r.curfn.SetWrapper(true)
+
+ call := typecheck.Call(pos, fn, args, fn.Type().IsVariadic()).(*ir.CallExpr)
+
+ var stmt ir.Node
+ if fn.Type().NumResults() != 0 {
+ stmt = typecheck.Stmt(ir.NewReturnStmt(pos, []ir.Node{call}))
+ } else {
+ stmt = call
+ }
+ r.curfn.Body.Append(stmt)
+}
+
+// dictNameOf returns the runtime dictionary corresponding to dict.
+func (pr *pkgReader) dictNameOf(dict *readerDict) *ir.Name {
+ pos := base.AutogeneratedPos
+
+ // Check that we only instantiate runtime dictionaries with real types.
+ base.AssertfAt(!dict.shaped, pos, "runtime dictionary of shaped object %v", dict.baseSym)
+
+ sym := dict.baseSym.Pkg.Lookup(objabi.GlobalDictPrefix + "." + dict.baseSym.Name)
+ if sym.Def != nil {
+ return sym.Def.(*ir.Name)
+ }
+
+ name := ir.NewNameAt(pos, sym)
+ name.Class = ir.PEXTERN
+ sym.Def = name // break cycles with mutual subdictionaries
+
+ lsym := name.Linksym()
+ ot := 0
+
+ assertOffset := func(section string, offset int) {
+ base.AssertfAt(ot == offset*types.PtrSize, pos, "writing section %v at offset %v, but it should be at %v*%v", section, ot, offset, types.PtrSize)
+ }
+
+ assertOffset("type param method exprs", dict.typeParamMethodExprsOffset())
+ for _, info := range dict.typeParamMethodExprs {
+ typeParam := dict.targs[info.typeParamIdx]
+ method := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(typeParam), info.method)).(*ir.SelectorExpr)
+ assert(method.Op() == ir.OMETHEXPR)
+
+ rsym := method.FuncName().Linksym()
+ assert(rsym.ABI() == obj.ABIInternal) // must be ABIInternal; see ir.OCFUNC in ssagen/ssa.go
+
+ ot = objw.SymPtr(lsym, ot, rsym, 0)
+ }
+
+ assertOffset("subdictionaries", dict.subdictsOffset())
+ for _, info := range dict.subdicts {
+ explicits := pr.typListIdx(info.explicits, dict)
+
+ // Careful: Due to subdictionary cycles, name may not be fully
+ // initialized yet.
+ name := pr.objDictName(info.idx, dict.targs, explicits)
+
+ ot = objw.SymPtr(lsym, ot, name.Linksym(), 0)
+ }
+
+ assertOffset("rtypes", dict.rtypesOffset())
+ for _, info := range dict.rtypes {
+ typ := pr.typIdx(info, dict, true)
+ ot = objw.SymPtr(lsym, ot, reflectdata.TypeLinksym(typ), 0)
+
+ // TODO(mdempsky): Double check this.
+ reflectdata.MarkTypeUsedInInterface(typ, lsym)
+ }
+
+ // For each (typ, iface) pair, we write the *runtime.itab pointer
+ // for the pair. For pairs that don't actually require an itab
+ // (i.e., typ is an interface, or iface is an empty interface), we
+ // write a nil pointer instead. This is wasteful, but rare in
+ // practice (e.g., instantiating a type parameter with an interface
+ // type).
+ assertOffset("itabs", dict.itabsOffset())
+ for _, info := range dict.itabs {
+ typ := pr.typIdx(info.typ, dict, true)
+ iface := pr.typIdx(info.iface, dict, true)
+
+ if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() {
+ ot = objw.SymPtr(lsym, ot, reflectdata.ITabLsym(typ, iface), 0)
+ } else {
+ ot += types.PtrSize
+ }
+
+ // TODO(mdempsky): Double check this.
+ reflectdata.MarkTypeUsedInInterface(typ, lsym)
+ reflectdata.MarkTypeUsedInInterface(iface, lsym)
+ }
+
+ objw.Global(lsym, int32(ot), obj.DUPOK|obj.RODATA)
+
+ name.SetType(dict.varType())
+ name.SetTypecheck(1)
+
+ return name
+}
+
+// typeParamMethodExprsOffset returns the offset of the runtime
+// dictionary's type parameter method expressions section, in words.
+func (dict *readerDict) typeParamMethodExprsOffset() int {
+ return 0
+}
+
+// subdictsOffset returns the offset of the runtime dictionary's
+// subdictionary section, in words.
+func (dict *readerDict) subdictsOffset() int {
+ return dict.typeParamMethodExprsOffset() + len(dict.typeParamMethodExprs)
+}
+
+// rtypesOffset returns the offset of the runtime dictionary's rtypes
+// section, in words.
+func (dict *readerDict) rtypesOffset() int {
+ return dict.subdictsOffset() + len(dict.subdicts)
+}
+
+// itabsOffset returns the offset of the runtime dictionary's itabs
+// section, in words.
+func (dict *readerDict) itabsOffset() int {
+ return dict.rtypesOffset() + len(dict.rtypes)
+}
+
+// numWords returns the total number of words that comprise dict's
+// runtime dictionary variable.
+func (dict *readerDict) numWords() int64 {
+ return int64(dict.itabsOffset() + len(dict.itabs))
+}
+
+// varType returns the type of dict's runtime dictionary variable.
+func (dict *readerDict) varType() *types.Type {
+ return types.NewArray(types.Types[types.TUINTPTR], dict.numWords())
+}
+
+func (r *reader) funcargs(fn *ir.Func) {
+ sig := fn.Nname.Type()
+
+ if recv := sig.Recv(); recv != nil {
+ r.funcarg(recv, recv.Sym, ir.PPARAM)
+ }
+ for _, param := range sig.Params().FieldSlice() {
+ r.funcarg(param, param.Sym, ir.PPARAM)
+ }
+
+ for i, param := range sig.Results().FieldSlice() {
+ sym := types.OrigSym(param.Sym)
+
+ if sym == nil || sym.IsBlank() {
+ prefix := "~r"
+ if r.inlCall != nil {
+ prefix = "~R"
+ } else if sym != nil {
+ prefix = "~b"
+ }
+ sym = typecheck.LookupNum(prefix, i)
+ }
+
+ r.funcarg(param, sym, ir.PPARAMOUT)
+ }
+}
+
+func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
+ if sym == nil {
+ assert(ctxt == ir.PPARAM)
+ if r.inlCall != nil {
+ r.inlvars.Append(ir.BlankNode)
+ }
+ return
+ }
+
+ name := ir.NewNameAt(r.inlPos(param.Pos), sym)
+ setType(name, param.Type)
+ r.addLocal(name, ctxt)
+
+ if r.inlCall == nil {
+ if !r.funarghack {
+ param.Sym = sym
+ param.Nname = name
+ }
+ } else {
+ if ctxt == ir.PPARAMOUT {
+ r.retvars.Append(name)
+ } else {
+ r.inlvars.Append(name)
+ }
+ }
+}
+
+func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
+ assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
+
+ if name.Sym().Name == dictParamName {
+ r.dictParam = name
+ } else {
+ if r.synthetic == nil {
+ r.Sync(pkgbits.SyncAddLocal)
+ if r.p.SyncMarkers() {
+ want := r.Int()
+ if have := len(r.locals); have != want {
+ base.FatalfAt(name.Pos(), "locals table has desynced")
+ }
+ }
+ r.varDictIndex(name)
+ }
+
+ r.locals = append(r.locals, name)
+ }
+
+ name.SetUsed(true)
+
+ // TODO(mdempsky): Move earlier.
+ if ir.IsBlank(name) {
+ return
+ }
+
+ if r.inlCall != nil {
+ if ctxt == ir.PAUTO {
+ name.SetInlLocal(true)
+ } else {
+ name.SetInlFormal(true)
+ ctxt = ir.PAUTO
+ }
+
+ // TODO(mdempsky): Rethink this hack.
+ if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 {
+ name.SetPos(r.inlCall.Pos())
+ name.SetInlFormal(false)
+ name.SetInlLocal(false)
+ }
+ }
+
+ name.Class = ctxt
+ name.Curfn = r.curfn
+
+ r.curfn.Dcl = append(r.curfn.Dcl, name)
+
+ if ctxt == ir.PAUTO {
+ name.SetFrameOffset(0)
+ }
+}
+
+func (r *reader) useLocal() *ir.Name {
+ r.Sync(pkgbits.SyncUseObjLocal)
+ if r.Bool() {
+ return r.locals[r.Len()]
+ }
+ return r.closureVars[r.Len()]
+}
+
+func (r *reader) openScope() {
+ r.Sync(pkgbits.SyncOpenScope)
+ pos := r.pos()
+
+ if base.Flag.Dwarf {
+ r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl))
+ r.marker.Push(pos)
+ }
+}
+
+func (r *reader) closeScope() {
+ r.Sync(pkgbits.SyncCloseScope)
+ r.lastCloseScopePos = r.pos()
+
+ r.closeAnotherScope()
+}
+
+// closeAnotherScope is like closeScope, but it reuses the same mark
+// position as the last closeScope call. This is useful for "for" and
+// "if" statements, as their implicit blocks always end at the same
+// position as an explicit block.
+func (r *reader) closeAnotherScope() {
+ r.Sync(pkgbits.SyncCloseAnotherScope)
+
+ if base.Flag.Dwarf {
+ scopeVars := r.scopeVars[len(r.scopeVars)-1]
+ r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
+
+ // Quirkish: noder decides which scopes to keep before
+ // typechecking, whereas incremental typechecking during IR
+ // construction can result in new autotemps being allocated. To
+ // produce identical output, we ignore autotemps here for the
+ // purpose of deciding whether to retract the scope.
+ //
+ // This is important for net/http/fcgi, because it contains:
+ //
+ // var body io.ReadCloser
+ // if len(content) > 0 {
+ // body, req.pw = io.Pipe()
+ // } else { … }
+ //
+ // Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
+ // variable at the call site.
+ //
+ // Noder does not preserve the scope where the io.Pipe() call
+ // resides, because it doesn't contain any declared variables in
+ // source. So the ~R0 variable ends up being assigned to the
+ // enclosing scope instead.
+ //
+ // However, typechecking this assignment also introduces
+ // autotemps, because io.Pipe's results need conversion before
+ // they can be assigned to their respective destination variables.
+ //
+ // TODO(mdempsky): We should probably just keep all scopes, and
+ // let dwarfgen take care of pruning them instead.
+ retract := true
+ for _, n := range r.curfn.Dcl[scopeVars:] {
+ if !n.AutoTemp() {
+ retract = false
+ break
+ }
+ }
+
+ if retract {
+ // no variables were declared in this scope, so we can retract it.
+ r.marker.Unpush()
+ } else {
+ r.marker.Pop(r.lastCloseScopePos)
+ }
+ }
+}
+
+// @@@ Statements
+
+func (r *reader) stmt() ir.Node {
+ switch stmts := r.stmts(); len(stmts) {
+ case 0:
+ return nil
+ case 1:
+ return stmts[0]
+ default:
+ return ir.NewBlockStmt(stmts[0].Pos(), stmts)
+ }
+}
+
+func (r *reader) stmts() []ir.Node {
+ assert(ir.CurFunc == r.curfn)
+ var res ir.Nodes
+
+ r.Sync(pkgbits.SyncStmts)
+ for {
+ tag := codeStmt(r.Code(pkgbits.SyncStmt1))
+ if tag == stmtEnd {
+ r.Sync(pkgbits.SyncStmtsEnd)
+ return res
+ }
+
+ if n := r.stmt1(tag, &res); n != nil {
+ res.Append(typecheck.Stmt(n))
+ }
+ }
+}
+
+func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
+ var label *types.Sym
+ if n := len(*out); n > 0 {
+ if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok {
+ label = ls.Label
+ }
+ }
+
+ switch tag {
+ default:
+ panic("unexpected statement")
+
+ case stmtAssign:
+ pos := r.pos()
+ names, lhs := r.assignList()
+ rhs := r.multiExpr()
+
+ if len(rhs) == 0 {
+ for _, name := range names {
+ as := ir.NewAssignStmt(pos, name, nil)
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
+ out.Append(typecheck.Stmt(as))
+ }
+ return nil
+ }
+
+ if len(lhs) == 1 && len(rhs) == 1 {
+ n := ir.NewAssignStmt(pos, lhs[0], rhs[0])
+ n.Def = r.initDefn(n, names)
+ return n
+ }
+
+ n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
+ n.Def = r.initDefn(n, names)
+ return n
+
+ case stmtAssignOp:
+ op := r.op()
+ lhs := r.expr()
+ pos := r.pos()
+ rhs := r.expr()
+ return ir.NewAssignOpStmt(pos, op, lhs, rhs)
+
+ case stmtIncDec:
+ op := r.op()
+ lhs := r.expr()
+ pos := r.pos()
+ n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one))
+ n.IncDec = true
+ return n
+
+ case stmtBlock:
+ out.Append(r.blockStmt()...)
+ return nil
+
+ case stmtBranch:
+ pos := r.pos()
+ op := r.op()
+ sym := r.optLabel()
+ return ir.NewBranchStmt(pos, op, sym)
+
+ case stmtCall:
+ pos := r.pos()
+ op := r.op()
+ call := r.expr()
+ return ir.NewGoDeferStmt(pos, op, call)
+
+ case stmtExpr:
+ return r.expr()
+
+ case stmtFor:
+ return r.forStmt(label)
+
+ case stmtIf:
+ return r.ifStmt()
+
+ case stmtLabel:
+ pos := r.pos()
+ sym := r.label()
+ return ir.NewLabelStmt(pos, sym)
+
+ case stmtReturn:
+ pos := r.pos()
+ results := r.multiExpr()
+ return ir.NewReturnStmt(pos, results)
+
+ case stmtSelect:
+ return r.selectStmt(label)
+
+ case stmtSend:
+ pos := r.pos()
+ ch := r.expr()
+ value := r.expr()
+ return ir.NewSendStmt(pos, ch, value)
+
+ case stmtSwitch:
+ return r.switchStmt(label)
+ }
+}
+
+func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
+ lhs := make([]ir.Node, r.Len())
+ var names []*ir.Name
+
+ for i := range lhs {
+ expr, def := r.assign()
+ lhs[i] = expr
+ if def {
+ names = append(names, expr.(*ir.Name))
+ }
+ }
+
+ return names, lhs
+}
+
+// assign returns an assignee expression. It also reports whether the
+// returned expression is a newly declared variable.
+func (r *reader) assign() (ir.Node, bool) {
+ switch tag := codeAssign(r.Code(pkgbits.SyncAssign)); tag {
+ default:
+ panic("unhandled assignee expression")
+
+ case assignBlank:
+ return typecheck.AssignExpr(ir.BlankNode), false
+
+ case assignDef:
+ pos := r.pos()
+ setBasePos(pos)
+ _, sym := r.localIdent()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, sym)
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ return name, true
+
+ case assignExpr:
+ return r.expr(), false
+ }
+}
+
+func (r *reader) blockStmt() []ir.Node {
+ r.Sync(pkgbits.SyncBlockStmt)
+ r.openScope()
+ stmts := r.stmts()
+ r.closeScope()
+ return stmts
+}
+
+func (r *reader) forStmt(label *types.Sym) ir.Node {
+ r.Sync(pkgbits.SyncForStmt)
+
+ r.openScope()
+
+ if r.Bool() {
+ pos := r.pos()
+ rang := ir.NewRangeStmt(pos, nil, nil, nil, nil)
+ rang.Label = label
+
+ names, lhs := r.assignList()
+ if len(lhs) >= 1 {
+ rang.Key = lhs[0]
+ if len(lhs) >= 2 {
+ rang.Value = lhs[1]
+ }
+ }
+ rang.Def = r.initDefn(rang, names)
+
+ rang.X = r.expr()
+ if rang.X.Type().IsMap() {
+ rang.RType = r.rtype(pos)
+ }
+ if rang.Key != nil && !ir.IsBlank(rang.Key) {
+ rang.KeyTypeWord, rang.KeySrcRType = r.convRTTI(pos)
+ }
+ if rang.Value != nil && !ir.IsBlank(rang.Value) {
+ rang.ValueTypeWord, rang.ValueSrcRType = r.convRTTI(pos)
+ }
+
+ rang.Body = r.blockStmt()
+ r.closeAnotherScope()
+
+ return rang
+ }
+
+ pos := r.pos()
+ init := r.stmt()
+ cond := r.optExpr()
+ post := r.stmt()
+ body := r.blockStmt()
+ r.closeAnotherScope()
+
+ stmt := ir.NewForStmt(pos, init, cond, post, body)
+ stmt.Label = label
+ return stmt
+}
+
+func (r *reader) ifStmt() ir.Node {
+ r.Sync(pkgbits.SyncIfStmt)
+ r.openScope()
+ pos := r.pos()
+ init := r.stmts()
+ cond := r.expr()
+ then := r.blockStmt()
+ els := r.stmts()
+ n := ir.NewIfStmt(pos, cond, then, els)
+ n.SetInit(init)
+ r.closeAnotherScope()
+ return n
+}
+
+func (r *reader) selectStmt(label *types.Sym) ir.Node {
+ r.Sync(pkgbits.SyncSelectStmt)
+
+ pos := r.pos()
+ clauses := make([]*ir.CommClause, r.Len())
+ for i := range clauses {
+ if i > 0 {
+ r.closeScope()
+ }
+ r.openScope()
+
+ pos := r.pos()
+ comm := r.stmt()
+ body := r.stmts()
+
+ // "case i = <-c: ..." may require an implicit conversion (e.g.,
+ // see fixedbugs/bug312.go). Currently, typecheck throws away the
+ // implicit conversion and relies on it being reinserted later,
+ // but that would lose any explicit RTTI operands too. To preserve
+ // RTTI, we rewrite this as "case tmp := <-c: i = tmp; ...".
+ if as, ok := comm.(*ir.AssignStmt); ok && as.Op() == ir.OAS && !as.Def {
+ if conv, ok := as.Y.(*ir.ConvExpr); ok && conv.Op() == ir.OCONVIFACE {
+ base.AssertfAt(conv.Implicit(), conv.Pos(), "expected implicit conversion: %v", conv)
+
+ recv := conv.X
+ base.AssertfAt(recv.Op() == ir.ORECV, recv.Pos(), "expected receive expression: %v", recv)
+
+ tmp := r.temp(pos, recv.Type())
+
+ // Replace comm with `tmp := <-c`.
+ tmpAs := ir.NewAssignStmt(pos, tmp, recv)
+ tmpAs.Def = true
+ tmpAs.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ comm = tmpAs
+
+ // Change original assignment to `i = tmp`, and prepend to body.
+ conv.X = tmp
+ body = append([]ir.Node{as}, body...)
+ }
+ }
+
+ // multiExpr will have desugared a comma-ok receive expression
+ // into a separate statement. However, the rest of the compiler
+ // expects comm to be the OAS2RECV statement itself, so we need to
+ // shuffle things around to fit that pattern.
+ if as2, ok := comm.(*ir.AssignListStmt); ok && as2.Op() == ir.OAS2 {
+ init := ir.TakeInit(as2.Rhs[0])
+ base.AssertfAt(len(init) == 1 && init[0].Op() == ir.OAS2RECV, as2.Pos(), "unexpected assignment: %+v", as2)
+
+ comm = init[0]
+ body = append([]ir.Node{as2}, body...)
+ }
+
+ clauses[i] = ir.NewCommStmt(pos, comm, body)
+ }
+ if len(clauses) > 0 {
+ r.closeScope()
+ }
+ n := ir.NewSelectStmt(pos, clauses)
+ n.Label = label
+ return n
+}
+
+func (r *reader) switchStmt(label *types.Sym) ir.Node {
+ r.Sync(pkgbits.SyncSwitchStmt)
+
+ r.openScope()
+ pos := r.pos()
+ init := r.stmt()
+
+ var tag ir.Node
+ var ident *ir.Ident
+ var iface *types.Type
+ if r.Bool() {
+ pos := r.pos()
+ if r.Bool() {
+ pos := r.pos()
+ _, sym := r.localIdent()
+ ident = ir.NewIdent(pos, sym)
+ }
+ x := r.expr()
+ iface = x.Type()
+ tag = ir.NewTypeSwitchGuard(pos, ident, x)
+ } else {
+ tag = r.optExpr()
+ }
+
+ clauses := make([]*ir.CaseClause, r.Len())
+ for i := range clauses {
+ if i > 0 {
+ r.closeScope()
+ }
+ r.openScope()
+
+ pos := r.pos()
+ var cases, rtypes []ir.Node
+ if iface != nil {
+ cases = make([]ir.Node, r.Len())
+ if len(cases) == 0 {
+ cases = nil // TODO(mdempsky): Unclear if this matters.
+ }
+ for i := range cases {
+ if r.Bool() { // case nil
+ cases[i] = typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
+ } else {
+ cases[i] = r.exprType()
+ }
+ }
+ } else {
+ cases = r.exprList()
+
+ // For `switch { case any(true): }` (e.g., issue 3980 in
+ // test/switch.go), the backend still creates a mixed bool/any
+ // comparison, and we need to explicitly supply the RTTI for the
+ // comparison.
+ //
+ // TODO(mdempsky): Change writer.go to desugar "switch {" into
+ // "switch true {", which we already handle correctly.
+ if tag == nil {
+ for i, cas := range cases {
+ if cas.Type().IsEmptyInterface() {
+ for len(rtypes) < i {
+ rtypes = append(rtypes, nil)
+ }
+ rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL]))
+ }
+ }
+ }
+ }
+
+ clause := ir.NewCaseStmt(pos, cases, nil)
+ clause.RTypes = rtypes
+
+ if ident != nil {
+ pos := r.pos()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, ident.Sym())
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ clause.Var = name
+ name.Defn = tag
+ }
+
+ clause.Body = r.stmts()
+ clauses[i] = clause
+ }
+ if len(clauses) > 0 {
+ r.closeScope()
+ }
+ r.closeScope()
+
+ n := ir.NewSwitchStmt(pos, tag, clauses)
+ n.Label = label
+ if init != nil {
+ n.SetInit([]ir.Node{init})
+ }
+ return n
+}
+
+func (r *reader) label() *types.Sym {
+ r.Sync(pkgbits.SyncLabel)
+ name := r.String()
+ if r.inlCall != nil {
+ name = fmt.Sprintf("~%s·%d", name, inlgen)
+ }
+ return typecheck.Lookup(name)
+}
+
+func (r *reader) optLabel() *types.Sym {
+ r.Sync(pkgbits.SyncOptLabel)
+ if r.Bool() {
+ return r.label()
+ }
+ return nil
+}
+
+// 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.
+func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
+ if len(names) == 0 {
+ return false
+ }
+
+ init := make([]ir.Node, len(names))
+ for i, name := range names {
+ name.Defn = defn
+ init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
+ }
+ defn.SetInit(init)
+ return true
+}
+
+// @@@ Expressions
+
+// expr reads and returns a typechecked expression.
+func (r *reader) expr() (res ir.Node) {
+ defer func() {
+ if res != nil && res.Typecheck() == 0 {
+ base.FatalfAt(res.Pos(), "%v missed typecheck", res)
+ }
+ }()
+
+ switch tag := codeExpr(r.Code(pkgbits.SyncExpr)); tag {
+ default:
+ panic("unhandled expression")
+
+ case exprLocal:
+ return typecheck.Expr(r.useLocal())
+
+ case exprGlobal:
+ // Callee instead of Expr allows builtins
+ // TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
+ return typecheck.Callee(r.obj())
+
+ case exprFuncInst:
+ origPos, pos := r.origPos()
+ wrapperFn, baseFn, dictPtr := r.funcInst(pos)
+ if wrapperFn != nil {
+ return wrapperFn
+ }
+ return r.curry(origPos, false, baseFn, dictPtr, nil)
+
+ case exprConst:
+ pos := r.pos()
+ typ := r.typ()
+ val := FixValue(typ, r.Value())
+ op := r.op()
+ orig := r.String()
+ return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
+
+ case exprNil:
+ pos := r.pos()
+ typ := r.typ()
+ return Nil(pos, typ)
+
+ case exprCompLit:
+ return r.compLit()
+
+ case exprFuncLit:
+ return r.funcLit()
+
+ case exprFieldVal:
+ x := r.expr()
+ pos := r.pos()
+ _, sym := r.selector()
+
+ return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
+
+ case exprMethodVal:
+ recv := r.expr()
+ origPos, pos := r.origPos()
+ wrapperFn, baseFn, dictPtr := r.methodExpr()
+
+ // For simple wrapperFn values, the existing machinery for creating
+ // and deduplicating wrapperFn value wrappers still works fine.
+ if wrapperFn, ok := wrapperFn.(*ir.SelectorExpr); ok && wrapperFn.Op() == ir.OMETHEXPR {
+ // The receiver expression we constructed may have a shape type.
+ // For example, in fixedbugs/issue54343.go, `New[int]()` is
+ // constructed as `New[go.shape.int](&.dict.New[int])`, which
+ // has type `*T[go.shape.int]`, not `*T[int]`.
+ //
+ // However, the method we want to select here is `(*T[int]).M`,
+ // not `(*T[go.shape.int]).M`, so we need to manually convert
+ // the type back so that the OXDOT resolves correctly.
+ //
+ // TODO(mdempsky): Logically it might make more sense for
+ // exprCall to take responsibility for setting a non-shaped
+ // result type, but this is the only place where we care
+ // currently. And only because existing ir.OMETHVALUE backend
+ // code relies on n.X.Type() instead of n.Selection.Recv().Type
+ // (because the latter is types.FakeRecvType() in the case of
+ // interface method values).
+ //
+ if recv.Type().HasShape() {
+ typ := wrapperFn.Type().Params().Field(0).Type
+ if !types.Identical(typ, recv.Type()) {
+ base.FatalfAt(wrapperFn.Pos(), "receiver %L does not match %L", recv, wrapperFn)
+ }
+ recv = typecheck.Expr(ir.NewConvExpr(recv.Pos(), ir.OCONVNOP, typ, recv))
+ }
+
+ n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, recv, wrapperFn.Sel)).(*ir.SelectorExpr)
+
+ // As a consistency check here, we make sure "n" selected the
+ // same method (represented by a types.Field) that wrapperFn
+ // selected. However, for anonymous receiver types, there can be
+ // multiple such types.Field instances (#58563). So we may need
+ // to fallback to making sure Sym and Type (including the
+ // receiver parameter's type) match.
+ if n.Selection != wrapperFn.Selection {
+ assert(n.Selection.Sym == wrapperFn.Selection.Sym)
+ assert(types.Identical(n.Selection.Type, wrapperFn.Selection.Type))
+ assert(types.Identical(n.Selection.Type.Recv().Type, wrapperFn.Selection.Type.Recv().Type))
+ }
+
+ wrapper := methodValueWrapper{
+ rcvr: n.X.Type(),
+ method: n.Selection,
+ }
+
+ if r.importedDef() {
+ haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
+ } else {
+ needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
+ }
+ return n
+ }
+
+ // For more complicated method expressions, we construct a
+ // function literal wrapper.
+ return r.curry(origPos, true, baseFn, recv, dictPtr)
+
+ case exprMethodExpr:
+ recv := r.typ()
+
+ implicits := make([]int, r.Len())
+ for i := range implicits {
+ implicits[i] = r.Len()
+ }
+ var deref, addr bool
+ if r.Bool() {
+ deref = true
+ } else if r.Bool() {
+ addr = true
+ }
+
+ origPos, pos := r.origPos()
+ wrapperFn, baseFn, dictPtr := r.methodExpr()
+
+ // If we already have a wrapper and don't need to do anything with
+ // it, we can just return the wrapper directly.
+ //
+ // N.B., we use implicits/deref/addr here as the source of truth
+ // rather than types.Identical, because the latter can be confused
+ // by tricky promoted methods (e.g., typeparam/mdempsky/21.go).
+ if wrapperFn != nil && len(implicits) == 0 && !deref && !addr {
+ if !types.Identical(recv, wrapperFn.Type().Params().Field(0).Type) {
+ base.FatalfAt(pos, "want receiver type %v, but have method %L", recv, wrapperFn)
+ }
+ return wrapperFn
+ }
+
+ // Otherwise, if the wrapper function is a static method
+ // expression (OMETHEXPR) and the receiver type is unshaped, then
+ // we can rely on a statically generated wrapper being available.
+ if method, ok := wrapperFn.(*ir.SelectorExpr); ok && method.Op() == ir.OMETHEXPR && !recv.HasShape() {
+ return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), method.Sel)).(*ir.SelectorExpr)
+ }
+
+ return r.methodExprWrap(origPos, recv, implicits, deref, addr, baseFn, dictPtr)
+
+ case exprIndex:
+ x := r.expr()
+ pos := r.pos()
+ index := r.expr()
+ n := typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+ switch n.Op() {
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
+
+ case exprSlice:
+ x := r.expr()
+ pos := r.pos()
+ var index [3]ir.Node
+ for i := range index {
+ index[i] = r.optExpr()
+ }
+ op := ir.OSLICE
+ if index[2] != nil {
+ op = ir.OSLICE3
+ }
+ return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2]))
+
+ case exprAssert:
+ x := r.expr()
+ pos := r.pos()
+ typ := r.exprType()
+ srcRType := r.rtype(pos)
+
+ // TODO(mdempsky): Always emit ODYNAMICDOTTYPE for uniformity?
+ if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
+ assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType)
+ assert.SrcRType = srcRType
+ assert.ITab = typ.ITab
+ return typed(typ.Type(), assert)
+ }
+ return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ.Type()))
+
+ case exprUnaryOp:
+ op := r.op()
+ pos := r.pos()
+ x := r.expr()
+
+ switch op {
+ case ir.OADDR:
+ return typecheck.Expr(typecheck.NodAddrAt(pos, x))
+ case ir.ODEREF:
+ return typecheck.Expr(ir.NewStarExpr(pos, x))
+ }
+ return typecheck.Expr(ir.NewUnaryExpr(pos, op, x))
+
+ case exprBinaryOp:
+ op := r.op()
+ x := r.expr()
+ pos := r.pos()
+ y := r.expr()
+
+ switch op {
+ case ir.OANDAND, ir.OOROR:
+ return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
+ }
+ return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+
+ case exprRecv:
+ x := r.expr()
+ pos := r.pos()
+ for i, n := 0, r.Len(); i < n; i++ {
+ x = Implicit(DotField(pos, x, r.Len()))
+ }
+ if r.Bool() { // needs deref
+ x = Implicit(Deref(pos, x.Type().Elem(), x))
+ } else if r.Bool() { // needs addr
+ x = Implicit(Addr(pos, x))
+ }
+ return x
+
+ case exprCall:
+ var fun ir.Node
+ var args ir.Nodes
+ if r.Bool() { // method call
+ recv := r.expr()
+ _, method, dictPtr := r.methodExpr()
+
+ if recv.Type().IsInterface() && method.Op() == ir.OMETHEXPR {
+ method := method.(*ir.SelectorExpr)
+
+ // The compiler backend (e.g., devirtualization) handle
+ // OCALLINTER/ODOTINTER better than OCALLFUNC/OMETHEXPR for
+ // interface calls, so we prefer to continue constructing
+ // calls that way where possible.
+ //
+ // There are also corner cases where semantically it's perhaps
+ // significant; e.g., fixedbugs/issue15975.go, #38634, #52025.
+
+ fun = typecheck.Callee(ir.NewSelectorExpr(method.Pos(), ir.OXDOT, recv, method.Sel))
+ } else {
+ if recv.Type().IsInterface() {
+ // N.B., this happens currently for typeparam/issue51521.go
+ // and typeparam/typeswitch3.go.
+ if base.Flag.LowerM > 0 {
+ base.WarnfAt(method.Pos(), "imprecise interface call")
+ }
+ }
+
+ fun = method
+ args.Append(recv)
+ }
+ if dictPtr != nil {
+ args.Append(dictPtr)
+ }
+ } else if r.Bool() { // call to instanced function
+ pos := r.pos()
+ _, shapedFn, dictPtr := r.funcInst(pos)
+ fun = shapedFn
+ args.Append(dictPtr)
+ } else {
+ fun = r.expr()
+ }
+ pos := r.pos()
+ args.Append(r.multiExpr()...)
+ dots := r.Bool()
+ n := typecheck.Call(pos, fun, args, dots)
+ switch n.Op() {
+ case ir.OAPPEND:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ // For append(a, b...), we don't need the implicit conversion. The typechecker already
+ // ensured that a and b are both slices with the same base type, or []byte and string.
+ if n.IsDDD {
+ if conv, ok := n.Args[1].(*ir.ConvExpr); ok && conv.Op() == ir.OCONVNOP && conv.Implicit() {
+ n.Args[1] = conv.X
+ }
+ }
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ case ir.OUNSAFESLICE:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
+
+ case exprMake:
+ pos := r.pos()
+ typ := r.exprType()
+ extra := r.exprs()
+ n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr)
+ n.RType = r.rtype(pos)
+ return n
+
+ case exprNew:
+ pos := r.pos()
+ typ := r.exprType()
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
+
+ case exprReshape:
+ typ := r.typ()
+ x := r.expr()
+
+ if types.IdenticalStrict(x.Type(), typ) {
+ return x
+ }
+
+ // Comparison expressions are constructed as "untyped bool" still.
+ //
+ // TODO(mdempsky): It should be safe to reshape them here too, but
+ // maybe it's better to construct them with the proper type
+ // instead.
+ if x.Type() == types.UntypedBool && typ.IsBoolean() {
+ return x
+ }
+
+ base.AssertfAt(x.Type().HasShape() || typ.HasShape(), x.Pos(), "%L and %v are not shape types", x, typ)
+ base.AssertfAt(types.Identical(x.Type(), typ), x.Pos(), "%L is not shape-identical to %v", x, typ)
+
+ // We use ir.HasUniquePos here as a check that x only appears once
+ // in the AST, so it's okay for us to call SetType without
+ // breaking any other uses of it.
+ //
+ // Notably, any ONAMEs should already have the exactly right shape
+ // type and been caught by types.IdenticalStrict above.
+ base.AssertfAt(ir.HasUniquePos(x), x.Pos(), "cannot call SetType(%v) on %L", typ, x)
+
+ if base.Debug.Reshape != 0 {
+ base.WarnfAt(x.Pos(), "reshaping %L to %v", x, typ)
+ }
+
+ x.SetType(typ)
+ return x
+
+ case exprConvert:
+ implicit := r.Bool()
+ typ := r.typ()
+ pos := r.pos()
+ typeWord, srcRType := r.convRTTI(pos)
+ dstTypeParam := r.Bool()
+ identical := r.Bool()
+ x := r.expr()
+
+ // TODO(mdempsky): Stop constructing expressions of untyped type.
+ x = typecheck.DefaultLit(x, typ)
+
+ ce := ir.NewConvExpr(pos, ir.OCONV, typ, x)
+ ce.TypeWord, ce.SrcRType = typeWord, srcRType
+ if implicit {
+ ce.SetImplicit(true)
+ }
+ n := typecheck.Expr(ce)
+
+ // Conversions between non-identical, non-empty interfaces always
+ // requires a runtime call, even if they have identical underlying
+ // interfaces. This is because we create separate itab instances
+ // for each unique interface type, not merely each unique
+ // interface shape.
+ //
+ // However, due to shape types, typecheck.Expr might mistakenly
+ // think a conversion between two non-empty interfaces are
+ // identical and set ir.OCONVNOP, instead of ir.OCONVIFACE. To
+ // ensure we update the itab field appropriately, we force it to
+ // ir.OCONVIFACE instead when shape types are involved.
+ //
+ // TODO(mdempsky): Are there other places we might get this wrong?
+ // Should this be moved down into typecheck.{Assign,Convert}op?
+ // This would be a non-issue if itabs were unique for each
+ // *underlying* interface type instead.
+ if !identical {
+ if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OCONVNOP && n.Type().IsInterface() && !n.Type().IsEmptyInterface() && (n.Type().HasShape() || n.X.Type().HasShape()) {
+ n.SetOp(ir.OCONVIFACE)
+ }
+ }
+
+ // spec: "If the type is a type parameter, the constant is converted
+ // into a non-constant value of the type parameter."
+ if dstTypeParam && ir.IsConstNode(n) {
+ // Wrap in an OCONVNOP node to ensure result is non-constant.
+ n = Implicit(ir.NewConvExpr(pos, ir.OCONVNOP, n.Type(), n))
+ n.SetTypecheck(1)
+ }
+ return n
+ }
+}
+
+// funcInst reads an instantiated function reference, and returns
+// three (possibly nil) expressions related to it:
+//
+// baseFn is always non-nil: it's either a function of the appropriate
+// type already, or it has an extra dictionary parameter as the first
+// parameter.
+//
+// If dictPtr is non-nil, then it's a dictionary argument that must be
+// passed as the first argument to baseFn.
+//
+// If wrapperFn is non-nil, then it's either the same as baseFn (if
+// dictPtr is nil), or it's semantically equivalent to currying baseFn
+// to pass dictPtr. (wrapperFn is nil when dictPtr is an expression
+// that needs to be computed dynamically.)
+//
+// For callers that are creating a call to the returned function, it's
+// best to emit a call to baseFn, and include dictPtr in the arguments
+// list as appropriate.
+//
+// For callers that want to return the function without invoking it,
+// they may return wrapperFn if it's non-nil; but otherwise, they need
+// to create their own wrapper.
+func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
+ // Like in methodExpr, I'm pretty sure this isn't needed.
+ var implicits []*types.Type
+ if r.dict != nil {
+ implicits = r.dict.targs
+ }
+
+ if r.Bool() { // dynamic subdictionary
+ idx := r.Len()
+ info := r.dict.subdicts[idx]
+ explicits := r.p.typListIdx(info.explicits, r.dict)
+
+ baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
+
+ // TODO(mdempsky): Is there a more robust way to get the
+ // dictionary pointer type here?
+ dictPtrType := baseFn.Type().Params().Field(0).Type
+ dictPtr = typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx)))
+
+ return
+ }
+
+ info := r.objInfo()
+ explicits := r.p.typListIdx(info.explicits, r.dict)
+
+ wrapperFn = r.p.objIdx(info.idx, implicits, explicits, false).(*ir.Name)
+ baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
+
+ dictName := r.p.objDictName(info.idx, implicits, explicits)
+ dictPtr = typecheck.Expr(ir.NewAddrExpr(pos, dictName))
+
+ return
+}
+
+func (pr *pkgReader) objDictName(idx pkgbits.Index, implicits, explicits []*types.Type) *ir.Name {
+ rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+ _, sym := rname.qualifiedIdent()
+ tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
+
+ if tag == pkgbits.ObjStub {
+ assert(!sym.IsBlank())
+ if pri, ok := objReader[sym]; ok {
+ return pri.pr.objDictName(pri.idx, nil, explicits)
+ }
+ base.Fatalf("unresolved stub: %v", sym)
+ }
+
+ dict := pr.objDictIdx(sym, idx, implicits, explicits, false)
+
+ return pr.dictNameOf(dict)
+}
+
+// curry returns a function literal that calls fun with arg0 and
+// (optionally) arg1, accepting additional arguments to the function
+// literal as necessary to satisfy fun's signature.
+//
+// If nilCheck is true and arg0 is an interface value, then it's
+// checked to be non-nil as an initial step at the point of evaluating
+// the function literal itself.
+func (r *reader) curry(origPos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir.Node) ir.Node {
+ var captured ir.Nodes
+ captured.Append(fun, arg0)
+ if arg1 != nil {
+ captured.Append(arg1)
+ }
+
+ params, results := syntheticSig(fun.Type())
+ params = params[len(captured)-1:] // skip curried parameters
+ typ := types.NewSignature(types.NoPkg, nil, nil, params, results)
+
+ addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
+ recvs, params := r.syntheticArgs(pos)
+ assert(len(recvs) == 0)
+
+ fun := captured[0]
+
+ var args ir.Nodes
+ args.Append(captured[1:]...)
+ args.Append(params...)
+
+ r.syntheticTailCall(pos, fun, args)
+ }
+
+ return r.syntheticClosure(origPos, typ, ifaceHack, captured, addBody)
+}
+
+// methodExprWrap returns a function literal that changes method's
+// first parameter's type to recv, and uses implicits/deref/addr to
+// select the appropriate receiver parameter to pass to method.
+func (r *reader) methodExprWrap(origPos src.XPos, recv *types.Type, implicits []int, deref, addr bool, method, dictPtr ir.Node) ir.Node {
+ var captured ir.Nodes
+ captured.Append(method)
+
+ params, results := syntheticSig(method.Type())
+
+ // Change first parameter to recv.
+ params[0].Type = recv
+
+ // If we have a dictionary pointer argument to pass, then omit the
+ // underlying method expression's dictionary parameter from the
+ // returned signature too.
+ if dictPtr != nil {
+ captured.Append(dictPtr)
+ params = append(params[:1], params[2:]...)
+ }
+
+ typ := types.NewSignature(types.NoPkg, nil, nil, params, results)
+
+ addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
+ recvs, args := r.syntheticArgs(pos)
+ assert(len(recvs) == 0)
+
+ fn := captured[0]
+
+ // Rewrite first argument based on implicits/deref/addr.
+ {
+ arg := args[0]
+ for _, ix := range implicits {
+ arg = Implicit(DotField(pos, arg, ix))
+ }
+ if deref {
+ arg = Implicit(Deref(pos, arg.Type().Elem(), arg))
+ } else if addr {
+ arg = Implicit(Addr(pos, arg))
+ }
+ args[0] = arg
+ }
+
+ // Insert dictionary argument, if provided.
+ if dictPtr != nil {
+ newArgs := make([]ir.Node, len(args)+1)
+ newArgs[0] = args[0]
+ newArgs[1] = captured[1]
+ copy(newArgs[2:], args[1:])
+ args = newArgs
+ }
+
+ r.syntheticTailCall(pos, fn, args)
+ }
+
+ return r.syntheticClosure(origPos, typ, false, captured, addBody)
+}
+
+// syntheticClosure constructs a synthetic function literal for
+// currying dictionary arguments. origPos is the position used for the
+// closure, which must be a non-inlined position. typ is the function
+// literal's signature type.
+//
+// captures is a list of expressions that need to be evaluated at the
+// point of function literal evaluation and captured by the function
+// literal. If ifaceHack is true and captures[1] is an interface type,
+// it's checked to be non-nil after evaluation.
+//
+// addBody is a callback function to populate the function body. The
+// list of captured values passed back has the captured variables for
+// use within the function literal, corresponding to the expressions
+// in captures.
+func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack bool, captures ir.Nodes, addBody func(pos src.XPos, r *reader, captured []ir.Node)) ir.Node {
+ // isSafe reports whether n is an expression that we can safely
+ // defer to evaluating inside the closure instead, to avoid storing
+ // them into the closure.
+ //
+ // In practice this is always (and only) the wrappee function.
+ isSafe := func(n ir.Node) bool {
+ if n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PFUNC {
+ return true
+ }
+ if n.Op() == ir.OMETHEXPR {
+ return true
+ }
+
+ return false
+ }
+
+ // The ODCLFUNC and its body need to use the original position, but
+ // the OCLOSURE node and any Init statements should use the inlined
+ // position instead. See also the explanation in reader.funcLit.
+ inlPos := r.inlPos(origPos)
+
+ fn := ir.NewClosureFunc(origPos, r.curfn != nil)
+ fn.SetWrapper(true)
+ clo := fn.OClosure
+ clo.SetPos(inlPos)
+ ir.NameClosure(clo, r.curfn)
+
+ setType(fn.Nname, typ)
+ typecheck.Func(fn)
+ setType(clo, fn.Type())
+
+ var init ir.Nodes
+ for i, n := range captures {
+ if isSafe(n) {
+ continue // skip capture; can reference directly
+ }
+
+ tmp := r.tempCopy(inlPos, n, &init)
+ ir.NewClosureVar(origPos, fn, tmp)
+
+ // We need to nil check interface receivers at the point of method
+ // value evaluation, ugh.
+ if ifaceHack && i == 1 && n.Type().IsInterface() {
+ check := ir.NewUnaryExpr(inlPos, ir.OCHECKNIL, ir.NewUnaryExpr(inlPos, ir.OITAB, tmp))
+ init.Append(typecheck.Stmt(check))
+ }
+ }
+
+ pri := pkgReaderIndex{synthetic: func(pos src.XPos, r *reader) {
+ captured := make([]ir.Node, len(captures))
+ next := 0
+ for i, n := range captures {
+ if isSafe(n) {
+ captured[i] = n
+ } else {
+ captured[i] = r.closureVars[next]
+ next++
+ }
+ }
+ assert(next == len(r.closureVars))
+
+ addBody(origPos, r, captured)
+ }}
+ bodyReader[fn] = pri
+ pri.funcBody(fn)
+
+ // TODO(mdempsky): Remove hard-coding of typecheck.Target.
+ return ir.InitExpr(init, ir.UseClosure(clo, typecheck.Target))
+}
+
+// syntheticSig duplicates and returns the params and results lists
+// for sig, but renaming anonymous parameters so they can be assigned
+// ir.Names.
+func syntheticSig(sig *types.Type) (params, results []*types.Field) {
+ clone := func(params []*types.Field) []*types.Field {
+ res := make([]*types.Field, len(params))
+ for i, param := range params {
+ sym := param.Sym
+ if sym == nil || sym.Name == "_" {
+ sym = typecheck.LookupNum(".anon", i)
+ }
+ // TODO(mdempsky): It would be nice to preserve the original
+ // parameter positions here instead, but at least
+ // typecheck.NewMethodType replaces them with base.Pos, making
+ // them useless. Worse, the positions copied from base.Pos may
+ // have inlining contexts, which we definitely don't want here
+ // (e.g., #54625).
+ res[i] = types.NewField(base.AutogeneratedPos, sym, param.Type)
+ res[i].SetIsDDD(param.IsDDD())
+ }
+ return res
+ }
+
+ return clone(sig.Params().FieldSlice()), clone(sig.Results().FieldSlice())
+}
+
+func (r *reader) optExpr() ir.Node {
+ if r.Bool() {
+ return r.expr()
+ }
+ return nil
+}
+
+// methodExpr reads a method expression reference, and returns three
+// (possibly nil) expressions related to it:
+//
+// baseFn is always non-nil: it's either a function of the appropriate
+// type already, or it has an extra dictionary parameter as the second
+// parameter (i.e., immediately after the promoted receiver
+// parameter).
+//
+// If dictPtr is non-nil, then it's a dictionary argument that must be
+// passed as the second argument to baseFn.
+//
+// If wrapperFn is non-nil, then it's either the same as baseFn (if
+// dictPtr is nil), or it's semantically equivalent to currying baseFn
+// to pass dictPtr. (wrapperFn is nil when dictPtr is an expression
+// that needs to be computed dynamically.)
+//
+// For callers that are creating a call to the returned method, it's
+// best to emit a call to baseFn, and include dictPtr in the arguments
+// list as appropriate.
+//
+// For callers that want to return a method expression without
+// invoking it, they may return wrapperFn if it's non-nil; but
+// otherwise, they need to create their own wrapper.
+func (r *reader) methodExpr() (wrapperFn, baseFn, dictPtr ir.Node) {
+ recv := r.typ()
+ sig0 := r.typ()
+ pos := r.pos()
+ _, sym := r.selector()
+
+ // Signature type to return (i.e., recv prepended to the method's
+ // normal parameters list).
+ sig := typecheck.NewMethodType(sig0, recv)
+
+ if r.Bool() { // type parameter method expression
+ idx := r.Len()
+ word := r.dictWord(pos, r.dict.typeParamMethodExprsOffset()+idx)
+
+ // TODO(mdempsky): If the type parameter was instantiated with an
+ // interface type (i.e., embed.IsInterface()), then we could
+ // return the OMETHEXPR instead and save an indirection.
+
+ // We wrote the method expression's entry point PC into the
+ // dictionary, but for Go `func` values we need to return a
+ // closure (i.e., pointer to a structure with the PC as the first
+ // field). Because method expressions don't have any closure
+ // variables, we pun the dictionary entry as the closure struct.
+ fn := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, sig, ir.NewAddrExpr(pos, word)))
+ return fn, fn, nil
+ }
+
+ // TODO(mdempsky): I'm pretty sure this isn't needed: implicits is
+ // only relevant to locally defined types, but they can't have
+ // (non-promoted) methods.
+ var implicits []*types.Type
+ if r.dict != nil {
+ implicits = r.dict.targs
+ }
+
+ if r.Bool() { // dynamic subdictionary
+ idx := r.Len()
+ info := r.dict.subdicts[idx]
+ explicits := r.p.typListIdx(info.explicits, r.dict)
+
+ shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
+ shapedFn := shapedMethodExpr(pos, shapedObj, sym)
+
+ // TODO(mdempsky): Is there a more robust way to get the
+ // dictionary pointer type here?
+ dictPtrType := shapedFn.Type().Params().Field(1).Type
+ dictPtr := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx)))
+
+ return nil, shapedFn, dictPtr
+ }
+
+ if r.Bool() { // static dictionary
+ info := r.objInfo()
+ explicits := r.p.typListIdx(info.explicits, r.dict)
+
+ shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
+ shapedFn := shapedMethodExpr(pos, shapedObj, sym)
+
+ dict := r.p.objDictName(info.idx, implicits, explicits)
+ dictPtr := typecheck.Expr(ir.NewAddrExpr(pos, dict))
+
+ // Check that dictPtr matches shapedFn's dictionary parameter.
+ if !types.Identical(dictPtr.Type(), shapedFn.Type().Params().Field(1).Type) {
+ base.FatalfAt(pos, "dict %L, but shaped method %L", dict, shapedFn)
+ }
+
+ // For statically known instantiations, we can take advantage of
+ // the stenciled wrapper.
+ base.AssertfAt(!recv.HasShape(), pos, "shaped receiver %v", recv)
+ wrapperFn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
+ base.AssertfAt(types.Identical(sig, wrapperFn.Type()), pos, "wrapper %L does not have type %v", wrapperFn, sig)
+
+ return wrapperFn, shapedFn, dictPtr
+ }
+
+ // Simple method expression; no dictionary needed.
+ base.AssertfAt(!recv.HasShape() || recv.IsInterface(), pos, "shaped receiver %v", recv)
+ fn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
+ return fn, fn, nil
+}
+
+// shapedMethodExpr returns the specified method on the given shaped
+// type.
+func shapedMethodExpr(pos src.XPos, obj *ir.Name, sym *types.Sym) *ir.SelectorExpr {
+ assert(obj.Op() == ir.OTYPE)
+
+ typ := obj.Type()
+ assert(typ.HasShape())
+
+ method := func() *types.Field {
+ for _, method := range typ.Methods().Slice() {
+ if method.Sym == sym {
+ return method
+ }
+ }
+
+ base.FatalfAt(pos, "failed to find method %v in shaped type %v", sym, typ)
+ panic("unreachable")
+ }()
+
+ // Construct an OMETHEXPR node.
+ recv := method.Type.Recv().Type
+ return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr)
+}
+
+func (r *reader) multiExpr() []ir.Node {
+ r.Sync(pkgbits.SyncMultiExpr)
+
+ if r.Bool() { // N:1
+ pos := r.pos()
+ expr := r.expr()
+
+ results := make([]ir.Node, r.Len())
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr})
+ as.Def = true
+ for i := range results {
+ tmp := r.temp(pos, r.typ())
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ as.Lhs.Append(tmp)
+
+ res := ir.Node(tmp)
+ if r.Bool() {
+ n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res)
+ n.TypeWord, n.SrcRType = r.convRTTI(pos)
+ n.SetImplicit(true)
+ res = typecheck.Expr(n)
+ }
+ results[i] = res
+ }
+
+ // TODO(mdempsky): Could use ir.InlinedCallExpr instead?
+ results[0] = ir.InitExpr([]ir.Node{typecheck.Stmt(as)}, results[0])
+ return results
+ }
+
+ // N:N
+ exprs := make([]ir.Node, r.Len())
+ if len(exprs) == 0 {
+ return nil
+ }
+ for i := range exprs {
+ exprs[i] = r.expr()
+ }
+ return exprs
+}
+
+// temp returns a new autotemp of the specified type.
+func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name {
+ // See typecheck.typecheckargs.
+ curfn := r.curfn
+ if curfn == nil {
+ curfn = typecheck.InitTodoFunc
+ }
+
+ return typecheck.TempAt(pos, curfn, typ)
+}
+
+// tempCopy declares and returns a new autotemp initialized to the
+// value of expr.
+func (r *reader) tempCopy(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
+ if r.curfn == nil {
+ // Escape analysis doesn't know how to handle package-scope
+ // function literals with free variables (i.e., that capture
+ // temporary variables added to typecheck.InitTodoFunc).
+ //
+ // stencil.go works around this limitation by spilling values to
+ // global variables instead, but that causes the value to stay
+ // alive indefinitely; see go.dev/issue/54343.
+ //
+ // This code path (which implements the same workaround) isn't
+ // actually needed by unified IR, because it creates uses normal
+ // OMETHEXPR/OMETHVALUE nodes when statically-known instantiated
+ // types are used. But it's kept around for now because it's handy
+ // for testing that the generic fallback paths work correctly.
+ base.Fatalf("tempCopy called at package scope")
+
+ tmp := staticinit.StaticName(expr.Type())
+
+ assign := ir.NewAssignStmt(pos, tmp, expr)
+ assign.Def = true
+ tmp.Defn = assign
+
+ typecheck.Target.Decls = append(typecheck.Target.Decls, typecheck.Stmt(assign))
+
+ return tmp
+ }
+
+ tmp := r.temp(pos, expr.Type())
+
+ init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
+
+ assign := ir.NewAssignStmt(pos, tmp, expr)
+ assign.Def = true
+ init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, expr)))
+
+ tmp.Defn = assign
+
+ return tmp
+}
+
+func (r *reader) compLit() ir.Node {
+ r.Sync(pkgbits.SyncCompLit)
+ pos := r.pos()
+ typ0 := r.typ()
+
+ typ := typ0
+ if typ.IsPtr() {
+ typ = typ.Elem()
+ }
+ if typ.Kind() == types.TFORW {
+ base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
+ }
+ var rtype ir.Node
+ if typ.IsMap() {
+ rtype = r.rtype(pos)
+ }
+ isStruct := typ.Kind() == types.TSTRUCT
+
+ elems := make([]ir.Node, r.Len())
+ for i := range elems {
+ elemp := &elems[i]
+
+ if isStruct {
+ sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil)
+ *elemp, elemp = sk, &sk.Value
+ } else if r.Bool() {
+ kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
+ *elemp, elemp = kv, &kv.Value
+ }
+
+ *elemp = wrapName(r.pos(), r.expr())
+ }
+
+ lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
+ if rtype != nil {
+ lit := lit.(*ir.CompLitExpr)
+ lit.RType = rtype
+ }
+ if typ0.IsPtr() {
+ lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
+ lit.SetType(typ0)
+ }
+ return lit
+}
+
+func wrapName(pos src.XPos, x ir.Node) ir.Node {
+ // These nodes do not carry line numbers.
+ // Introduce a wrapper node to give them the correct line.
+ switch ir.Orig(x).Op() {
+ case ir.OTYPE, ir.OLITERAL:
+ if x.Sym() == nil {
+ break
+ }
+ fallthrough
+ case ir.ONAME, ir.ONONAME, ir.ONIL:
+ p := ir.NewParenExpr(pos, x)
+ p.SetImplicit(true)
+ return p
+ }
+ return x
+}
+
+func (r *reader) funcLit() ir.Node {
+ r.Sync(pkgbits.SyncFuncLit)
+
+ // The underlying function declaration (including its parameters'
+ // positions, if any) need to remain the original, uninlined
+ // positions. This is because we track inlining-context on nodes so
+ // we can synthesize the extra implied stack frames dynamically when
+ // generating tracebacks, whereas those stack frames don't make
+ // sense *within* the function literal. (Any necessary inlining
+ // adjustments will have been applied to the call expression
+ // instead.)
+ //
+ // This is subtle, and getting it wrong leads to cycles in the
+ // inlining tree, which lead to infinite loops during stack
+ // unwinding (#46234, #54625).
+ //
+ // Note that we *do* want the inline-adjusted position for the
+ // OCLOSURE node, because that position represents where any heap
+ // allocation of the closure is credited (#49171).
+ r.suppressInlPos++
+ pos := r.pos()
+ xtype2 := r.signature(types.LocalPkg, nil)
+ r.suppressInlPos--
+
+ fn := ir.NewClosureFunc(pos, r.curfn != nil)
+ clo := fn.OClosure
+ clo.SetPos(r.inlPos(pos)) // see comment above
+ ir.NameClosure(clo, r.curfn)
+
+ setType(fn.Nname, xtype2)
+ typecheck.Func(fn)
+ setType(clo, fn.Type())
+
+ fn.ClosureVars = make([]*ir.Name, 0, r.Len())
+ for len(fn.ClosureVars) < cap(fn.ClosureVars) {
+ ir.NewClosureVar(r.pos(), fn, r.useLocal())
+ }
+ if param := r.dictParam; param != nil {
+ // If we have a dictionary parameter, capture it too. For
+ // simplicity, we capture it last and unconditionally.
+ ir.NewClosureVar(param.Pos(), fn, param)
+ }
+
+ r.addBody(fn, nil)
+
+ // TODO(mdempsky): Remove hard-coding of typecheck.Target.
+ return ir.UseClosure(clo, typecheck.Target)
+}
+
+func (r *reader) exprList() []ir.Node {
+ r.Sync(pkgbits.SyncExprList)
+ return r.exprs()
+}
+
+func (r *reader) exprs() []ir.Node {
+ r.Sync(pkgbits.SyncExprs)
+ nodes := make([]ir.Node, r.Len())
+ if len(nodes) == 0 {
+ return nil // TODO(mdempsky): Unclear if this matters.
+ }
+ for i := range nodes {
+ nodes[i] = r.expr()
+ }
+ return nodes
+}
+
+// dictWord returns an expression to return the specified
+// uintptr-typed word from the dictionary parameter.
+func (r *reader) dictWord(pos src.XPos, idx int) ir.Node {
+ base.AssertfAt(r.dictParam != nil, pos, "expected dictParam in %v", r.curfn)
+ return typecheck.Expr(ir.NewIndexExpr(pos, r.dictParam, ir.NewBasicLit(pos, constant.MakeInt64(int64(idx)))))
+}
+
+// rttiWord is like dictWord, but converts it to *byte (the type used
+// internally to represent *runtime._type and *runtime.itab).
+func (r *reader) rttiWord(pos src.XPos, idx int) ir.Node {
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TUINT8]), r.dictWord(pos, idx)))
+}
+
+// rtype reads a type reference from the element bitstream, and
+// returns an expression of type *runtime._type representing that
+// type.
+func (r *reader) rtype(pos src.XPos) ir.Node {
+ _, rtype := r.rtype0(pos)
+ return rtype
+}
+
+func (r *reader) rtype0(pos src.XPos) (typ *types.Type, rtype ir.Node) {
+ r.Sync(pkgbits.SyncRType)
+ if r.Bool() { // derived type
+ idx := r.Len()
+ info := r.dict.rtypes[idx]
+ typ = r.p.typIdx(info, r.dict, true)
+ rtype = r.rttiWord(pos, r.dict.rtypesOffset()+idx)
+ return
+ }
+
+ typ = r.typ()
+ rtype = reflectdata.TypePtrAt(pos, typ)
+ return
+}
+
+// varDictIndex populates name.DictIndex if name is a derived type.
+func (r *reader) varDictIndex(name *ir.Name) {
+ if r.Bool() {
+ idx := 1 + r.dict.rtypesOffset() + r.Len()
+ if int(uint16(idx)) != idx {
+ base.FatalfAt(name.Pos(), "DictIndex overflow for %v: %v", name, idx)
+ }
+ name.DictIndex = uint16(idx)
+ }
+}
+
+// itab returns a (typ, iface) pair of types.
+//
+// typRType and ifaceRType are expressions that evaluate to the
+// *runtime._type for typ and iface, respectively.
+//
+// If typ is a concrete type and iface is a non-empty interface type,
+// then itab is an expression that evaluates to the *runtime.itab for
+// the pair. Otherwise, itab is nil.
+func (r *reader) itab(pos src.XPos) (typ *types.Type, typRType ir.Node, iface *types.Type, ifaceRType ir.Node, itab ir.Node) {
+ typ, typRType = r.rtype0(pos)
+ iface, ifaceRType = r.rtype0(pos)
+
+ idx := -1
+ if r.Bool() {
+ idx = r.Len()
+ }
+
+ if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() {
+ if idx >= 0 {
+ itab = r.rttiWord(pos, r.dict.itabsOffset()+idx)
+ } else {
+ base.AssertfAt(!typ.HasShape(), pos, "%v is a shape type", typ)
+ base.AssertfAt(!iface.HasShape(), pos, "%v is a shape type", iface)
+
+ lsym := reflectdata.ITabLsym(typ, iface)
+ itab = typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
+ }
+ }
+
+ return
+}
+
+// convRTTI returns expressions appropriate for populating an
+// ir.ConvExpr's TypeWord and SrcRType fields, respectively.
+func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) {
+ r.Sync(pkgbits.SyncConvRTTI)
+ src, srcRType0, dst, dstRType, itab := r.itab(pos)
+ if !dst.IsInterface() {
+ return
+ }
+
+ // See reflectdata.ConvIfaceTypeWord.
+ switch {
+ case dst.IsEmptyInterface():
+ if !src.IsInterface() {
+ typeWord = srcRType0 // direct eface construction
+ }
+ case !src.IsInterface():
+ typeWord = itab // direct iface construction
+ default:
+ typeWord = dstRType // convI2I
+ }
+
+ // See reflectdata.ConvIfaceSrcRType.
+ if !src.IsInterface() {
+ srcRType = srcRType0
+ }
+
+ return
+}
+
+func (r *reader) exprType() ir.Node {
+ r.Sync(pkgbits.SyncExprType)
+ pos := r.pos()
+
+ var typ *types.Type
+ var rtype, itab ir.Node
+
+ if r.Bool() {
+ typ, rtype, _, _, itab = r.itab(pos)
+ if !typ.IsInterface() {
+ rtype = nil // TODO(mdempsky): Leave set?
+ }
+ } else {
+ typ, rtype = r.rtype0(pos)
+
+ if !r.Bool() { // not derived
+ // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node.
+ n := ir.TypeNode(typ)
+ n.SetTypecheck(1)
+ return n
+ }
+ }
+
+ dt := ir.NewDynamicType(pos, rtype)
+ dt.ITab = itab
+ return typed(typ, dt)
+}
+
+func (r *reader) op() ir.Op {
+ r.Sync(pkgbits.SyncOp)
+ return ir.Op(r.Len())
+}
+
+// @@@ Package initialization
+
+func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
+ cgoPragmas := make([][]string, r.Len())
+ for i := range cgoPragmas {
+ cgoPragmas[i] = r.Strings()
+ }
+ target.CgoPragmas = cgoPragmas
+
+ r.pkgDecls(target)
+
+ r.Sync(pkgbits.SyncEOF)
+}
+
+func (r *reader) pkgDecls(target *ir.Package) {
+ r.Sync(pkgbits.SyncDecls)
+ for {
+ switch code := codeDecl(r.Code(pkgbits.SyncDecl)); code {
+ default:
+ panic(fmt.Sprintf("unhandled decl: %v", code))
+
+ case declEnd:
+ return
+
+ case declFunc:
+ names := r.pkgObjs(target)
+ assert(len(names) == 1)
+ target.Decls = append(target.Decls, names[0].Func)
+
+ case declMethod:
+ typ := r.typ()
+ _, sym := r.selector()
+
+ method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0)
+ target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func)
+
+ case declVar:
+ pos := r.pos()
+ names := r.pkgObjs(target)
+ values := r.exprList()
+
+ if len(names) > 1 && len(values) == 1 {
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values)
+ for _, name := range names {
+ as.Lhs.Append(name)
+ name.Defn = as
+ }
+ target.Decls = append(target.Decls, as)
+ } else {
+ for i, name := range names {
+ as := ir.NewAssignStmt(pos, name, nil)
+ if i < len(values) {
+ as.Y = values[i]
+ }
+ name.Defn = as
+ target.Decls = append(target.Decls, as)
+ }
+ }
+
+ if n := r.Len(); n > 0 {
+ assert(len(names) == 1)
+ embeds := make([]ir.Embed, n)
+ for i := range embeds {
+ embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.Strings()}
+ }
+ names[0].Embed = &embeds
+ target.Embeds = append(target.Embeds, names[0])
+ }
+
+ case declOther:
+ r.pkgObjs(target)
+ }
+ }
+}
+
+func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
+ r.Sync(pkgbits.SyncDeclNames)
+ nodes := make([]*ir.Name, r.Len())
+ for i := range nodes {
+ r.Sync(pkgbits.SyncDeclName)
+
+ name := r.obj().(*ir.Name)
+ nodes[i] = name
+
+ sym := name.Sym()
+ if sym.IsBlank() {
+ continue
+ }
+
+ switch name.Class {
+ default:
+ base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class)
+
+ case ir.PEXTERN:
+ target.Externs = append(target.Externs, name)
+
+ case ir.PFUNC:
+ assert(name.Type().Recv() == nil)
+
+ // TODO(mdempsky): Cleaner way to recognize init?
+ if strings.HasPrefix(sym.Name, "init.") {
+ target.Inits = append(target.Inits, name.Func)
+ }
+ }
+
+ if types.IsExported(sym.Name) {
+ assert(!sym.OnExportList())
+ target.Exports = append(target.Exports, name)
+ sym.SetOnExportList(true)
+ }
+
+ if base.Flag.AsmHdr != "" {
+ assert(!sym.Asm())
+ target.Asms = append(target.Asms, name)
+ sym.SetAsm(true)
+ }
+ }
+
+ return nodes
+}
+
+// @@@ Inlining
+
+// unifiedHaveInlineBody reports whether we have the function body for
+// fn, so we can inline it.
+func unifiedHaveInlineBody(fn *ir.Func) bool {
+ if fn.Inl == nil {
+ return false
+ }
+
+ _, ok := bodyReaderFor(fn)
+ return ok
+}
+
+var inlgen = 0
+
+// unifiedInlineCall implements inline.NewInline by re-reading the function
+// body from its Unified IR export data.
+func unifiedInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
+ // TODO(mdempsky): Turn callerfn into an explicit parameter.
+ callerfn := ir.CurFunc
+
+ pri, ok := bodyReaderFor(fn)
+ if !ok {
+ base.FatalfAt(call.Pos(), "cannot inline call to %v: missing inline body", fn)
+ }
+
+ if fn.Inl.Body == nil {
+ expandInline(fn, pri)
+ }
+
+ r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+
+ // TODO(mdempsky): This still feels clumsy. Can we do better?
+ tmpfn := ir.NewFunc(fn.Pos())
+ tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym())
+ tmpfn.Closgen = callerfn.Closgen
+ defer func() { callerfn.Closgen = tmpfn.Closgen }()
+
+ setType(tmpfn.Nname, fn.Type())
+ r.curfn = tmpfn
+
+ r.inlCaller = callerfn
+ r.inlCall = call
+ r.inlFunc = fn
+ r.inlTreeIndex = inlIndex
+ r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
+
+ r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
+ for i, cv := range r.inlFunc.ClosureVars {
+ r.closureVars[i] = cv.Outer
+ }
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
+
+ r.funcargs(fn)
+
+ r.delayResults = fn.Inl.CanDelayResults
+
+ r.retlabel = typecheck.AutoLabel(".i")
+ inlgen++
+
+ init := ir.TakeInit(call)
+
+ // 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 {
+ inline.CalleeEffects(&init, call.X)
+ }
+
+ var args ir.Nodes
+ if call.Op() == ir.OCALLMETH {
+ base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
+ }
+ args.Append(call.Args...)
+
+ // Create assignment to declare and initialize inlvars.
+ as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args)
+ as2.Def = true
+ var as2init ir.Nodes
+ for _, name := range r.inlvars {
+ if ir.IsBlank(name) {
+ continue
+ }
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
+ name.Defn = as2
+ }
+ as2.SetInit(as2init)
+ init.Append(typecheck.Stmt(as2))
+
+ if !r.delayResults {
+ // If not delaying retvars, declare and zero initialize the
+ // result variables now.
+ for _, name := range r.retvars {
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
+ ras := ir.NewAssignStmt(call.Pos(), name, nil)
+ init.Append(typecheck.Stmt(ras))
+ }
+ }
+
+ // 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.
+ init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex)))
+
+ nparams := len(r.curfn.Dcl)
+
+ ir.WithFunc(r.curfn, func() {
+ if !r.syntheticBody(call.Pos()) {
+ assert(r.Bool()) // have body
+
+ r.curfn.Body = r.stmts()
+ r.curfn.Endlineno = r.pos()
+ }
+
+ // TODO(mdempsky): This shouldn't be necessary. Inlining might
+ // read in new function/method declarations, which could
+ // potentially be recursively inlined themselves; but we shouldn't
+ // need to read in the non-inlined bodies for the declarations
+ // themselves. But currently it's an easy fix to #50552.
+ readBodies(typecheck.Target, true)
+
+ deadcode.Func(r.curfn)
+
+ // Replace any "return" statements within the function body.
+ var edit func(ir.Node) ir.Node
+ edit = func(n ir.Node) ir.Node {
+ if ret, ok := n.(*ir.ReturnStmt); ok {
+ n = typecheck.Stmt(r.inlReturn(ret))
+ }
+ ir.EditChildren(n, edit)
+ return n
+ }
+ edit(r.curfn)
+ })
+
+ body := ir.Nodes(r.curfn.Body)
+
+ // Quirkish: We need to eagerly prune variables added during
+ // inlining, but removed by deadcode.FuncBody above. Unused
+ // variables will get removed during stack frame layout anyway, but
+ // len(fn.Dcl) ends up influencing things like autotmp naming.
+
+ used := usedLocals(body)
+
+ for i, name := range r.curfn.Dcl {
+ if i < nparams || used.Has(name) {
+ name.Curfn = callerfn
+ callerfn.Dcl = append(callerfn.Dcl, name)
+
+ // Quirkish. TODO(mdempsky): Document why.
+ if name.AutoTemp() {
+ name.SetEsc(ir.EscUnknown)
+
+ if base.Flag.GenDwarfInl != 0 {
+ name.SetInlLocal(true)
+ } else {
+ name.SetPos(r.inlCall.Pos())
+ }
+ }
+ }
+ }
+
+ body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
+
+ res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...))
+ res.SetInit(init)
+ res.SetType(call.Type())
+ res.SetTypecheck(1)
+
+ // Inlining shouldn't add any functions to todoBodies.
+ assert(len(todoBodies) == 0)
+
+ return res
+}
+
+// inlReturn returns a statement that can substitute for the given
+// return statement when inlining.
+func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt {
+ pos := r.inlCall.Pos()
+
+ block := ir.TakeInit(ret)
+
+ if results := ret.Results; len(results) != 0 {
+ assert(len(r.retvars) == len(results))
+
+ as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results)
+
+ if r.delayResults {
+ for _, name := range r.retvars {
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ block.Append(ir.NewDecl(pos, ir.ODCL, name))
+ name.Defn = as2
+ }
+ }
+
+ block.Append(as2)
+ }
+
+ block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel))
+ return ir.NewBlockStmt(pos, block)
+}
+
+// expandInline reads in an extra copy of IR to populate
+// fn.Inl.{Dcl,Body}.
+func expandInline(fn *ir.Func, pri pkgReaderIndex) {
+ // TODO(mdempsky): Remove this function. It's currently needed by
+ // dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to
+ // create abstract function DIEs. But we should be able to provide it
+ // with the same information some other way.
+
+ fndcls := len(fn.Dcl)
+ topdcls := len(typecheck.Target.Decls)
+
+ tmpfn := ir.NewFunc(fn.Pos())
+ tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym())
+ tmpfn.ClosureVars = fn.ClosureVars
+
+ {
+ r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ setType(tmpfn.Nname, fn.Type())
+
+ // Don't change parameter's Sym/Nname fields.
+ r.funarghack = true
+
+ r.funcBody(tmpfn)
+
+ ir.WithFunc(tmpfn, func() {
+ deadcode.Func(tmpfn)
+ })
+ }
+
+ used := usedLocals(tmpfn.Body)
+
+ for _, name := range tmpfn.Dcl {
+ if name.Class != ir.PAUTO || used.Has(name) {
+ name.Curfn = fn
+ fn.Inl.Dcl = append(fn.Inl.Dcl, name)
+ }
+ }
+ fn.Inl.Body = tmpfn.Body
+
+ // Double check that we didn't change fn.Dcl by accident.
+ assert(fndcls == len(fn.Dcl))
+
+ // typecheck.Stmts may have added function literals to
+ // typecheck.Target.Decls. Remove them again so we don't risk trying
+ // to compile them multiple times.
+ typecheck.Target.Decls = typecheck.Target.Decls[:topdcls]
+}
+
+// usedLocals returns a set of local variables that are used within body.
+func usedLocals(body []ir.Node) ir.NameSet {
+ var used ir.NameSet
+ ir.VisitList(body, func(n ir.Node) {
+ if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
+ used.Add(n)
+ }
+ })
+ return used
+}
+
+// @@@ Method wrappers
+
+// needWrapperTypes lists types for which we may need to generate
+// method wrappers.
+var needWrapperTypes []*types.Type
+
+// haveWrapperTypes lists types for which we know we already have
+// method wrappers, because we found the type in an imported package.
+var haveWrapperTypes []*types.Type
+
+// needMethodValueWrappers lists methods for which we may need to
+// generate method value wrappers.
+var needMethodValueWrappers []methodValueWrapper
+
+// haveMethodValueWrappers lists methods for which we know we already
+// have method value wrappers, because we found it in an imported
+// package.
+var haveMethodValueWrappers []methodValueWrapper
+
+type methodValueWrapper struct {
+ rcvr *types.Type
+ method *types.Field
+}
+
+func (r *reader) needWrapper(typ *types.Type) {
+ if typ.IsPtr() {
+ return
+ }
+
+ // If a type was found in an imported package, then we can assume
+ // that package (or one of its transitive dependencies) already
+ // generated method wrappers for it.
+ if r.importedDef() {
+ haveWrapperTypes = append(haveWrapperTypes, typ)
+ } else {
+ needWrapperTypes = append(needWrapperTypes, typ)
+ }
+}
+
+// importedDef reports whether r is reading from an imported and
+// non-generic element.
+//
+// If a type was found in an imported package, then we can assume that
+// package (or one of its transitive dependencies) already generated
+// method wrappers for it.
+//
+// Exception: If we're instantiating an imported generic type or
+// function, we might be instantiating it with type arguments not
+// previously seen before.
+//
+// TODO(mdempsky): Distinguish when a generic function or type was
+// instantiated in an imported package so that we can add types to
+// haveWrapperTypes instead.
+func (r *reader) importedDef() bool {
+ return r.p != localPkgReader && !r.hasTypeParams()
+}
+
+func MakeWrappers(target *ir.Package) {
+ // Only unified IR emits its own wrappers.
+ if base.Debug.Unified == 0 {
+ return
+ }
+
+ // always generate a wrapper for error.Error (#29304)
+ needWrapperTypes = append(needWrapperTypes, types.ErrorType)
+
+ seen := make(map[string]*types.Type)
+
+ for _, typ := range haveWrapperTypes {
+ wrapType(typ, target, seen, false)
+ }
+ haveWrapperTypes = nil
+
+ for _, typ := range needWrapperTypes {
+ wrapType(typ, target, seen, true)
+ }
+ needWrapperTypes = nil
+
+ for _, wrapper := range haveMethodValueWrappers {
+ wrapMethodValue(wrapper.rcvr, wrapper.method, target, false)
+ }
+ haveMethodValueWrappers = nil
+
+ for _, wrapper := range needMethodValueWrappers {
+ wrapMethodValue(wrapper.rcvr, wrapper.method, target, true)
+ }
+ needMethodValueWrappers = nil
+}
+
+func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) {
+ key := typ.LinkString()
+ if prev := seen[key]; prev != nil {
+ if !types.Identical(typ, prev) {
+ base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key)
+ }
+ return
+ }
+ seen[key] = typ
+
+ if !needed {
+ // Only called to add to 'seen'.
+ return
+ }
+
+ if !typ.IsInterface() {
+ typecheck.CalcMethods(typ)
+ }
+ for _, meth := range typ.AllMethods().Slice() {
+ if meth.Sym.IsBlank() || !meth.IsMethod() {
+ base.FatalfAt(meth.Pos, "invalid method: %v", meth)
+ }
+
+ methodWrapper(0, typ, meth, target)
+
+ // For non-interface types, we also want *T wrappers.
+ if !typ.IsInterface() {
+ methodWrapper(1, typ, meth, target)
+
+ // For not-in-heap types, *T is a scalar, not pointer shaped,
+ // so the interface wrappers use **T.
+ if typ.NotInHeap() {
+ methodWrapper(2, typ, meth, target)
+ }
+ }
+ }
+}
+
+func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
+ wrapper := tbase
+ for i := 0; i < derefs; i++ {
+ wrapper = types.NewPtr(wrapper)
+ }
+
+ sym := ir.MethodSym(wrapper, method.Sym)
+ base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
+ sym.SetSiggen(true)
+
+ wrappee := method.Type.Recv().Type
+ if types.Identical(wrapper, wrappee) ||
+ !types.IsMethodApplicable(wrapper, method) ||
+ !reflectdata.NeedEmit(tbase) {
+ return
+ }
+
+ // TODO(mdempsky): Use method.Pos instead?
+ pos := base.AutogeneratedPos
+
+ fn := newWrapperFunc(pos, sym, wrapper, method)
+
+ var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
+
+ // For simple *T wrappers around T methods, panicwrap produces a
+ // nicer panic message.
+ if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
+ cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
+ then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
+ fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
+ }
+
+ // typecheck will add one implicit deref, if necessary,
+ // but not-in-heap types require more for their **T wrappers.
+ for i := 1; i < derefs; i++ {
+ recv = Implicit(ir.NewStarExpr(pos, recv))
+ }
+
+ addTailCall(pos, fn, recv, method)
+
+ finishWrapperFunc(fn, target)
+}
+
+func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
+ sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
+ if sym.Uniq() {
+ return
+ }
+ sym.SetUniq(true)
+
+ // TODO(mdempsky): Use method.Pos instead?
+ pos := base.AutogeneratedPos
+
+ fn := newWrapperFunc(pos, sym, nil, method)
+ sym.Def = fn.Nname
+
+ // Declare and initialize variable holding receiver.
+ recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
+
+ if !needed {
+ typecheck.Func(fn)
+ return
+ }
+
+ addTailCall(pos, fn, recv, method)
+
+ finishWrapperFunc(fn, target)
+}
+
+func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
+ fn := ir.NewFunc(pos)
+ fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
+
+ name := ir.NewNameAt(pos, sym)
+ ir.MarkFunc(name)
+ name.Func = fn
+ name.Defn = fn
+ fn.Nname = name
+
+ sig := newWrapperType(wrapper, method)
+ setType(name, sig)
+
+ // TODO(mdempsky): De-duplicate with similar logic in funcargs.
+ defParams := func(class ir.Class, params *types.Type) {
+ for _, param := range params.FieldSlice() {
+ name := ir.NewNameAt(param.Pos, param.Sym)
+ name.Class = class
+ setType(name, param.Type)
+
+ name.Curfn = fn
+ fn.Dcl = append(fn.Dcl, name)
+
+ param.Nname = name
+ }
+ }
+
+ defParams(ir.PPARAM, sig.Recvs())
+ defParams(ir.PPARAM, sig.Params())
+ defParams(ir.PPARAMOUT, sig.Results())
+
+ return fn
+}
+
+func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
+ typecheck.Func(fn)
+
+ ir.WithFunc(fn, func() {
+ typecheck.Stmts(fn.Body)
+ })
+
+ // We generate wrappers after the global inlining pass,
+ // so we're responsible for applying inlining ourselves here.
+ // TODO(prattmic): plumb PGO.
+ inline.InlineCalls(fn, nil)
+
+ // The body of wrapper function after inlining may reveal new ir.OMETHVALUE node,
+ // we don't know whether wrapper function has been generated for it or not, so
+ // generate one immediately here.
+ ir.VisitList(fn.Body, func(n ir.Node) {
+ if n, ok := n.(*ir.SelectorExpr); ok && n.Op() == ir.OMETHVALUE {
+ wrapMethodValue(n.X.Type(), n.Selection, target, true)
+ }
+ })
+
+ target.Decls = append(target.Decls, fn)
+}
+
+// newWrapperType returns a copy of the given signature type, but with
+// the receiver parameter type substituted with recvType.
+// If recvType is nil, newWrapperType returns a signature
+// without a receiver parameter.
+func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
+ clone := func(params []*types.Field) []*types.Field {
+ res := make([]*types.Field, len(params))
+ for i, param := range params {
+ sym := param.Sym
+ if sym == nil || sym.Name == "_" {
+ sym = typecheck.LookupNum(".anon", i)
+ }
+ res[i] = types.NewField(param.Pos, sym, param.Type)
+ res[i].SetIsDDD(param.IsDDD())
+ }
+ return res
+ }
+
+ sig := method.Type
+
+ var recv *types.Field
+ if recvType != nil {
+ recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
+ }
+ params := clone(sig.Params().FieldSlice())
+ results := clone(sig.Results().FieldSlice())
+
+ return types.NewSignature(types.NoPkg, recv, nil, params, results)
+}
+
+func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
+ sig := fn.Nname.Type()
+ args := make([]ir.Node, sig.NumParams())
+ for i, param := range sig.Params().FieldSlice() {
+ args[i] = param.Nname.(*ir.Name)
+ }
+
+ // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
+ // Not urgent though, because tail calls are currently incompatible with regabi anyway.
+
+ fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
+
+ dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
+ call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
+
+ if method.Type.NumResults() == 0 {
+ fn.Body.Append(call)
+ return
+ }
+
+ ret := ir.NewReturnStmt(pos, nil)
+ ret.Results = []ir.Node{call}
+ fn.Body.Append(ret)
+}
+
+func setBasePos(pos src.XPos) {
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = pos
+}
+
+// dictParamName is the name of the synthetic dictionary parameter
+// added to shaped functions.
+//
+// N.B., this variable name is known to Delve:
+// https://github.com/go-delve/delve/blob/cb91509630529e6055be845688fd21eb89ae8714/pkg/proc/eval.go#L28
+const dictParamName = ".dict"
+
+// shapeSig returns a copy of fn's signature, except adding a
+// dictionary parameter and promoting the receiver parameter (if any)
+// to a normal parameter.
+//
+// The parameter types.Fields are all copied too, so their Nname
+// fields can be initialized for use by the shape function.
+func shapeSig(fn *ir.Func, dict *readerDict) *types.Type {
+ sig := fn.Nname.Type()
+ oldRecv := sig.Recv()
+
+ var recv *types.Field
+ if oldRecv != nil {
+ recv = types.NewField(oldRecv.Pos, oldRecv.Sym, oldRecv.Type)
+ }
+
+ params := make([]*types.Field, 1+sig.Params().Fields().Len())
+ params[0] = types.NewField(fn.Pos(), fn.Sym().Pkg.Lookup(dictParamName), types.NewPtr(dict.varType()))
+ for i, param := range sig.Params().Fields().Slice() {
+ d := types.NewField(param.Pos, param.Sym, param.Type)
+ d.SetIsDDD(param.IsDDD())
+ params[1+i] = d
+ }
+
+ results := make([]*types.Field, sig.Results().Fields().Len())
+ for i, result := range sig.Results().Fields().Slice() {
+ results[i] = types.NewField(result.Pos, result.Sym, result.Type)
+ }
+
+ return types.NewSignature(types.LocalPkg, recv, nil, params, results)
+}
diff --git a/src/cmd/compile/internal/noder/scopes.go b/src/cmd/compile/internal/noder/scopes.go
new file mode 100644
index 0000000..eb51847
--- /dev/null
+++ b/src/cmd/compile/internal/noder/scopes.go
@@ -0,0 +1,64 @@
+// 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/sizes.go b/src/cmd/compile/internal/noder/sizes.go
new file mode 100644
index 0000000..107f4d0
--- /dev/null
+++ b/src/cmd/compile/internal/noder/sizes.go
@@ -0,0 +1,162 @@
+// 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 (
+ "fmt"
+
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// Code below based on go/types.StdSizes.
+// Intentional differences are marked with "gc:".
+
+type gcSizes struct{}
+
+func (s *gcSizes) Alignof(T types2.Type) int64 {
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := T.Underlying().(type) {
+ case *types2.Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return s.Alignof(t.Elem())
+ case *types2.Struct:
+ if t.NumFields() == 0 && types2.IsSyncAtomicAlign64(T) {
+ // Special case: sync/atomic.align64 is an
+ // empty struct we recognize as a signal that
+ // the struct it contains must be
+ // 64-bit-aligned.
+ //
+ // This logic is equivalent to the logic in
+ // cmd/compile/internal/types/size.go:calcStructOffset
+ return 8
+ }
+
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for i, nf := 0, t.NumFields(); i < nf; i++ {
+ if a := s.Alignof(t.Field(i).Type()); a > max {
+ max = a
+ }
+ }
+ return max
+ case *types2.Slice, *types2.Interface:
+ // Multiword data structures are effectively structs
+ // in which each element has size PtrSize.
+ return int64(types.PtrSize)
+ case *types2.Basic:
+ // Strings are like slices and interfaces.
+ if t.Info()&types2.IsString != 0 {
+ return int64(types.PtrSize)
+ }
+ }
+ a := s.Sizeof(T) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ // complex{64,128} are aligned like [2]float{32,64}.
+ if isComplex(T) {
+ a /= 2
+ }
+ if a > int64(types.RegSize) {
+ return int64(types.RegSize)
+ }
+ return a
+}
+
+func isComplex(T types2.Type) bool {
+ basic, ok := T.Underlying().(*types2.Basic)
+ return ok && basic.Info()&types2.IsComplex != 0
+}
+
+func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ typ := f.Type()
+ a := s.Alignof(typ)
+ o = types.RoundUp(o, a)
+ offsets[i] = o
+ o += s.Sizeof(typ)
+ }
+ return offsets
+}
+
+func (s *gcSizes) Sizeof(T types2.Type) int64 {
+ switch t := T.Underlying().(type) {
+ case *types2.Basic:
+ k := t.Kind()
+ if int(k) < len(basicSizes) {
+ if s := basicSizes[k]; s > 0 {
+ return int64(s)
+ }
+ }
+ switch k {
+ case types2.String:
+ return int64(types.PtrSize) * 2
+ case types2.Int, types2.Uint, types2.Uintptr, types2.UnsafePointer:
+ return int64(types.PtrSize)
+ }
+ panic(fmt.Sprintf("unimplemented basic: %v (kind %v)", T, k))
+ case *types2.Array:
+ n := t.Len()
+ if n <= 0 {
+ return 0
+ }
+ // n > 0
+ // gc: Size includes alignment padding.
+ return s.Sizeof(t.Elem()) * n
+ case *types2.Slice:
+ return int64(types.PtrSize) * 3
+ case *types2.Struct:
+ n := t.NumFields()
+ if n == 0 {
+ return 0
+ }
+ fields := make([]*types2.Var, n)
+ for i := range fields {
+ fields[i] = t.Field(i)
+ }
+ offsets := s.Offsetsof(fields)
+
+ // gc: The last field of a non-zero-sized struct is not allowed to
+ // have size 0.
+ last := s.Sizeof(fields[n-1].Type())
+ if last == 0 && offsets[n-1] > 0 {
+ last = 1
+ }
+
+ // gc: Size includes alignment padding.
+ return types.RoundUp(offsets[n-1]+last, s.Alignof(t))
+ case *types2.Interface:
+ return int64(types.PtrSize) * 2
+ case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature:
+ return int64(types.PtrSize)
+ default:
+ panic(fmt.Sprintf("unimplemented type: %T", t))
+ }
+}
+
+var basicSizes = [...]byte{
+ types2.Invalid: 1,
+ types2.Bool: 1,
+ types2.Int8: 1,
+ types2.Int16: 2,
+ types2.Int32: 4,
+ types2.Int64: 8,
+ types2.Uint8: 1,
+ types2.Uint16: 2,
+ types2.Uint32: 4,
+ types2.Uint64: 8,
+ types2.Float32: 4,
+ types2.Float64: 8,
+ types2.Complex64: 8,
+ types2.Complex128: 16,
+}
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
new file mode 100644
index 0000000..26a088e
--- /dev/null
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -0,0 +1,2334 @@
+// 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 will evolve, since we plan to do a mix of stenciling and passing
+// around dictionaries.
+
+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<m>(x, a)
+ se := call.X.(*ir.SelectorExpr)
+ call.SetOp(ir.OCALLFUNC)
+ idx := findMethodExprClosure(info.dictInfo, se)
+ c := getDictionaryEntryAddr(se.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen)
+ t := typecheck.NewMethodType(se.Type(), se.X.Type())
+ call.X = ir.NewConvExpr(se.Pos(), ir.OCONVNOP, t, c)
+ typed(t, call.X)
+ call.Args.Prepend(se.X)
+ break
+ // TODO: deref case?
+ }
+ if op == ir.OMETHVALUE {
+ // Redo the transformation of OXDOT, now that we
+ // know the method value is being called.
+ call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
+ transformDot(call.X.(*ir.SelectorExpr), true)
+ }
+ transformCall(call)
+
+ case ir.OCONVIFACE:
+ if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
+ // Was T->interface{}, after stenciling it is now interface{}->interface{}.
+ // No longer need the conversion. See issue 48276.
+ m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
+ break
+ }
+ mce := m.(*ir.ConvExpr)
+ // Note: x's argument is still typed as a type parameter.
+ // m's argument now has an instantiated type.
+ if mce.X.Type().HasShape() || (m.Type().HasShape() && !m.Type().IsEmptyInterface()) {
+ m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type())
+ }
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ dt := m.(*ir.TypeAssertExpr)
+ if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) {
+ break
+ }
+ if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) {
+ break
+ }
+ var rtype, itab ir.Node
+ if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
+ // TODO(mdempsky): Investigate executing this block unconditionally.
+ ix := findDictType(info, m.Type())
+ assert(ix >= 0)
+ rtype = getDictionaryType(info, info.dictParam, dt.Pos(), ix)
+ } else {
+ // nonempty interface to noninterface. Need an itab.
+ ix := -1
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == m {
+ ix = info.dictInfo.startItabConv + i
+ break
+ }
+ }
+ assert(ix >= 0)
+ itab = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+ }
+ op := ir.ODYNAMICDOTTYPE
+ if m.Op() == ir.ODOTTYPE2 {
+ op = ir.ODYNAMICDOTTYPE2
+ }
+ m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rtype)
+ m.(*ir.DynamicTypeAssertExpr).ITab = itab
+ m.SetType(dt.Type())
+ m.SetTypecheck(1)
+ case ir.OCASE:
+ if _, ok := m.(*ir.CommClause); ok {
+ // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
+ break
+ }
+ m := m.(*ir.CaseClause)
+ for i, c := range m.List {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
+ // Use a *runtime._type for the dynamic type.
+ ix := findDictType(info, m.List[i].Type())
+ assert(ix >= 0)
+ dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen))
+
+ // For type switch from nonempty interfaces to non-interfaces, we need an itab as well.
+ if !m.List[i].Type().IsInterface() {
+ if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok {
+ // Type switch from nonempty interface. We need a *runtime.itab
+ // for the dynamic type.
+ ix := -1
+ for j, ic := range info.dictInfo.itabConvs {
+ if ic == m.List[i] {
+ ix = info.dictInfo.startItabConv + j
+ break
+ }
+ }
+ assert(ix >= 0)
+ dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+ }
+ }
+ typed(m.List[i].Type(), dt)
+ m.List[i] = dt
+ }
+ }
+
+ }
+ return m
+ }
+ edit(info.fun)
+ ir.CurFunc = savef
+}
+
+// findDictType looks for type t in the typeparams or derived types in the generic
+// function info.gfInfo. This will indicate the dictionary entry with the
+// correct concrete type for the associated instantiated function.
+func findDictType(info *instInfo, t *types.Type) int {
+ for i, dt := range info.dictInfo.shapeParams {
+ if dt == t {
+ return i
+ }
+ }
+ for i, dt := range info.dictInfo.derivedTypes {
+ if types.IdenticalStrict(dt, t) {
+ return i + len(info.dictInfo.shapeParams)
+ }
+ }
+ return -1
+}
+
+// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface
+// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
+// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
+// conversion.
+func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
+ assert(v.Type().HasShape() || (in.Type().HasShape() && !in.Type().IsEmptyInterface()))
+ assert(dst.IsInterface())
+
+ if v.Type().IsInterface() {
+ // Converting from an interface. The shape-ness of the source doesn't really matter, as
+ // we'll be using the concrete type from the first interface word.
+ if dst.IsEmptyInterface() {
+ // Converting I2E. OCONVIFACE does that for us, and doesn't depend
+ // on what the empty interface was instantiated with. No dictionary entry needed.
+ v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
+ v.SetTypecheck(1)
+ return v
+ }
+ if !in.Type().HasShape() {
+ // Regular OCONVIFACE works if the destination isn't parameterized.
+ v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
+ v.SetTypecheck(1)
+ return v
+ }
+
+ // We get the destination interface type from the dictionary and the concrete
+ // type from the argument's itab. Call runtime.convI2I to get the new itab.
+ tmp := typecheck.Temp(v.Type())
+ as := ir.NewAssignStmt(pos, tmp, v)
+ as.SetTypecheck(1)
+ itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp)
+ typed(types.Types[types.TUINTPTR].PtrTo(), itab)
+ idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp)
+ typed(types.Types[types.TUNSAFEPTR], idata)
+
+ fn := typecheck.LookupRuntime("convI2I")
+ fn.SetTypecheck(1)
+ types.CalcSize(fn.Type())
+ call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
+ typed(types.Types[types.TUINT8].PtrTo(), call)
+ ix := findDictType(info, in.Type())
+ assert(ix >= 0)
+ inter := getDictionaryType(info, dictParam, pos, ix)
+ call.Args = []ir.Node{inter, itab}
+ i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata)
+ typed(dst, i)
+ i.PtrInit().Append(as)
+ return i
+ }
+
+ var rt ir.Node
+ if !dst.IsEmptyInterface() {
+ // We should have an itab entry in the dictionary. Using this itab
+ // will be more efficient than converting to an empty interface first
+ // and then type asserting to dst.
+ ix := -1
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == in {
+ ix = info.dictInfo.startItabConv + i
+ break
+ }
+ }
+ assert(ix >= 0)
+ rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
+ } else {
+ ix := findDictType(info, v.Type())
+ assert(ix >= 0)
+ // Load the actual runtime._type of the type parameter from the dictionary.
+ rt = getDictionaryType(info, dictParam, pos, ix)
+ }
+
+ // Figure out what the data field of the interface will be.
+ data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
+ typed(types.Types[types.TUNSAFEPTR], data)
+
+ // Build an interface from the type and data parts.
+ var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
+ typed(dst, i)
+ return i
+}
+
+func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
+ s := make([]*ir.Name, len(l))
+ for i, n := range l {
+ s[i] = subst.localvar(n)
+ }
+ return s
+}
+
+func (subst *subster) list(l []ir.Node) []ir.Node {
+ s := make([]ir.Node, len(l))
+ for i, n := range l {
+ s[i] = subst.node(n)
+ }
+ return s
+}
+
+// fields sets the Nname field for the Field nodes inside a type signature, based
+// on the corresponding in/out parameters in dcl. It depends on the in and out
+// parameters being in order in dcl.
+func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
+ // Find the starting index in dcl of declarations of the class (either
+ // PPARAM or PPARAMOUT).
+ var i int
+ for i = range dcl {
+ if dcl[i].Class == class {
+ break
+ }
+ }
+
+ // Create newfields nodes that are copies of the oldfields nodes, but
+ // with substitution for any type params, and with Nname set to be the node in
+ // Dcl for the corresponding PPARAM or PPARAMOUT.
+ newfields := make([]*types.Field, len(oldfields))
+ for j := range oldfields {
+ newfields[j] = oldfields[j].Copy()
+ newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
+ // A PPARAM field will be missing from dcl if its name is
+ // unspecified or specified as "_". So, we compare the dcl sym
+ // with the field sym (or sym of the field's Nname node). (Unnamed
+ // results still have a name like ~r2 in their Nname node.) If
+ // they don't match, this dcl (if there is one left) must apply to
+ // a later field.
+ if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
+ (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
+ newfields[j].Nname = dcl[i]
+ i++
+ }
+ }
+ return newfields
+}
+
+// deref does a single deref of type t, if it is a pointer type.
+func deref(t *types.Type) *types.Type {
+ if t.IsPtr() {
+ return t.Elem()
+ }
+ return t
+}
+
+// markTypeUsed marks type t as used in order to help avoid dead-code elimination of
+// needed methods.
+func markTypeUsed(t *types.Type, lsym *obj.LSym) {
+ if t.IsInterface() {
+ return
+ }
+ // TODO: This is somewhat overkill, we really only need it
+ // for types that are put into interfaces.
+ // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
+ reflectdata.MarkTypeUsedInInterface(t, lsym)
+}
+
+// getDictionarySym returns the dictionary for the named generic function gf, which
+// is instantiated with the type arguments targs.
+func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
+ if len(targs) == 0 {
+ base.Fatalf("%s should have type arguments", gf.Sym().Name)
+ }
+
+ // Enforce that only concrete types can make it to here.
+ for _, t := range targs {
+ if t.HasShape() {
+ panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
+ }
+ }
+
+ // Get a symbol representing the dictionary.
+ sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth)
+
+ // Initialize the dictionary, if we haven't yet already.
+ lsym := sym.Linksym()
+ if len(lsym.P) > 0 {
+ // We already started creating this dictionary and its lsym.
+ return sym
+ }
+
+ infoPrint("=== Creating dictionary %v\n", sym.Name)
+ off := 0
+ // Emit an entry for each targ (concrete type or gcshape).
+ for _, t := range targs {
+ infoPrint(" * %v\n", t)
+ s := reflectdata.TypeLinksym(t)
+ off = objw.SymPtr(lsym, off, s, 0)
+ markTypeUsed(t, lsym)
+ }
+
+ instInfo := g.getInstantiation(gf, targs, isMeth)
+ info := instInfo.dictInfo
+
+ subst := typecheck.Tsubster{
+ Tparams: info.shapeParams,
+ Targs: targs,
+ }
+ // Emit an entry for each derived type (after substituting targs)
+ for _, t := range info.derivedTypes {
+ ts := subst.Typ(t)
+ infoPrint(" - %v\n", ts)
+ s := reflectdata.TypeLinksym(ts)
+ off = objw.SymPtr(lsym, off, s, 0)
+ markTypeUsed(ts, lsym)
+ }
+ // Emit an entry for each subdictionary (after substituting targs)
+ for _, subDictInfo := range info.subDictCalls {
+ var sym *types.Sym
+ n := subDictInfo.callNode
+ switch n.Op() {
+ case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
+ call := n.(*ir.CallExpr)
+ if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
+ var nameNode *ir.Name
+ se := call.X.(*ir.SelectorExpr)
+ if se.X.Type().IsShape() {
+ tparam := se.X.Type()
+ // Ensure methods on all instantiating types are computed.
+ typecheck.CalcMethods(tparam)
+ if typecheck.Lookdot1(nil, se.Sel, tparam, tparam.AllMethods(), 0) != nil {
+ // This is a method call enabled by a type bound.
+ // We need this extra check for method expressions,
+ // which don't add in the implicit XDOTs.
+ tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
+ tmpse = typecheck.AddImplicitDots(tmpse)
+ tparam = tmpse.X.Type()
+ }
+ if !tparam.IsShape() {
+ // The method expression is not
+ // really on a typeparam.
+ break
+ }
+ ix := -1
+ for i, shape := range info.shapeParams {
+ if shape == tparam {
+ ix = i
+ break
+ }
+ }
+ assert(ix >= 0)
+ recvType := targs[ix]
+ if recvType.IsInterface() || len(recvType.RParams()) == 0 {
+ // No sub-dictionary entry is
+ // actually needed, since the
+ // type arg is not an
+ // instantiated type that
+ // will have generic methods.
+ break
+ }
+ // This is a method call for an
+ // instantiated type, so we need a
+ // sub-dictionary.
+ targs := recvType.RParams()
+ genRecvType := recvType.OrigType()
+ nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ sym = g.getDictionarySym(nameNode, targs, true)
+ } else {
+ // This is the case of a normal
+ // method call on a generic type.
+ assert(subDictInfo.savedXNode == se)
+ sym = g.getSymForMethodCall(se, &subst)
+ }
+ } else {
+ inst, ok := call.X.(*ir.InstExpr)
+ if ok {
+ // Code hasn't been transformed yet
+ assert(subDictInfo.savedXNode == inst)
+ }
+ // If !ok, then the generic method/function call has
+ // already been transformed to a shape instantiation
+ // call. Either way, use the SelectorExpr/InstExpr
+ // node saved in info.
+ cex := subDictInfo.savedXNode
+ if se, ok := cex.(*ir.SelectorExpr); ok {
+ sym = g.getSymForMethodCall(se, &subst)
+ } else {
+ inst := cex.(*ir.InstExpr)
+ nameNode := inst.X.(*ir.Name)
+ subtargs := typecheck.TypesOf(inst.Targs)
+ for i, t := range subtargs {
+ subtargs[i] = subst.Typ(t)
+ }
+ sym = g.getDictionarySym(nameNode, subtargs, false)
+ }
+ }
+
+ case ir.OFUNCINST:
+ inst := n.(*ir.InstExpr)
+ nameNode := inst.X.(*ir.Name)
+ subtargs := typecheck.TypesOf(inst.Targs)
+ for i, t := range subtargs {
+ subtargs[i] = subst.Typ(t)
+ }
+ sym = g.getDictionarySym(nameNode, subtargs, false)
+
+ case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
+ sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)
+
+ default:
+ assert(false)
+ }
+
+ if sym == nil {
+ // Unused sub-dictionary entry, just emit 0.
+ off = objw.Uintptr(lsym, off, 0)
+ infoPrint(" - Unused subdict entry\n")
+ } else {
+ off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
+ infoPrint(" - Subdict %v\n", sym.Name)
+ }
+ }
+
+ g.instantiateMethods()
+ delay := &delayInfo{
+ gf: gf,
+ targs: targs,
+ sym: sym,
+ off: off,
+ isMeth: isMeth,
+ }
+ g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
+ return sym
+}
+
+// getSymForMethodCall gets the dictionary sym for a method call, method value, or method
+// expression that has selector se. subst gives the substitution from shape types to
+// concrete types.
+func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym {
+ // For everything except method expressions, 'recvType = deref(se.X.Type)' would
+ // also give the receiver type. For method expressions with embedded types, we
+ // need to look at the type of the selection to get the final receiver type.
+ recvType := deref(se.Selection.Type.Recv().Type)
+ genRecvType := recvType.OrigType()
+ nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ subtargs := recvType.RParams()
+ s2targs := make([]*types.Type, len(subtargs))
+ for i, t := range subtargs {
+ s2targs[i] = subst.Typ(t)
+ }
+ return g.getDictionarySym(nameNode, s2targs, true)
+}
+
+// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
+// any needed LSyms for itabs. The itab lsyms create wrappers which need various
+// dictionaries and method instantiations to be complete, so, to avoid recursive
+// dependencies, we finalize the itab lsyms only after all dictionaries syms and
+// instantiations have been created.
+// Also handles writing method expression closures into the dictionaries.
+func (g *genInst) finalizeSyms() {
+Outer:
+ for _, d := range g.dictSymsToFinalize {
+ infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
+
+ lsym := d.sym.Linksym()
+ instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
+ info := instInfo.dictInfo
+
+ subst := typecheck.Tsubster{
+ Tparams: info.shapeParams,
+ Targs: d.targs,
+ }
+
+ // Emit an entry for each itab
+ for _, n := range info.itabConvs {
+ var srctype, dsttype *types.Type
+ switch n.Op() {
+ case ir.OXDOT, ir.OMETHVALUE:
+ se := n.(*ir.SelectorExpr)
+ srctype = subst.Typ(se.X.Type())
+ dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type())
+ dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type())
+ case ir.OCONVIFACE:
+ srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
+ dsttype = subst.Typ(n.Type())
+ case ir.OTYPE:
+ srctype = subst.Typ(n.Type())
+ dsttype = subst.Typ(info.type2switchType[n])
+ default:
+ base.Fatalf("itab entry with unknown op %s", n.Op())
+ }
+ if srctype.IsInterface() || dsttype.IsEmptyInterface() {
+ // No itab is wanted if src type is an interface. We
+ // will use a type assert instead.
+ d.off = objw.Uintptr(lsym, d.off, 0)
+ infoPrint(" + Unused itab entry for %v\n", srctype)
+ } else {
+ // Make sure all new fully-instantiated types have
+ // their methods created before generating any itabs.
+ g.instantiateMethods()
+ itabLsym := reflectdata.ITabLsym(srctype, dsttype)
+ d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
+ markTypeUsed(srctype, lsym)
+ infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
+ }
+ }
+
+ // Emit an entry for each method expression closure.
+ // Each entry is a (captureless) closure pointing to the method on the instantiating type.
+ // In other words, the entry is a runtime.funcval whose fn field is set to the method
+ // in question, and has no other fields. The address of this dictionary entry can be
+ // cast to a func of the appropriate type.
+ // TODO: do these need to be done when finalizing, or can we do them earlier?
+ for _, bf := range info.methodExprClosures {
+ rcvr := d.targs[bf.idx]
+ rcvr2 := deref(rcvr)
+ found := false
+ typecheck.CalcMethods(rcvr2) // Ensure methods on all instantiating types are computed.
+ for _, f := range rcvr2.AllMethods().Slice() {
+ if f.Sym.Name == bf.name {
+ codePtr := ir.MethodSym(rcvr, f.Sym).Linksym()
+ d.off = objw.SymPtr(lsym, d.off, codePtr, 0)
+ infoPrint(" + MethodExprClosure for %v.%s\n", rcvr, bf.name)
+ found = true
+ break
+ }
+ }
+ if !found {
+ // We failed to find a method expression needed for this
+ // dictionary. This may happen because we tried to create a
+ // dictionary for an invalid instantiation.
+ //
+ // For example, in test/typeparam/issue54225.go, we attempt to
+ // construct a dictionary for "Node[struct{}].contentLen",
+ // even though "struct{}" does not implement "Value", so it
+ // cannot actually be used as a type argument to "Node".
+ //
+ // The real issue here is we shouldn't be attempting to create
+ // those dictionaries in the first place (e.g., CL 428356),
+ // but that fix is scarier for backporting to Go 1.19. Too
+ // many backport CLs to this code have fixed one issue while
+ // introducing another.
+ //
+ // So as a hack, instead of calling Fatalf, we simply skip
+ // calling objw.Global below, which prevents us from emitting
+ // the broken dictionary. The linker's dead code elimination
+ // should then naturally prune this invalid, unneeded
+ // dictionary. Worst case, if the dictionary somehow *is*
+ // needed by the final executable, we've just turned an ICE
+ // into a link-time missing symbol failure.
+ infoPrint(" ! abandoning dictionary %v; missing method expression %v.%s\n", d.sym.Name, rcvr, bf.name)
+ continue Outer
+ }
+ }
+
+ objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
+ infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
+ }
+ g.dictSymsToFinalize = nil
+}
+
+func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
+ sym := g.getDictionarySym(gf, targs, isMeth)
+
+ // Make (or reuse) a node referencing the dictionary symbol.
+ var n *ir.Name
+ if sym.Def != nil {
+ n = sym.Def.(*ir.Name)
+ } else {
+ // We set the position of a static dictionary to be the position of
+ // one of its uses.
+ n = ir.NewNameAt(pos, sym)
+ n.Curfn = ir.CurFunc
+ n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
+ n.SetTypecheck(1)
+ n.Class = ir.PEXTERN
+ sym.Def = n
+ }
+
+ // Return the address of the dictionary. Addr node gets position that was passed in.
+ np := typecheck.NodAddrAt(pos, n)
+ // Note: treat dictionary pointers as uintptrs, so they aren't pointers
+ // with respect to GC. That saves on stack scanning work, write barriers, etc.
+ // We can get away with it because dictionaries are global variables.
+ // TODO: use a cast, or is typing directly ok?
+ np.SetType(types.Types[types.TUINTPTR])
+ np.SetTypecheck(1)
+ return np
+}
+
+// hasShapeNodes returns true if the type of any node in targs has a shape.
+func hasShapeNodes(targs []ir.Ntype) bool {
+ for _, n := range targs {
+ if n.Type().HasShape() {
+ return true
+ }
+ }
+ return false
+}
+
+// hasShapeTypes returns true if any type in targs has a shape.
+func hasShapeTypes(targs []*types.Type) bool {
+ for _, t := range targs {
+ if t.HasShape() {
+ return true
+ }
+ }
+ return false
+}
+
+// getInstInfo get the dictionary format for a function instantiation- type params, derived
+// types, and needed subdictionaries, itabs, and method expression closures.
+func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
+ info := instInfo.dictInfo
+ info.shapeParams = shapes
+
+ for _, t := range info.shapeParams {
+ b := info.shapeToBound[t]
+ if b.HasShape() {
+ // If a type bound is parameterized (unusual case), then we
+ // may need its derived type to do a type assert when doing a
+ // bound call for a type arg that is an interface.
+ addType(info, nil, b)
+ }
+ }
+
+ for _, n := range st.Dcl {
+ addType(info, n, n.Type())
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+ }
+
+ if infoPrintMode {
+ fmt.Printf(">>> InstInfo for %v\n", st)
+ for _, t := range info.shapeParams {
+ fmt.Printf(" Typeparam %v\n", t)
+ }
+ }
+
+ // Map to remember when we have seen an instantiated function value or method
+ // expression/value as part of a call, so we can determine when we encounter
+ // an uncalled function value or method expression/value.
+ callMap := make(map[ir.Node]bool)
+
+ var visitFunc func(ir.Node)
+ visitFunc = func(n ir.Node) {
+ switch n.Op() {
+ case ir.OFUNCINST:
+ if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
+ infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
+ info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
+ }
+ case ir.OMETHEXPR, ir.OMETHVALUE:
+ if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
+ len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
+ hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
+ if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
+ infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n)
+ } else {
+ infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
+ }
+ info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
+ }
+ case ir.OCALL:
+ ce := n.(*ir.CallExpr)
+ if ce.X.Op() == ir.OFUNCINST {
+ callMap[ce.X] = true
+ if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
+ infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
+ // Save the instExpr node for the function call,
+ // since we will lose this information when the
+ // generic function call is transformed to a call
+ // on the shape instantiation.
+ info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
+ }
+ }
+ // Note: this XDOT code is not actually needed as long as we
+ // continue to disable type parameters on RHS of type
+ // declarations (#45639).
+ if ce.X.Op() == ir.OXDOT {
+ callMap[ce.X] = true
+ if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) {
+ infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
+ info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
+ }
+ }
+ case ir.OCALLMETH:
+ ce := n.(*ir.CallExpr)
+ if ce.X.Op() == ir.ODOTMETH &&
+ len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
+ callMap[ce.X] = true
+ if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
+ infoPrint(" Subdictionary at generic method call: %v\n", n)
+ // Save the selector for the method call, since we
+ // will eventually lose this information when the
+ // generic method call is transformed into a
+ // function call on the method shape instantiation.
+ info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
+ }
+ }
+ case ir.OCONVIFACE:
+ if n.Type().IsInterface() && !n.Type().IsEmptyInterface() &&
+ (n.Type().HasShape() || n.(*ir.ConvExpr).X.Type().HasShape()) {
+ infoPrint(" Itab for interface conv: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+ case ir.OXDOT:
+ se := n.(*ir.SelectorExpr)
+ if se.X.Op() == ir.OTYPE && se.X.Type().IsShape() {
+ // Method expression.
+ addMethodExprClosure(info, se)
+ break
+ }
+ if isBoundMethod(info, se) {
+ if callMap[n] {
+ // Method value called directly. Use method expression closure.
+ addMethodExprClosure(info, se)
+ break
+ }
+ // Method value not called directly. Still doing the old way.
+ infoPrint(" Itab for bound call: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() {
+ infoPrint(" Itab for dot type: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+ case ir.OCLOSURE:
+ // Visit the closure body and add all relevant entries to the
+ // dictionary of the outer function (closure will just use
+ // the dictionary of the outer function).
+ cfunc := n.(*ir.ClosureExpr).Func
+ for _, n1 := range cfunc.Body {
+ ir.Visit(n1, visitFunc)
+ }
+ for _, n := range cfunc.Dcl {
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+ }
+ case ir.OSWITCH:
+ ss := n.(*ir.SwitchStmt)
+ if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW &&
+ !ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
+ for _, cc := range ss.Cases {
+ for _, c := range cc.List {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
+ // Type switch from a non-empty interface - might need an itab.
+ infoPrint(" Itab for type switch: %v\n", c)
+ info.itabConvs = append(info.itabConvs, c)
+ if info.type2switchType == nil {
+ info.type2switchType = map[ir.Node]*types.Type{}
+ }
+ info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type()
+ }
+ }
+ }
+ }
+ }
+ addType(info, n, n.Type())
+ }
+
+ for _, stmt := range st.Body {
+ ir.Visit(stmt, visitFunc)
+ }
+ if infoPrintMode {
+ for _, t := range info.derivedTypes {
+ fmt.Printf(" Derived type %v\n", t)
+ }
+ fmt.Printf(">>> Done Instinfo\n")
+ }
+ info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
+ info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
+ info.startMethodExprClosures = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
+ info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) + len(info.methodExprClosures)
+}
+
+// isBoundMethod returns true if the selection indicated by se is a bound method of
+// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If
+// isBoundMethod returns false, then the selection must be a field access of a
+// structural type.
+func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool {
+ bound := info.shapeToBound[se.X.Type()]
+ return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil
+}
+
+func shapeIndex(info *dictInfo, t *types.Type) int {
+ for i, s := range info.shapeParams {
+ if s == t {
+ return i
+ }
+ }
+ base.Fatalf("can't find type %v in shape params", t)
+ return -1
+}
+
+// addMethodExprClosure adds the T.M method expression to the list of bound method expressions
+// used in the generic body.
+// isBoundMethod must have returned true on the same arguments.
+func addMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) {
+ idx := shapeIndex(info, se.X.Type())
+ name := se.Sel.Name
+ for _, b := range info.methodExprClosures {
+ if idx == b.idx && name == b.name {
+ return
+ }
+ }
+ infoPrint(" Method expression closure for %v.%s\n", info.shapeParams[idx], name)
+ info.methodExprClosures = append(info.methodExprClosures, methodExprClosure{idx: idx, name: name})
+}
+
+// findMethodExprClosure finds the entry in the dictionary to use for the T.M
+// method expression encoded in se.
+// isBoundMethod must have returned true on the same arguments.
+func findMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) int {
+ idx := shapeIndex(info, se.X.Type())
+ name := se.Sel.Name
+ for i, b := range info.methodExprClosures {
+ if idx == b.idx && name == b.name {
+ return i
+ }
+ }
+ base.Fatalf("can't find method expression closure for %s %s", se.X.Type(), name)
+ return -1
+}
+
+// addType adds t to info.derivedTypes if it is parameterized type (which is not
+// just a simple shape) that is different from any existing type on
+// info.derivedTypes.
+func addType(info *dictInfo, n ir.Node, t *types.Type) {
+ if t == nil || !t.HasShape() {
+ return
+ }
+ if t.IsShape() {
+ return
+ }
+ if t.Kind() == types.TFUNC && n != nil &&
+ (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
+ // Don't use the type of a named generic function or method,
+ // since that is parameterized by other typeparams.
+ // (They all come from arguments of a FUNCINST node.)
+ return
+ }
+ if doubleCheck && !parameterizedBy(t, info.shapeParams) {
+ base.Fatalf("adding type with invalid parameters %+v", t)
+ }
+ if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
+ // Multiple return values are not a relevant new type (?).
+ return
+ }
+ // Ignore a derived type we've already added.
+ for _, et := range info.derivedTypes {
+ if types.IdenticalStrict(t, et) {
+ return
+ }
+ }
+ info.derivedTypes = append(info.derivedTypes, t)
+}
+
+// parameterizedBy returns true if t is parameterized by (at most) params.
+func parameterizedBy(t *types.Type, params []*types.Type) bool {
+ return parameterizedBy1(t, params, map[*types.Type]bool{})
+}
+func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
+ if visited[t] {
+ return true
+ }
+ visited[t] = true
+
+ if t.Sym() != nil && len(t.RParams()) > 0 {
+ // This defined type is instantiated. Check the instantiating types.
+ for _, r := range t.RParams() {
+ if !parameterizedBy1(r, params, visited) {
+ return false
+ }
+ }
+ return true
+ }
+ if t.IsShape() {
+ // Check if t is one of the allowed parameters in scope.
+ for _, p := range params {
+ if p == t {
+ return true
+ }
+ }
+ // Couldn't find t in the list of allowed parameters.
+ return false
+
+ }
+ switch t.Kind() {
+ case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
+ return parameterizedBy1(t.Elem(), params, visited)
+
+ case types.TMAP:
+ return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)
+
+ case types.TFUNC:
+ return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)
+
+ case types.TSTRUCT:
+ for _, f := range t.Fields().Slice() {
+ if !parameterizedBy1(f.Type, params, visited) {
+ return false
+ }
+ }
+ return true
+
+ case types.TINTER:
+ for _, f := range t.Methods().Slice() {
+ if !parameterizedBy1(f.Type, params, visited) {
+ return false
+ }
+ }
+ return true
+
+ case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
+ types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
+ types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
+ return true
+
+ case types.TUNION:
+ for i := 0; i < t.NumTerms(); i++ {
+ tt, _ := t.Term(i)
+ if !parameterizedBy1(tt, params, visited) {
+ return false
+ }
+ }
+ return true
+
+ default:
+ base.Fatalf("bad type kind %+v", t)
+ return true
+ }
+}
+
+// startClosure starts creation of a closure that has the function type typ. It
+// creates all the formal params and results according to the type typ. On return,
+// the body and closure variables of the closure must still be filled in, and
+// ir.UseClosure() called.
+func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) {
+ // Make a new internal function.
+ fn := ir.NewClosureFunc(pos, outer != nil)
+ ir.NameClosure(fn.OClosure, outer)
+
+ // Build formal argument and return lists.
+ var formalParams []*types.Field // arguments of closure
+ var formalResults []*types.Field // returns of closure
+ for i := 0; i < typ.NumParams(); i++ {
+ t := typ.Params().Field(i).Type
+ arg := ir.NewNameAt(pos, closureSym(outer, "a", i))
+ arg.Class = ir.PPARAM
+ typed(t, arg)
+ arg.Curfn = fn
+ fn.Dcl = append(fn.Dcl, arg)
+ f := types.NewField(pos, arg.Sym(), t)
+ f.Nname = arg
+ f.SetIsDDD(typ.Params().Field(i).IsDDD())
+ formalParams = append(formalParams, f)
+ }
+ for i := 0; i < typ.NumResults(); i++ {
+ t := typ.Results().Field(i).Type
+ result := ir.NewNameAt(pos, closureSym(outer, "r", i)) // TODO: names not needed?
+ result.Class = ir.PPARAMOUT
+ typed(t, result)
+ result.Curfn = fn
+ fn.Dcl = append(fn.Dcl, result)
+ f := types.NewField(pos, result.Sym(), t)
+ f.Nname = result
+ formalResults = append(formalResults, f)
+ }
+
+ // Build an internal function with the right signature.
+ closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults)
+ typed(closureType, fn.Nname)
+ typed(typ, fn.OClosure)
+ fn.SetTypecheck(1)
+ return fn, formalParams, formalResults
+
+}
+
+// closureSym returns outer.Sym().Pkg.LookupNum(prefix, n).
+// If outer is nil, then types.LocalPkg is used instead.
+func closureSym(outer *ir.Func, prefix string, n int) *types.Sym {
+ pkg := types.LocalPkg
+ if outer != nil {
+ pkg = outer.Sym().Pkg
+ }
+ return pkg.LookupNum(prefix, n)
+}
+
+// assertToBound returns a new node that converts a node rcvr with interface type to
+// the 'dst' interface type.
+func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
+ if !dst.HasShape() {
+ return typed(dst, ir.NewTypeAssertExpr(pos, rcvr, nil))
+ }
+
+ ix := findDictType(info, dst)
+ assert(ix >= 0)
+ rt := getDictionaryType(info, dictVar, pos, ix)
+ return typed(dst, ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt))
+}
diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go
new file mode 100644
index 0000000..a349a7e
--- /dev/null
+++ b/src/cmd/compile/internal/noder/stmt.go
@@ -0,0 +1,353 @@
+// 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"
+)
+
+// stmts creates nodes for a slice of statements that form a scope.
+func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
+ var nodes []ir.Node
+ types.Markdcl()
+ for _, stmt := range stmts {
+ switch s := g.stmt(stmt).(type) {
+ case nil: // EmptyStmt
+ case *ir.BlockStmt:
+ nodes = append(nodes, s.List...)
+ default:
+ nodes = append(nodes, s)
+ }
+ }
+ types.Popdcl()
+ return nodes
+}
+
+func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
+ base.Assert(g.exprStmtOK)
+ switch stmt := stmt.(type) {
+ case nil, *syntax.EmptyStmt:
+ return nil
+ case *syntax.LabeledStmt:
+ return g.labeledStmt(stmt)
+ case *syntax.BlockStmt:
+ return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
+ case *syntax.ExprStmt:
+ return wrapname(g.pos(stmt.X), g.expr(stmt.X))
+ case *syntax.SendStmt:
+ n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
+ if !g.delayTransform() {
+ transformSend(n)
+ }
+ n.SetTypecheck(1)
+ return n
+ case *syntax.DeclStmt:
+ if g.topFuncIsGeneric && len(stmt.DeclList) > 0 {
+ if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok {
+ // TODO: remove this restriction. See issue 47631.
+ base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported")
+ }
+ }
+ n := ir.NewBlockStmt(g.pos(stmt), nil)
+ g.decls(&n.List, stmt.DeclList)
+ return n
+
+ case *syntax.AssignStmt:
+ if stmt.Op != 0 && stmt.Op != syntax.Def {
+ op := g.op(stmt.Op, binOps[:])
+ var n *ir.AssignOpStmt
+ if stmt.Rhs == nil {
+ n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
+ } else {
+ // Eval rhs before lhs, for compatibility with noder1
+ rhs := g.expr(stmt.Rhs)
+ lhs := g.expr(stmt.Lhs)
+ n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs)
+ }
+ if !g.delayTransform() {
+ transformAsOp(n)
+ }
+ n.SetTypecheck(1)
+ return n
+ }
+
+ // Eval rhs before lhs, for compatibility with noder1
+ rhs := g.exprList(stmt.Rhs)
+ names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
+
+ if len(lhs) == 1 && len(rhs) == 1 {
+ n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
+ n.Def = initDefn(n, names)
+
+ if !g.delayTransform() {
+ lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+ transformAssign(n, lhs, rhs)
+ n.X, n.Y = lhs[0], rhs[0]
+ }
+ n.SetTypecheck(1)
+ return n
+ }
+
+ n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
+ n.Def = initDefn(n, names)
+ if !g.delayTransform() {
+ transformAssign(n, n.Lhs, n.Rhs)
+ }
+ n.SetTypecheck(1)
+ return n
+
+ case *syntax.BranchStmt:
+ return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
+ case *syntax.CallStmt:
+ return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
+ case *syntax.ReturnStmt:
+ n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
+ if !g.delayTransform() {
+ transformReturn(n)
+ }
+ n.SetTypecheck(1)
+ return n
+ case *syntax.IfStmt:
+ return g.ifStmt(stmt)
+ case *syntax.ForStmt:
+ return g.forStmt(stmt)
+ case *syntax.SelectStmt:
+ n := g.selectStmt(stmt)
+
+ if !g.delayTransform() {
+ transformSelect(n.(*ir.SelectStmt))
+ }
+ n.SetTypecheck(1)
+ return n
+ case *syntax.SwitchStmt:
+ return g.switchStmt(stmt)
+
+ default:
+ g.unhandled("statement", stmt)
+ panic("unreachable")
+ }
+}
+
+// TODO(mdempsky): Investigate replacing with switch statements or dense arrays.
+
+var branchOps = [...]ir.Op{
+ syntax.Break: ir.OBREAK,
+ syntax.Continue: ir.OCONTINUE,
+ syntax.Fallthrough: ir.OFALL,
+ syntax.Goto: ir.OGOTO,
+}
+
+var callOps = [...]ir.Op{
+ syntax.Defer: ir.ODEFER,
+ 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.
+func initDefn(defn ir.InitNode, names []*ir.Name) bool {
+ if len(names) == 0 {
+ return false
+ }
+
+ init := make([]ir.Node, len(names))
+ for i, name := range names {
+ name.Defn = defn
+ init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
+ }
+ defn.SetInit(init)
+ 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) {
+ switch len(list) {
+ case 0:
+ return nil, nil
+ case 1:
+ return list[0], nil
+ default:
+ 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
new file mode 100644
index 0000000..15adb89
--- /dev/null
+++ b/src/cmd/compile/internal/noder/transform.go
@@ -0,0 +1,1085 @@
+// 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
new file mode 100644
index 0000000..57b35e6
--- /dev/null
+++ b/src/cmd/compile/internal/noder/types.go
@@ -0,0 +1,516 @@
+// 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/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],
+ types2.Int: &types.Types[types.TINT],
+ types2.Int8: &types.Types[types.TINT8],
+ types2.Int16: &types.Types[types.TINT16],
+ types2.Int32: &types.Types[types.TINT32],
+ types2.Int64: &types.Types[types.TINT64],
+ types2.Uint: &types.Types[types.TUINT],
+ types2.Uint8: &types.Types[types.TUINT8],
+ types2.Uint16: &types.Types[types.TUINT16],
+ types2.Uint32: &types.Types[types.TUINT32],
+ types2.Uint64: &types.Types[types.TUINT64],
+ types2.Uintptr: &types.Types[types.TUINTPTR],
+ types2.Float32: &types.Types[types.TFLOAT32],
+ types2.Float64: &types.Types[types.TFLOAT64],
+ types2.Complex64: &types.Types[types.TCOMPLEX64],
+ types2.Complex128: &types.Types[types.TCOMPLEX128],
+ types2.String: &types.Types[types.TSTRING],
+ types2.UnsafePointer: &types.Types[types.TUNSAFEPTR],
+ types2.UntypedBool: &types.UntypedBool,
+ types2.UntypedInt: &types.UntypedInt,
+ types2.UntypedRune: &types.UntypedRune,
+ types2.UntypedFloat: &types.UntypedFloat,
+ types2.UntypedComplex: &types.UntypedComplex,
+ types2.UntypedString: &types.UntypedString,
+ types2.UntypedNil: &types.Types[types.TNIL],
+}
+
+var dirs = [...]types.ChanDir{
+ types2.SendRecv: types.Cboth,
+ types2.SendOnly: types.Csend,
+ types2.RecvOnly: types.Crecv,
+}
+
+// deref2 does a single deref of types2 type t, if it is a pointer type.
+func deref2(t types2.Type) types2.Type {
+ if ptr := types2.AsPointer(t); ptr != nil {
+ t = ptr.Elem()
+ }
+ return t
+}
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
new file mode 100644
index 0000000..25136e6
--- /dev/null
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -0,0 +1,441 @@
+// 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 (
+ "fmt"
+ "internal/goversion"
+ "internal/pkgbits"
+ "io"
+ "runtime"
+ "sort"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+// localPkgReader holds the package reader used for reading the local
+// package. It exists so the unified IR linker can refer back to it
+// later.
+var localPkgReader *pkgReader
+
+// unified constructs the local package's Internal Representation (IR)
+// from its syntax tree (AST).
+//
+// The pipeline contains 2 steps:
+//
+// 1. Generate the export data "stub".
+//
+// 2. Generate the IR from the export data above.
+//
+// The package data "stub" at step (1) contains everything from the local package,
+// but nothing that has been imported. When we're actually writing out export data
+// to the output files (see writeNewExport), we run the "linker", which:
+//
+// - Updates compiler extensions data (e.g. inlining cost, escape analysis results).
+//
+// - Handles re-exporting any transitive dependencies.
+//
+// - Prunes out any unnecessary details (e.g. non-inlineable functions, because any
+// downstream importers only care about inlinable functions).
+//
+// The source files are typechecked twice: once before writing the export data
+// using types2, and again after reading the export data using gc/typecheck.
+// The duplication of work will go away once we only use the types2 type checker,
+// removing the gc/typecheck step. For now, it is kept because:
+//
+// - It reduces the engineering costs in maintaining a fork of typecheck
+// (e.g. no need to backport fixes like CL 327651).
+//
+// - It makes it easier to pass toolstash -cmp.
+//
+// - Historically, we would always re-run the typechecker after importing a package,
+// even though we know the imported data is valid. It's not ideal, but it's
+// not causing any problems either.
+//
+// - gc/typecheck is still in charge of some transformations, such as rewriting
+// multi-valued function calls or transforming ir.OINDEX to ir.OINDEXMAP.
+//
+// Using the syntax tree with types2, which has a complete representation of generics,
+// the unified IR has the full typed AST needed for introspection during step (1).
+// In other words, we have all the necessary information to build the generic IR form
+// (see writer.captureVars for an example).
+func unified(noders []*noder) {
+ inline.InlineCall = unifiedInlineCall
+ typecheck.HaveInlineBody = unifiedHaveInlineBody
+
+ data := writePkgStub(noders)
+
+ // We already passed base.Flag.Lang to types2 to handle validating
+ // the user's source code. Bump it up now to the current version and
+ // re-parse, so typecheck doesn't complain if we construct IR that
+ // utilizes newer Go features.
+ base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version)
+ types.ParseLangFlag()
+
+ target := typecheck.Target
+
+ typecheck.TypecheckAllowed = true
+
+ localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
+ readPackage(localPkgReader, types.LocalPkg, true)
+
+ r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
+ r.pkgInit(types.LocalPkg, target)
+
+ // Type-check any top-level assignments. We ignore non-assignments
+ // here because other declarations are typechecked as they're
+ // constructed.
+ for i, ndecls := 0, len(target.Decls); i < ndecls; i++ {
+ switch n := target.Decls[i]; n.Op() {
+ case ir.OAS, ir.OAS2:
+ target.Decls[i] = typecheck.Stmt(n)
+ }
+ }
+
+ readBodies(target, false)
+
+ // Check that nothing snuck past typechecking.
+ for _, n := range target.Decls {
+ if n.Typecheck() == 0 {
+ base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
+ }
+
+ // For functions, check that at least their first statement (if
+ // any) was typechecked too.
+ if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 {
+ if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
+ base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
+ }
+ }
+ }
+
+ base.ExitIfErrors() // just in case
+}
+
+// readBodies iteratively expands all pending dictionaries and
+// function bodies.
+//
+// If duringInlining is true, then the inline.InlineDecls is called as
+// necessary on instantiations of imported generic functions, so their
+// inlining costs can be computed.
+func readBodies(target *ir.Package, duringInlining bool) {
+ var inlDecls []ir.Node
+
+ // Don't use range--bodyIdx can add closures to todoBodies.
+ for {
+ // The order we expand dictionaries and bodies doesn't matter, so
+ // pop from the end to reduce todoBodies reallocations if it grows
+ // further.
+ //
+ // However, we do at least need to flush any pending dictionaries
+ // before reading bodies, because bodies might reference the
+ // dictionaries.
+
+ if len(todoDicts) > 0 {
+ fn := todoDicts[len(todoDicts)-1]
+ todoDicts = todoDicts[:len(todoDicts)-1]
+ fn()
+ continue
+ }
+
+ if len(todoBodies) > 0 {
+ fn := todoBodies[len(todoBodies)-1]
+ todoBodies = todoBodies[:len(todoBodies)-1]
+
+ pri, ok := bodyReader[fn]
+ assert(ok)
+ pri.funcBody(fn)
+
+ // Instantiated generic function: add to Decls for typechecking
+ // and compilation.
+ if fn.OClosure == nil && len(pri.dict.targs) != 0 {
+ // cmd/link does not support a type symbol referencing a method symbol
+ // across DSO boundary, so force re-compiling methods on a generic type
+ // even it was seen from imported package in linkshared mode, see #58966.
+ canSkipNonGenericMethod := !(base.Ctxt.Flag_linkshared && ir.IsMethod(fn))
+ if duringInlining && canSkipNonGenericMethod {
+ inlDecls = append(inlDecls, fn)
+ } else {
+ target.Decls = append(target.Decls, fn)
+ }
+ }
+
+ continue
+ }
+
+ break
+ }
+
+ todoDicts = nil
+ todoBodies = nil
+
+ if len(inlDecls) != 0 {
+ // If we instantiated any generic functions during inlining, we need
+ // to call CanInline on them so they'll be transitively inlined
+ // correctly (#56280).
+ //
+ // We know these functions were already compiled in an imported
+ // package though, so we don't need to actually apply InlineCalls or
+ // save the function bodies any further than this.
+ //
+ // We can also lower the -m flag to 0, to suppress duplicate "can
+ // inline" diagnostics reported against the imported package. Again,
+ // we already reported those diagnostics in the original package, so
+ // it's pointless repeating them here.
+
+ oldLowerM := base.Flag.LowerM
+ base.Flag.LowerM = 0
+ inline.InlineDecls(nil, inlDecls, false)
+ base.Flag.LowerM = oldLowerM
+
+ for _, fn := range inlDecls {
+ fn.(*ir.Func).Body = nil // free memory
+ }
+ }
+}
+
+// writePkgStub type checks the given parsed source files,
+// writes an export data package stub representing them,
+// and returns the result.
+func writePkgStub(noders []*noder) string {
+ m, pkg, info := checkFiles(noders)
+
+ pw := newPkgWriter(m, pkg, info)
+
+ pw.collectDecls(noders)
+
+ publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic)
+ privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate)
+
+ assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
+ assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
+
+ {
+ w := publicRootWriter
+ w.pkg(pkg)
+ w.Bool(false) // TODO(mdempsky): Remove; was "has init"
+
+ scope := pkg.Scope()
+ names := scope.Names()
+ w.Len(len(names))
+ for _, name := range names {
+ w.obj(scope.Lookup(name), nil)
+ }
+
+ w.Sync(pkgbits.SyncEOF)
+ w.Flush()
+ }
+
+ {
+ w := privateRootWriter
+ w.pkgInit(noders)
+ w.Flush()
+ }
+
+ var sb strings.Builder
+ pw.DumpTo(&sb)
+
+ // At this point, we're done with types2. Make sure the package is
+ // garbage collected.
+ freePackage(pkg)
+
+ return sb.String()
+}
+
+// freePackage ensures the given package is garbage collected.
+func freePackage(pkg *types2.Package) {
+ // The GC test below relies on a precise GC that runs finalizers as
+ // soon as objects are unreachable. Our implementation provides
+ // this, but other/older implementations may not (e.g., Go 1.4 does
+ // not because of #22350). To avoid imposing unnecessary
+ // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test
+ // during bootstrapping.
+ if base.CompilerBootstrap || base.Debug.GCCheck == 0 {
+ *pkg = types2.Package{}
+ return
+ }
+
+ // Set a finalizer on pkg so we can detect if/when it's collected.
+ done := make(chan struct{})
+ runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) })
+
+ // Important: objects involved in cycles are not finalized, so zero
+ // out pkg to break its cycles and allow the finalizer to run.
+ *pkg = types2.Package{}
+
+ // It typically takes just 1 or 2 cycles to release pkg, but it
+ // doesn't hurt to try a few more times.
+ for i := 0; i < 10; i++ {
+ select {
+ case <-done:
+ return
+ default:
+ runtime.GC()
+ }
+ }
+
+ base.Fatalf("package never finalized")
+}
+
+// readPackage reads package export data from pr to populate
+// importpkg.
+//
+// localStub indicates whether pr is reading the stub export data for
+// the local package, as opposed to relocated export data for an
+// import.
+func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
+ {
+ r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+
+ pkg := r.pkg()
+ base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg)
+
+ r.Bool() // TODO(mdempsky): Remove; was "has init"
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ r.Sync(pkgbits.SyncObject)
+ assert(!r.Bool())
+ idx := r.Reloc(pkgbits.RelocObj)
+ assert(r.Len() == 0)
+
+ path, name, code := r.p.PeekObj(idx)
+ if code != pkgbits.ObjStub {
+ objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil, nil}
+ }
+ }
+
+ r.Sync(pkgbits.SyncEOF)
+ }
+
+ if !localStub {
+ r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
+
+ if r.Bool() {
+ sym := importpkg.Lookup(".inittask")
+ task := ir.NewNameAt(src.NoXPos, sym)
+ task.Class = ir.PEXTERN
+ sym.Def = task
+ }
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ path := r.String()
+ name := r.String()
+ idx := r.Reloc(pkgbits.RelocBody)
+
+ sym := types.NewPkg(path, "").Lookup(name)
+ if _, ok := importBodyReader[sym]; !ok {
+ importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil, nil}
+ }
+ }
+
+ r.Sync(pkgbits.SyncEOF)
+ }
+}
+
+// writeUnifiedExport writes to `out` the finalized, self-contained
+// Unified IR export data file for the current compilation unit.
+func writeUnifiedExport(out io.Writer) {
+ l := linker{
+ pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
+
+ pkgs: make(map[string]pkgbits.Index),
+ decls: make(map[*types.Sym]pkgbits.Index),
+ bodies: make(map[*types.Sym]pkgbits.Index),
+ }
+
+ publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
+ privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate)
+ assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
+ assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
+
+ var selfPkgIdx pkgbits.Index
+
+ {
+ pr := localPkgReader
+ r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+
+ r.Sync(pkgbits.SyncPkg)
+ selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg))
+
+ r.Bool() // TODO(mdempsky): Remove; was "has init"
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ r.Sync(pkgbits.SyncObject)
+ assert(!r.Bool())
+ idx := r.Reloc(pkgbits.RelocObj)
+ assert(r.Len() == 0)
+
+ xpath, xname, xtag := pr.PeekObj(idx)
+ assert(xpath == pr.PkgPath())
+ assert(xtag != pkgbits.ObjStub)
+
+ if types.IsExported(xname) {
+ l.relocIdx(pr, pkgbits.RelocObj, idx)
+ }
+ }
+
+ r.Sync(pkgbits.SyncEOF)
+ }
+
+ {
+ var idxs []pkgbits.Index
+ for _, idx := range l.decls {
+ idxs = append(idxs, idx)
+ }
+ sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] })
+
+ w := publicRootWriter
+
+ w.Sync(pkgbits.SyncPkg)
+ w.Reloc(pkgbits.RelocPkg, selfPkgIdx)
+ w.Bool(false) // TODO(mdempsky): Remove; was "has init"
+
+ w.Len(len(idxs))
+ for _, idx := range idxs {
+ w.Sync(pkgbits.SyncObject)
+ w.Bool(false)
+ w.Reloc(pkgbits.RelocObj, idx)
+ w.Len(0)
+ }
+
+ w.Sync(pkgbits.SyncEOF)
+ w.Flush()
+ }
+
+ {
+ type symIdx struct {
+ sym *types.Sym
+ idx pkgbits.Index
+ }
+ var bodies []symIdx
+ for sym, idx := range l.bodies {
+ bodies = append(bodies, symIdx{sym, idx})
+ }
+ sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx })
+
+ w := privateRootWriter
+
+ w.Bool(typecheck.Lookup(".inittask").Def != nil)
+
+ w.Len(len(bodies))
+ for _, body := range bodies {
+ w.String(body.sym.Pkg.Path)
+ w.String(body.sym.Name)
+ w.Reloc(pkgbits.RelocBody, body.idx)
+ }
+
+ w.Sync(pkgbits.SyncEOF)
+ w.Flush()
+ }
+
+ base.Ctxt.Fingerprint = l.pw.DumpTo(out)
+}
diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go
new file mode 100644
index 0000000..baf8bd3
--- /dev/null
+++ b/src/cmd/compile/internal/noder/validate.go
@@ -0,0 +1,132 @@
+// 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/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
new file mode 100644
index 0000000..da5c1e9
--- /dev/null
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -0,0 +1,2735 @@
+// 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 (
+ "fmt"
+ "internal/pkgbits"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// This file implements the Unified IR package writer and defines the
+// Unified IR export data format.
+//
+// Low-level coding details (e.g., byte-encoding of individual
+// primitive values, or handling element bitstreams and
+// cross-references) are handled by internal/pkgbits, so here we only
+// concern ourselves with higher-level worries like mapping Go
+// language constructs into elements.
+
+// There are two central types in the writing process: the "writer"
+// type handles writing out individual elements, while the "pkgWriter"
+// type keeps track of which elements have already been created.
+//
+// For each sort of "thing" (e.g., position, package, object, type)
+// that can be written into the export data, there are generally
+// several methods that work together:
+//
+// - writer.thing handles writing out a *use* of a thing, which often
+// means writing a relocation to that thing's encoded index.
+//
+// - pkgWriter.thingIdx handles reserving an index for a thing, and
+// writing out any elements needed for the thing.
+//
+// - writer.doThing handles writing out the *definition* of a thing,
+// which in general is a mix of low-level coding primitives (e.g.,
+// ints and strings) or uses of other things.
+//
+// A design goal of Unified IR is to have a single, canonical writer
+// implementation, but multiple reader implementations each tailored
+// to their respective needs. For example, within cmd/compile's own
+// backend, inlining is implemented largely by just re-running the
+// function body reading code.
+
+// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo,
+// and better document the file format boundary between public and
+// private data.
+
+// A pkgWriter constructs Unified IR export data from the results of
+// running the types2 type checker on a Go compilation unit.
+type pkgWriter struct {
+ pkgbits.PkgEncoder
+
+ m posMap
+ curpkg *types2.Package
+ info *types2.Info
+
+ // Indices for previously written syntax and types2 things.
+
+ posBasesIdx map[*syntax.PosBase]pkgbits.Index
+ pkgsIdx map[*types2.Package]pkgbits.Index
+ typsIdx map[types2.Type]pkgbits.Index
+ objsIdx map[types2.Object]pkgbits.Index
+
+ // Maps from types2.Objects back to their syntax.Decl.
+
+ funDecls map[*types2.Func]*syntax.FuncDecl
+ typDecls map[*types2.TypeName]typeDeclGen
+
+ // linknames maps package-scope objects to their linker symbol name,
+ // if specified by a //go:linkname directive.
+ linknames map[types2.Object]string
+
+ // cgoPragmas accumulates any //go:cgo_* pragmas that need to be
+ // passed through to cmd/link.
+ cgoPragmas [][]string
+}
+
+// newPkgWriter returns an initialized pkgWriter for the specified
+// package.
+func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
+ return &pkgWriter{
+ PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
+
+ m: m,
+ curpkg: pkg,
+ info: info,
+
+ pkgsIdx: make(map[*types2.Package]pkgbits.Index),
+ objsIdx: make(map[types2.Object]pkgbits.Index),
+ typsIdx: make(map[types2.Type]pkgbits.Index),
+
+ posBasesIdx: make(map[*syntax.PosBase]pkgbits.Index),
+
+ funDecls: make(map[*types2.Func]*syntax.FuncDecl),
+ typDecls: make(map[*types2.TypeName]typeDeclGen),
+
+ linknames: make(map[types2.Object]string),
+ }
+}
+
+// errorf reports a user error about thing p.
+func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) {
+ base.ErrorfAt(pw.m.pos(p), msg, args...)
+}
+
+// fatalf reports an internal compiler error about thing p.
+func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) {
+ base.FatalfAt(pw.m.pos(p), msg, args...)
+}
+
+// unexpected reports a fatal error about a thing of unexpected
+// dynamic type.
+func (pw *pkgWriter) unexpected(what string, p poser) {
+ pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
+}
+
+func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
+ tv := x.GetTypeInfo()
+ if tv.Type == nil {
+ pw.fatalf(x, "missing Types entry: %v", syntax.String(x))
+ }
+ return tv
+}
+func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {
+ tv := x.GetTypeInfo()
+ return tv, tv.Type != nil
+}
+
+// typeOf returns the Type of the given value expression.
+func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type {
+ tv := pw.typeAndValue(expr)
+ if !tv.IsValue() {
+ pw.fatalf(expr, "expected value: %v", syntax.String(expr))
+ }
+ return tv.Type
+}
+
+// A writer provides APIs for writing out an individual element.
+type writer struct {
+ p *pkgWriter
+
+ pkgbits.Encoder
+
+ // sig holds the signature for the current function body, if any.
+ sig *types2.Signature
+
+ // TODO(mdempsky): We should be able to prune localsIdx whenever a
+ // scope closes, and then maybe we can just use the same map for
+ // storing the TypeParams too (as their TypeName instead).
+
+ // localsIdx tracks any local variables declared within this
+ // function body. It's unused for writing out non-body things.
+ localsIdx map[*types2.Var]int
+
+ // closureVars tracks any free variables that are referenced by this
+ // function body. It's unused for writing out non-body things.
+ closureVars []posVar
+ closureVarsIdx map[*types2.Var]int // index of previously seen free variables
+
+ dict *writerDict
+
+ // derived tracks whether the type being written out references any
+ // type parameters. It's unused for writing non-type things.
+ derived bool
+}
+
+// A writerDict tracks types and objects that are used by a declaration.
+type writerDict struct {
+ implicits []*types2.TypeName
+
+ // derived is a slice of type indices for computing derived types
+ // (i.e., types that depend on the declaration's type parameters).
+ derived []derivedInfo
+
+ // derivedIdx maps a Type to its corresponding index within the
+ // derived slice, if present.
+ derivedIdx map[types2.Type]pkgbits.Index
+
+ // These slices correspond to entries in the runtime dictionary.
+ typeParamMethodExprs []writerMethodExprInfo
+ subdicts []objInfo
+ rtypes []typeInfo
+ itabs []itabInfo
+}
+
+type itabInfo struct {
+ typ typeInfo
+ iface typeInfo
+}
+
+// typeParamIndex returns the index of the given type parameter within
+// the dictionary. This may differ from typ.Index() when there are
+// implicit type parameters due to defined types declared within a
+// generic function or method.
+func (dict *writerDict) typeParamIndex(typ *types2.TypeParam) int {
+ for idx, implicit := range dict.implicits {
+ if implicit.Type().(*types2.TypeParam) == typ {
+ return idx
+ }
+ }
+
+ return len(dict.implicits) + typ.Index()
+}
+
+// A derivedInfo represents a reference to an encoded generic Go type.
+type derivedInfo struct {
+ idx pkgbits.Index
+ needed bool // TODO(mdempsky): Remove.
+}
+
+// A typeInfo represents a reference to an encoded Go type.
+//
+// If derived is true, then the typeInfo represents a generic Go type
+// that contains type parameters. In this case, idx is an index into
+// the readerDict.derived{,Types} arrays.
+//
+// Otherwise, the typeInfo represents a non-generic Go type, and idx
+// is an index into the reader.typs array instead.
+type typeInfo struct {
+ idx pkgbits.Index
+ derived bool
+}
+
+// An objInfo represents a reference to an encoded, instantiated (if
+// applicable) Go object.
+type objInfo struct {
+ idx pkgbits.Index // index for the generic function declaration
+ explicits []typeInfo // info for the type arguments
+}
+
+// A selectorInfo represents a reference to an encoded field or method
+// name (i.e., objects that can only be accessed using selector
+// expressions).
+type selectorInfo struct {
+ pkgIdx pkgbits.Index
+ nameIdx pkgbits.Index
+}
+
+// anyDerived reports whether any of info's explicit type arguments
+// are derived types.
+func (info objInfo) anyDerived() bool {
+ for _, explicit := range info.explicits {
+ if explicit.derived {
+ return true
+ }
+ }
+ return false
+}
+
+// equals reports whether info and other represent the same Go object
+// (i.e., same base object and identical type arguments, if any).
+func (info objInfo) equals(other objInfo) bool {
+ if info.idx != other.idx {
+ return false
+ }
+ assert(len(info.explicits) == len(other.explicits))
+ for i, targ := range info.explicits {
+ if targ != other.explicits[i] {
+ return false
+ }
+ }
+ return true
+}
+
+type writerMethodExprInfo struct {
+ typeParamIdx int
+ methodInfo selectorInfo
+}
+
+// typeParamMethodExprIdx returns the index where the given encoded
+// method expression function pointer appears within this dictionary's
+// type parameters method expressions section, adding it if necessary.
+func (dict *writerDict) typeParamMethodExprIdx(typeParamIdx int, methodInfo selectorInfo) int {
+ newInfo := writerMethodExprInfo{typeParamIdx, methodInfo}
+
+ for idx, oldInfo := range dict.typeParamMethodExprs {
+ if oldInfo == newInfo {
+ return idx
+ }
+ }
+
+ idx := len(dict.typeParamMethodExprs)
+ dict.typeParamMethodExprs = append(dict.typeParamMethodExprs, newInfo)
+ return idx
+}
+
+// subdictIdx returns the index where the given encoded object's
+// runtime dictionary appears within this dictionary's subdictionary
+// section, adding it if necessary.
+func (dict *writerDict) subdictIdx(newInfo objInfo) int {
+ for idx, oldInfo := range dict.subdicts {
+ if oldInfo.equals(newInfo) {
+ return idx
+ }
+ }
+
+ idx := len(dict.subdicts)
+ dict.subdicts = append(dict.subdicts, newInfo)
+ return idx
+}
+
+// rtypeIdx returns the index where the given encoded type's
+// *runtime._type value appears within this dictionary's rtypes
+// section, adding it if necessary.
+func (dict *writerDict) rtypeIdx(newInfo typeInfo) int {
+ for idx, oldInfo := range dict.rtypes {
+ if oldInfo == newInfo {
+ return idx
+ }
+ }
+
+ idx := len(dict.rtypes)
+ dict.rtypes = append(dict.rtypes, newInfo)
+ return idx
+}
+
+// itabIdx returns the index where the given encoded type pair's
+// *runtime.itab value appears within this dictionary's itabs section,
+// adding it if necessary.
+func (dict *writerDict) itabIdx(typInfo, ifaceInfo typeInfo) int {
+ newInfo := itabInfo{typInfo, ifaceInfo}
+
+ for idx, oldInfo := range dict.itabs {
+ if oldInfo == newInfo {
+ return idx
+ }
+ }
+
+ idx := len(dict.itabs)
+ dict.itabs = append(dict.itabs, newInfo)
+ return idx
+}
+
+func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer {
+ return &writer{
+ Encoder: pw.NewEncoder(k, marker),
+ p: pw,
+ }
+}
+
+// @@@ Positions
+
+// pos writes the position of p into the element bitstream.
+func (w *writer) pos(p poser) {
+ w.Sync(pkgbits.SyncPos)
+ pos := p.Pos()
+
+ // TODO(mdempsky): Track down the remaining cases here and fix them.
+ if !w.Bool(pos.IsKnown()) {
+ return
+ }
+
+ // TODO(mdempsky): Delta encoding.
+ w.posBase(pos.Base())
+ w.Uint(pos.Line())
+ w.Uint(pos.Col())
+}
+
+// posBase writes a reference to the given PosBase into the element
+// bitstream.
+func (w *writer) posBase(b *syntax.PosBase) {
+ w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b))
+}
+
+// posBaseIdx returns the index for the given PosBase.
+func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index {
+ if idx, ok := pw.posBasesIdx[b]; ok {
+ return idx
+ }
+
+ w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase)
+ w.p.posBasesIdx[b] = w.Idx
+
+ w.String(trimFilename(b))
+
+ if !w.Bool(b.IsFileBase()) {
+ w.pos(b)
+ w.Uint(b.Line())
+ w.Uint(b.Col())
+ }
+
+ return w.Flush()
+}
+
+// @@@ Packages
+
+// pkg writes a use of the given Package into the element bitstream.
+func (w *writer) pkg(pkg *types2.Package) {
+ w.pkgRef(w.p.pkgIdx(pkg))
+}
+
+func (w *writer) pkgRef(idx pkgbits.Index) {
+ w.Sync(pkgbits.SyncPkg)
+ w.Reloc(pkgbits.RelocPkg, idx)
+}
+
+// pkgIdx returns the index for the given package, adding it to the
+// package export data if needed.
+func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index {
+ if idx, ok := pw.pkgsIdx[pkg]; ok {
+ return idx
+ }
+
+ w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
+ pw.pkgsIdx[pkg] = w.Idx
+
+ // The universe and package unsafe need to be handled specially by
+ // importers anyway, so we serialize them using just their package
+ // path. This ensures that readers don't confuse them for
+ // user-defined packages.
+ switch pkg {
+ case nil: // universe
+ w.String("builtin") // same package path used by godoc
+ case types2.Unsafe:
+ w.String("unsafe")
+ default:
+ // TODO(mdempsky): Write out pkg.Path() for curpkg too.
+ var path string
+ if pkg != w.p.curpkg {
+ path = pkg.Path()
+ }
+ base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path)
+ w.String(path)
+ w.String(pkg.Name())
+
+ w.Len(len(pkg.Imports()))
+ for _, imp := range pkg.Imports() {
+ w.pkg(imp)
+ }
+ }
+
+ return w.Flush()
+}
+
+// @@@ Types
+
+var (
+ anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
+ comparableTypeName = types2.Universe.Lookup("comparable").(*types2.TypeName)
+ runeTypeName = types2.Universe.Lookup("rune").(*types2.TypeName)
+)
+
+// typ writes a use of the given type into the bitstream.
+func (w *writer) typ(typ types2.Type) {
+ w.typInfo(w.p.typIdx(typ, w.dict))
+}
+
+// typInfo writes a use of the given type (specified as a typeInfo
+// instead) into the bitstream.
+func (w *writer) typInfo(info typeInfo) {
+ w.Sync(pkgbits.SyncType)
+ if w.Bool(info.derived) {
+ w.Len(int(info.idx))
+ w.derived = true
+ } else {
+ w.Reloc(pkgbits.RelocType, info.idx)
+ }
+}
+
+// typIdx returns the index where the export data description of type
+// can be read back in. If no such index exists yet, it's created.
+//
+// typIdx also reports whether typ is a derived type; that is, whether
+// its identity depends on type parameters.
+func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
+ if idx, ok := pw.typsIdx[typ]; ok {
+ return typeInfo{idx: idx, derived: false}
+ }
+ if dict != nil {
+ if idx, ok := dict.derivedIdx[typ]; ok {
+ return typeInfo{idx: idx, derived: true}
+ }
+ }
+
+ w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
+ w.dict = dict
+
+ switch typ := typ.(type) {
+ default:
+ base.Fatalf("unexpected type: %v (%T)", typ, typ)
+
+ case *types2.Basic:
+ switch kind := typ.Kind(); {
+ case kind == types2.Invalid:
+ base.Fatalf("unexpected types2.Invalid")
+
+ case types2.Typ[kind] == typ:
+ w.Code(pkgbits.TypeBasic)
+ w.Len(int(kind))
+
+ default:
+ // Handle "byte" and "rune" as references to their TypeNames.
+ obj := types2.Universe.Lookup(typ.Name())
+ assert(obj.Type() == typ)
+
+ w.Code(pkgbits.TypeNamed)
+ w.obj(obj, nil)
+ }
+
+ case *types2.Named:
+ obj, targs := splitNamed(typ)
+
+ // Defined types that are declared within a generic function (and
+ // thus have implicit type parameters) are always derived types.
+ if w.p.hasImplicitTypeParams(obj) {
+ w.derived = true
+ }
+
+ w.Code(pkgbits.TypeNamed)
+ w.obj(obj, targs)
+
+ case *types2.TypeParam:
+ w.derived = true
+ w.Code(pkgbits.TypeTypeParam)
+ w.Len(w.dict.typeParamIndex(typ))
+
+ case *types2.Array:
+ w.Code(pkgbits.TypeArray)
+ w.Uint64(uint64(typ.Len()))
+ w.typ(typ.Elem())
+
+ case *types2.Chan:
+ w.Code(pkgbits.TypeChan)
+ w.Len(int(typ.Dir()))
+ w.typ(typ.Elem())
+
+ case *types2.Map:
+ w.Code(pkgbits.TypeMap)
+ w.typ(typ.Key())
+ w.typ(typ.Elem())
+
+ case *types2.Pointer:
+ w.Code(pkgbits.TypePointer)
+ w.typ(typ.Elem())
+
+ case *types2.Signature:
+ base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
+ w.Code(pkgbits.TypeSignature)
+ w.signature(typ)
+
+ case *types2.Slice:
+ w.Code(pkgbits.TypeSlice)
+ w.typ(typ.Elem())
+
+ case *types2.Struct:
+ w.Code(pkgbits.TypeStruct)
+ w.structType(typ)
+
+ case *types2.Interface:
+ // Handle "any" as reference to its TypeName.
+ if typ == anyTypeName.Type() {
+ w.Code(pkgbits.TypeNamed)
+ w.obj(anyTypeName, nil)
+ break
+ }
+
+ w.Code(pkgbits.TypeInterface)
+ w.interfaceType(typ)
+
+ case *types2.Union:
+ w.Code(pkgbits.TypeUnion)
+ w.unionType(typ)
+ }
+
+ if w.derived {
+ idx := pkgbits.Index(len(dict.derived))
+ dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})
+ dict.derivedIdx[typ] = idx
+ return typeInfo{idx: idx, derived: true}
+ }
+
+ pw.typsIdx[typ] = w.Idx
+ return typeInfo{idx: w.Flush(), derived: false}
+}
+
+func (w *writer) structType(typ *types2.Struct) {
+ w.Len(typ.NumFields())
+ for i := 0; i < typ.NumFields(); i++ {
+ f := typ.Field(i)
+ w.pos(f)
+ w.selector(f)
+ w.typ(f.Type())
+ w.String(typ.Tag(i))
+ w.Bool(f.Embedded())
+ }
+}
+
+func (w *writer) unionType(typ *types2.Union) {
+ w.Len(typ.Len())
+ for i := 0; i < typ.Len(); i++ {
+ t := typ.Term(i)
+ w.Bool(t.Tilde())
+ w.typ(t.Type())
+ }
+}
+
+func (w *writer) interfaceType(typ *types2.Interface) {
+ // If typ has no embedded types but it's not a basic interface, then
+ // the natural description we write out below will fail to
+ // reconstruct it.
+ if typ.NumEmbeddeds() == 0 && !typ.IsMethodSet() {
+ // Currently, this can only happen for the underlying Interface of
+ // "comparable", which is needed to handle type declarations like
+ // "type C comparable".
+ assert(typ == comparableTypeName.Type().(*types2.Named).Underlying())
+
+ // Export as "interface{ comparable }".
+ w.Len(0) // NumExplicitMethods
+ w.Len(1) // NumEmbeddeds
+ w.Bool(false) // IsImplicit
+ w.typ(comparableTypeName.Type()) // EmbeddedType(0)
+ return
+ }
+
+ w.Len(typ.NumExplicitMethods())
+ w.Len(typ.NumEmbeddeds())
+
+ if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 {
+ w.Bool(typ.IsImplicit())
+ } else {
+ // Implicit interfaces always have 0 explicit methods and 1
+ // embedded type, so we skip writing out the implicit flag
+ // otherwise as a space optimization.
+ assert(!typ.IsImplicit())
+ }
+
+ for i := 0; i < typ.NumExplicitMethods(); i++ {
+ m := typ.ExplicitMethod(i)
+ sig := m.Type().(*types2.Signature)
+ assert(sig.TypeParams() == nil)
+
+ w.pos(m)
+ w.selector(m)
+ w.signature(sig)
+ }
+
+ for i := 0; i < typ.NumEmbeddeds(); i++ {
+ w.typ(typ.EmbeddedType(i))
+ }
+}
+
+func (w *writer) signature(sig *types2.Signature) {
+ w.Sync(pkgbits.SyncSignature)
+ w.params(sig.Params())
+ w.params(sig.Results())
+ w.Bool(sig.Variadic())
+}
+
+func (w *writer) params(typ *types2.Tuple) {
+ w.Sync(pkgbits.SyncParams)
+ w.Len(typ.Len())
+ for i := 0; i < typ.Len(); i++ {
+ w.param(typ.At(i))
+ }
+}
+
+func (w *writer) param(param *types2.Var) {
+ w.Sync(pkgbits.SyncParam)
+ w.pos(param)
+ w.localIdent(param)
+ w.typ(param.Type())
+}
+
+// @@@ Objects
+
+// obj writes a use of the given object into the bitstream.
+//
+// If obj is a generic object, then explicits are the explicit type
+// arguments used to instantiate it (i.e., used to substitute the
+// object's own declared type parameters).
+func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
+ w.objInfo(w.p.objInstIdx(obj, explicits, w.dict))
+}
+
+// objInfo writes a use of the given encoded object into the
+// bitstream.
+func (w *writer) objInfo(info objInfo) {
+ w.Sync(pkgbits.SyncObject)
+ w.Bool(false) // TODO(mdempsky): Remove; was derived func inst.
+ w.Reloc(pkgbits.RelocObj, info.idx)
+
+ w.Len(len(info.explicits))
+ for _, info := range info.explicits {
+ w.typInfo(info)
+ }
+}
+
+// objInstIdx returns the indices for an object and a corresponding
+// list of type arguments used to instantiate it, adding them to the
+// export data as needed.
+func (pw *pkgWriter) objInstIdx(obj types2.Object, explicits *types2.TypeList, dict *writerDict) objInfo {
+ explicitInfos := make([]typeInfo, explicits.Len())
+ for i := range explicitInfos {
+ explicitInfos[i] = pw.typIdx(explicits.At(i), dict)
+ }
+ return objInfo{idx: pw.objIdx(obj), explicits: explicitInfos}
+}
+
+// objIdx returns the index for the given Object, adding it to the
+// export data as needed.
+func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index {
+ // TODO(mdempsky): Validate that obj is a global object (or a local
+ // defined type, which we hoist to global scope anyway).
+
+ if idx, ok := pw.objsIdx[obj]; ok {
+ return idx
+ }
+
+ dict := &writerDict{
+ derivedIdx: make(map[types2.Type]pkgbits.Index),
+ }
+
+ if isDefinedType(obj) && obj.Pkg() == pw.curpkg {
+ decl, ok := pw.typDecls[obj.(*types2.TypeName)]
+ assert(ok)
+ dict.implicits = decl.implicits
+ }
+
+ // We encode objects into 4 elements across different sections, all
+ // sharing the same index:
+ //
+ // - RelocName has just the object's qualified name (i.e.,
+ // Object.Pkg and Object.Name) and the CodeObj indicating what
+ // specific type of Object it is (Var, Func, etc).
+ //
+ // - RelocObj has the remaining public details about the object,
+ // relevant to go/types importers.
+ //
+ // - RelocObjExt has additional private details about the object,
+ // which are only relevant to cmd/compile itself. This is
+ // separated from RelocObj so that go/types importers are
+ // unaffected by internal compiler changes.
+ //
+ // - RelocObjDict has public details about the object's type
+ // parameters and derived type's used by the object. This is
+ // separated to facilitate the eventual introduction of
+ // shape-based stenciling.
+ //
+ // TODO(mdempsky): Re-evaluate whether RelocName still makes sense
+ // to keep separate from RelocObj.
+
+ w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1)
+ wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1)
+ wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1)
+ wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1)
+
+ pw.objsIdx[obj] = w.Idx // break cycles
+ assert(wext.Idx == w.Idx)
+ assert(wname.Idx == w.Idx)
+ assert(wdict.Idx == w.Idx)
+
+ w.dict = dict
+ wext.dict = dict
+
+ code := w.doObj(wext, obj)
+ w.Flush()
+ wext.Flush()
+
+ wname.qualifiedIdent(obj)
+ wname.Code(code)
+ wname.Flush()
+
+ wdict.objDict(obj, w.dict)
+ wdict.Flush()
+
+ return w.Idx
+}
+
+// doObj writes the RelocObj definition for obj to w, and the
+// RelocObjExt definition to wext.
+func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj {
+ if obj.Pkg() != w.p.curpkg {
+ return pkgbits.ObjStub
+ }
+
+ switch obj := obj.(type) {
+ default:
+ w.p.unexpected("object", obj)
+ panic("unreachable")
+
+ case *types2.Const:
+ w.pos(obj)
+ w.typ(obj.Type())
+ w.Value(obj.Val())
+ return pkgbits.ObjConst
+
+ case *types2.Func:
+ decl, ok := w.p.funDecls[obj]
+ assert(ok)
+ sig := obj.Type().(*types2.Signature)
+
+ w.pos(obj)
+ w.typeParamNames(sig.TypeParams())
+ w.signature(sig)
+ w.pos(decl)
+ wext.funcExt(obj)
+ return pkgbits.ObjFunc
+
+ case *types2.TypeName:
+ if obj.IsAlias() {
+ w.pos(obj)
+ w.typ(obj.Type())
+ return pkgbits.ObjAlias
+ }
+
+ named := obj.Type().(*types2.Named)
+ assert(named.TypeArgs() == nil)
+
+ w.pos(obj)
+ w.typeParamNames(named.TypeParams())
+ wext.typeExt(obj)
+ w.typ(named.Underlying())
+
+ w.Len(named.NumMethods())
+ for i := 0; i < named.NumMethods(); i++ {
+ w.method(wext, named.Method(i))
+ }
+
+ return pkgbits.ObjType
+
+ case *types2.Var:
+ w.pos(obj)
+ w.typ(obj.Type())
+ wext.varExt(obj)
+ return pkgbits.ObjVar
+ }
+}
+
+// objDict writes the dictionary needed for reading the given object.
+func (w *writer) objDict(obj types2.Object, dict *writerDict) {
+ // TODO(mdempsky): Split objDict into multiple entries? reader.go
+ // doesn't care about the type parameter bounds, and reader2.go
+ // doesn't care about referenced functions.
+
+ w.dict = dict // TODO(mdempsky): This is a bit sketchy.
+
+ w.Len(len(dict.implicits))
+
+ tparams := objTypeParams(obj)
+ ntparams := tparams.Len()
+ w.Len(ntparams)
+ for i := 0; i < ntparams; i++ {
+ w.typ(tparams.At(i).Constraint())
+ }
+
+ nderived := len(dict.derived)
+ w.Len(nderived)
+ for _, typ := range dict.derived {
+ w.Reloc(pkgbits.RelocType, typ.idx)
+ w.Bool(typ.needed)
+ }
+
+ // Write runtime dictionary information.
+ //
+ // N.B., the go/types importer reads up to the section, but doesn't
+ // read any further, so it's safe to change. (See TODO above.)
+
+ // For each type parameter, write out whether the constraint is a
+ // basic interface. This is used to determine how aggressively we
+ // can shape corresponding type arguments.
+ //
+ // This is somewhat redundant with writing out the full type
+ // parameter constraints above, but the compiler currently skips
+ // over those. Also, we don't care about the *declared* constraints,
+ // but how the type parameters are actually *used*. E.g., if a type
+ // parameter is constrained to `int | uint` but then never used in
+ // arithmetic/conversions/etc, we could shape those together.
+ for _, implicit := range dict.implicits {
+ tparam := implicit.Type().(*types2.TypeParam)
+ w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet())
+ }
+ for i := 0; i < ntparams; i++ {
+ tparam := tparams.At(i)
+ w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet())
+ }
+
+ w.Len(len(dict.typeParamMethodExprs))
+ for _, info := range dict.typeParamMethodExprs {
+ w.Len(info.typeParamIdx)
+ w.selectorInfo(info.methodInfo)
+ }
+
+ w.Len(len(dict.subdicts))
+ for _, info := range dict.subdicts {
+ w.objInfo(info)
+ }
+
+ w.Len(len(dict.rtypes))
+ for _, info := range dict.rtypes {
+ w.typInfo(info)
+ }
+
+ w.Len(len(dict.itabs))
+ for _, info := range dict.itabs {
+ w.typInfo(info.typ)
+ w.typInfo(info.iface)
+ }
+
+ assert(len(dict.derived) == nderived)
+}
+
+func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
+ w.Sync(pkgbits.SyncTypeParamNames)
+
+ ntparams := tparams.Len()
+ for i := 0; i < ntparams; i++ {
+ tparam := tparams.At(i).Obj()
+ w.pos(tparam)
+ w.localIdent(tparam)
+ }
+}
+
+func (w *writer) method(wext *writer, meth *types2.Func) {
+ decl, ok := w.p.funDecls[meth]
+ assert(ok)
+ sig := meth.Type().(*types2.Signature)
+
+ w.Sync(pkgbits.SyncMethod)
+ w.pos(meth)
+ w.selector(meth)
+ w.typeParamNames(sig.RecvTypeParams())
+ w.param(sig.Recv())
+ w.signature(sig)
+
+ w.pos(decl) // XXX: Hack to workaround linker limitations.
+ wext.funcExt(meth)
+}
+
+// qualifiedIdent writes out the name of an object declared at package
+// scope. (For now, it's also used to refer to local defined types.)
+func (w *writer) qualifiedIdent(obj types2.Object) {
+ w.Sync(pkgbits.SyncSym)
+
+ name := obj.Name()
+ if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
+ decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
+ assert(ok)
+ if decl.gen != 0 {
+ // For local defined types, we embed a scope-disambiguation
+ // number directly into their name. types.SplitVargenSuffix then
+ // knows to look for this.
+ //
+ // TODO(mdempsky): Find a better solution; this is terrible.
+ name = fmt.Sprintf("%s·%v", name, decl.gen)
+ }
+ }
+
+ w.pkg(obj.Pkg())
+ w.String(name)
+}
+
+// TODO(mdempsky): We should be able to omit pkg from both localIdent
+// and selector, because they should always be known from context.
+// However, past frustrations with this optimization in iexport make
+// me a little nervous to try it again.
+
+// localIdent writes the name of a locally declared object (i.e.,
+// objects that can only be accessed by non-qualified name, within the
+// context of a particular function).
+func (w *writer) localIdent(obj types2.Object) {
+ assert(!isGlobal(obj))
+ w.Sync(pkgbits.SyncLocalIdent)
+ w.pkg(obj.Pkg())
+ w.String(obj.Name())
+}
+
+// selector writes the name of a field or method (i.e., objects that
+// can only be accessed using selector expressions).
+func (w *writer) selector(obj types2.Object) {
+ w.selectorInfo(w.p.selectorIdx(obj))
+}
+
+func (w *writer) selectorInfo(info selectorInfo) {
+ w.Sync(pkgbits.SyncSelector)
+ w.pkgRef(info.pkgIdx)
+ w.StringRef(info.nameIdx)
+}
+
+func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo {
+ pkgIdx := pw.pkgIdx(obj.Pkg())
+ nameIdx := pw.StringIdx(obj.Name())
+ return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx}
+}
+
+// @@@ Compiler extensions
+
+func (w *writer) funcExt(obj *types2.Func) {
+ decl, ok := w.p.funDecls[obj]
+ assert(ok)
+
+ // TODO(mdempsky): Extend these pragma validation flags to account
+ // for generics. E.g., linkname probably doesn't make sense at
+ // least.
+
+ pragma := asPragmaFlag(decl.Pragma)
+ if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {
+ w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")
+ }
+
+ if decl.Body != nil {
+ if pragma&ir.Noescape != 0 {
+ w.p.errorf(decl, "can only use //go:noescape with external func implementations")
+ }
+ if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && 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.
+ w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")
+ }
+ } else {
+ if base.Flag.Complete || decl.Name.Value == "init" {
+ // Linknamed functions are allowed to have no body. Hopefully
+ // the linkname target has a body. See issue 23311.
+ if _, ok := w.p.linknames[obj]; !ok {
+ w.p.errorf(decl, "missing function body")
+ }
+ }
+ }
+
+ sig, block := obj.Type().(*types2.Signature), decl.Body
+ body, closureVars := w.p.bodyIdx(sig, block, w.dict)
+ assert(len(closureVars) == 0)
+
+ w.Sync(pkgbits.SyncFuncExt)
+ w.pragmaFlag(pragma)
+ w.linkname(obj)
+ w.Bool(false) // stub extension
+ w.Reloc(pkgbits.RelocBody, body)
+ w.Sync(pkgbits.SyncEOF)
+}
+
+func (w *writer) typeExt(obj *types2.TypeName) {
+ decl, ok := w.p.typDecls[obj]
+ assert(ok)
+
+ w.Sync(pkgbits.SyncTypeExt)
+
+ w.pragmaFlag(asPragmaFlag(decl.Pragma))
+
+ // No LSym.SymIdx info yet.
+ w.Int64(-1)
+ w.Int64(-1)
+}
+
+func (w *writer) varExt(obj *types2.Var) {
+ w.Sync(pkgbits.SyncVarExt)
+ w.linkname(obj)
+}
+
+func (w *writer) linkname(obj types2.Object) {
+ w.Sync(pkgbits.SyncLinkname)
+ w.Int64(-1)
+ w.String(w.p.linknames[obj])
+}
+
+func (w *writer) pragmaFlag(p ir.PragmaFlag) {
+ w.Sync(pkgbits.SyncPragma)
+ w.Int(int(p))
+}
+
+// @@@ Function bodies
+
+// bodyIdx returns the index for the given function body (specified by
+// block), adding it to the export data
+func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) {
+ w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ w.sig = sig
+ w.dict = dict
+
+ w.funcargs(sig)
+ if w.Bool(block != nil) {
+ w.stmts(block.List)
+ w.pos(block.Rbrace)
+ }
+
+ return w.Flush(), w.closureVars
+}
+
+func (w *writer) funcargs(sig *types2.Signature) {
+ do := func(params *types2.Tuple, result bool) {
+ for i := 0; i < params.Len(); i++ {
+ w.funcarg(params.At(i), result)
+ }
+ }
+
+ if recv := sig.Recv(); recv != nil {
+ w.funcarg(recv, false)
+ }
+ do(sig.Params(), false)
+ do(sig.Results(), true)
+}
+
+func (w *writer) funcarg(param *types2.Var, result bool) {
+ if param.Name() != "" || result {
+ w.addLocal(param)
+ }
+}
+
+// addLocal records the declaration of a new local variable.
+func (w *writer) addLocal(obj *types2.Var) {
+ idx := len(w.localsIdx)
+
+ w.Sync(pkgbits.SyncAddLocal)
+ if w.p.SyncMarkers() {
+ w.Int(idx)
+ }
+ w.varDictIndex(obj)
+
+ if w.localsIdx == nil {
+ w.localsIdx = make(map[*types2.Var]int)
+ }
+ w.localsIdx[obj] = idx
+}
+
+// useLocal writes a reference to the given local or free variable
+// into the bitstream.
+func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
+ w.Sync(pkgbits.SyncUseObjLocal)
+
+ if idx, ok := w.localsIdx[obj]; w.Bool(ok) {
+ w.Len(idx)
+ return
+ }
+
+ idx, ok := w.closureVarsIdx[obj]
+ if !ok {
+ if w.closureVarsIdx == nil {
+ w.closureVarsIdx = make(map[*types2.Var]int)
+ }
+ idx = len(w.closureVars)
+ w.closureVars = append(w.closureVars, posVar{pos, obj})
+ w.closureVarsIdx[obj] = idx
+ }
+ w.Len(idx)
+}
+
+func (w *writer) openScope(pos syntax.Pos) {
+ w.Sync(pkgbits.SyncOpenScope)
+ w.pos(pos)
+}
+
+func (w *writer) closeScope(pos syntax.Pos) {
+ w.Sync(pkgbits.SyncCloseScope)
+ w.pos(pos)
+ w.closeAnotherScope()
+}
+
+func (w *writer) closeAnotherScope() {
+ w.Sync(pkgbits.SyncCloseAnotherScope)
+}
+
+// @@@ Statements
+
+// stmt writes the given statement into the function body bitstream.
+func (w *writer) stmt(stmt syntax.Stmt) {
+ var stmts []syntax.Stmt
+ if stmt != nil {
+ stmts = []syntax.Stmt{stmt}
+ }
+ w.stmts(stmts)
+}
+
+func (w *writer) stmts(stmts []syntax.Stmt) {
+ w.Sync(pkgbits.SyncStmts)
+ for _, stmt := range stmts {
+ w.stmt1(stmt)
+ }
+ w.Code(stmtEnd)
+ w.Sync(pkgbits.SyncStmtsEnd)
+}
+
+func (w *writer) stmt1(stmt syntax.Stmt) {
+ switch stmt := stmt.(type) {
+ default:
+ w.p.unexpected("statement", stmt)
+
+ case nil, *syntax.EmptyStmt:
+ return
+
+ case *syntax.AssignStmt:
+ switch {
+ case stmt.Rhs == nil:
+ w.Code(stmtIncDec)
+ w.op(binOps[stmt.Op])
+ w.expr(stmt.Lhs)
+ w.pos(stmt)
+
+ case stmt.Op != 0 && stmt.Op != syntax.Def:
+ w.Code(stmtAssignOp)
+ w.op(binOps[stmt.Op])
+ w.expr(stmt.Lhs)
+ w.pos(stmt)
+
+ var typ types2.Type
+ if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr {
+ typ = w.p.typeOf(stmt.Lhs)
+ }
+ w.implicitConvExpr(typ, stmt.Rhs)
+
+ default:
+ w.assignStmt(stmt, stmt.Lhs, stmt.Rhs)
+ }
+
+ case *syntax.BlockStmt:
+ w.Code(stmtBlock)
+ w.blockStmt(stmt)
+
+ case *syntax.BranchStmt:
+ w.Code(stmtBranch)
+ w.pos(stmt)
+ w.op(branchOps[stmt.Tok])
+ w.optLabel(stmt.Label)
+
+ case *syntax.CallStmt:
+ w.Code(stmtCall)
+ w.pos(stmt)
+ w.op(callOps[stmt.Tok])
+ w.expr(stmt.Call)
+
+ case *syntax.DeclStmt:
+ for _, decl := range stmt.DeclList {
+ w.declStmt(decl)
+ }
+
+ case *syntax.ExprStmt:
+ w.Code(stmtExpr)
+ w.expr(stmt.X)
+
+ case *syntax.ForStmt:
+ w.Code(stmtFor)
+ w.forStmt(stmt)
+
+ case *syntax.IfStmt:
+ w.Code(stmtIf)
+ w.ifStmt(stmt)
+
+ case *syntax.LabeledStmt:
+ w.Code(stmtLabel)
+ w.pos(stmt)
+ w.label(stmt.Label)
+ w.stmt1(stmt.Stmt)
+
+ case *syntax.ReturnStmt:
+ w.Code(stmtReturn)
+ w.pos(stmt)
+
+ resultTypes := w.sig.Results()
+ dstType := func(i int) types2.Type {
+ return resultTypes.At(i).Type()
+ }
+ w.multiExpr(stmt, dstType, unpackListExpr(stmt.Results))
+
+ case *syntax.SelectStmt:
+ w.Code(stmtSelect)
+ w.selectStmt(stmt)
+
+ case *syntax.SendStmt:
+ chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan)
+
+ w.Code(stmtSend)
+ w.pos(stmt)
+ w.expr(stmt.Chan)
+ w.implicitConvExpr(chanType.Elem(), stmt.Value)
+
+ case *syntax.SwitchStmt:
+ w.Code(stmtSwitch)
+ w.switchStmt(stmt)
+ }
+}
+
+func (w *writer) assignList(expr syntax.Expr) {
+ exprs := unpackListExpr(expr)
+ w.Len(len(exprs))
+
+ for _, expr := range exprs {
+ w.assign(expr)
+ }
+}
+
+func (w *writer) assign(expr syntax.Expr) {
+ expr = unparen(expr)
+
+ if name, ok := expr.(*syntax.Name); ok {
+ if name.Value == "_" {
+ w.Code(assignBlank)
+ return
+ }
+
+ if obj, ok := w.p.info.Defs[name]; ok {
+ obj := obj.(*types2.Var)
+
+ w.Code(assignDef)
+ w.pos(obj)
+ w.localIdent(obj)
+ w.typ(obj.Type())
+
+ // TODO(mdempsky): Minimize locals index size by deferring
+ // this until the variables actually come into scope.
+ w.addLocal(obj)
+ return
+ }
+ }
+
+ w.Code(assignExpr)
+ w.expr(expr)
+}
+
+func (w *writer) declStmt(decl syntax.Decl) {
+ switch decl := decl.(type) {
+ default:
+ w.p.unexpected("declaration", decl)
+
+ case *syntax.ConstDecl, *syntax.TypeDecl:
+
+ case *syntax.VarDecl:
+ w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values)
+ }
+}
+
+// assignStmt writes out an assignment for "lhs = rhs".
+func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) {
+ lhs := unpackListExpr(lhs0)
+ rhs := unpackListExpr(rhs0)
+
+ w.Code(stmtAssign)
+ w.pos(pos)
+
+ // As if w.assignList(lhs0).
+ w.Len(len(lhs))
+ for _, expr := range lhs {
+ w.assign(expr)
+ }
+
+ dstType := func(i int) types2.Type {
+ dst := lhs[i]
+
+ // Finding dstType is somewhat involved, because for VarDecl
+ // statements, the Names are only added to the info.{Defs,Uses}
+ // maps, not to info.Types.
+ if name, ok := unparen(dst).(*syntax.Name); ok {
+ if name.Value == "_" {
+ return nil // ok: no implicit conversion
+ } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok {
+ return def.Type()
+ } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok {
+ return use.Type()
+ } else {
+ w.p.fatalf(dst, "cannot find type of destination object: %v", dst)
+ }
+ }
+
+ return w.p.typeOf(dst)
+ }
+
+ w.multiExpr(pos, dstType, rhs)
+}
+
+func (w *writer) blockStmt(stmt *syntax.BlockStmt) {
+ w.Sync(pkgbits.SyncBlockStmt)
+ w.openScope(stmt.Pos())
+ w.stmts(stmt.List)
+ w.closeScope(stmt.Rbrace)
+}
+
+func (w *writer) forStmt(stmt *syntax.ForStmt) {
+ w.Sync(pkgbits.SyncForStmt)
+ w.openScope(stmt.Pos())
+
+ if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
+ w.pos(rang)
+ w.assignList(rang.Lhs)
+ w.expr(rang.X)
+
+ xtyp := w.p.typeOf(rang.X)
+ if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap {
+ w.rtype(xtyp)
+ }
+ {
+ lhs := unpackListExpr(rang.Lhs)
+ assign := func(i int, src types2.Type) {
+ if i >= len(lhs) {
+ return
+ }
+ dst := unparen(lhs[i])
+ if name, ok := dst.(*syntax.Name); ok && name.Value == "_" {
+ return
+ }
+
+ var dstType types2.Type
+ if rang.Def {
+ // For `:=` assignments, the LHS names only appear in Defs,
+ // not Types (as used by typeOf).
+ dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type()
+ } else {
+ dstType = w.p.typeOf(dst)
+ }
+
+ w.convRTTI(src, dstType)
+ }
+
+ keyType, valueType := w.p.rangeTypes(rang.X)
+ assign(0, keyType)
+ assign(1, valueType)
+ }
+
+ } else {
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+ w.optExpr(stmt.Cond)
+ w.stmt(stmt.Post)
+ }
+
+ w.blockStmt(stmt.Body)
+ w.closeAnotherScope()
+}
+
+// rangeTypes returns the types of values produced by ranging over
+// expr.
+func (pw *pkgWriter) rangeTypes(expr syntax.Expr) (key, value types2.Type) {
+ typ := pw.typeOf(expr)
+ switch typ := types2.CoreType(typ).(type) {
+ case *types2.Pointer: // must be pointer to array
+ return types2.Typ[types2.Int], types2.CoreType(typ.Elem()).(*types2.Array).Elem()
+ case *types2.Array:
+ return types2.Typ[types2.Int], typ.Elem()
+ case *types2.Slice:
+ return types2.Typ[types2.Int], typ.Elem()
+ case *types2.Basic:
+ if typ.Info()&types2.IsString != 0 {
+ return types2.Typ[types2.Int], runeTypeName.Type()
+ }
+ case *types2.Map:
+ return typ.Key(), typ.Elem()
+ case *types2.Chan:
+ return typ.Elem(), nil
+ }
+ pw.fatalf(expr, "unexpected range type: %v", typ)
+ panic("unreachable")
+}
+
+func (w *writer) ifStmt(stmt *syntax.IfStmt) {
+ w.Sync(pkgbits.SyncIfStmt)
+ w.openScope(stmt.Pos())
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+ w.expr(stmt.Cond)
+ w.blockStmt(stmt.Then)
+ w.stmt(stmt.Else)
+ w.closeAnotherScope()
+}
+
+func (w *writer) selectStmt(stmt *syntax.SelectStmt) {
+ w.Sync(pkgbits.SyncSelectStmt)
+
+ w.pos(stmt)
+ w.Len(len(stmt.Body))
+ for i, clause := range stmt.Body {
+ if i > 0 {
+ w.closeScope(clause.Pos())
+ }
+ w.openScope(clause.Pos())
+
+ w.pos(clause)
+ w.stmt(clause.Comm)
+ w.stmts(clause.Body)
+ }
+ if len(stmt.Body) > 0 {
+ w.closeScope(stmt.Rbrace)
+ }
+}
+
+func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {
+ w.Sync(pkgbits.SyncSwitchStmt)
+
+ w.openScope(stmt.Pos())
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+
+ var iface, tagType types2.Type
+ if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {
+ iface = w.p.typeOf(guard.X)
+
+ w.pos(guard)
+ if tag := guard.Lhs; w.Bool(tag != nil) {
+ w.pos(tag)
+
+ // Like w.localIdent, but we don't have a types2.Object.
+ w.Sync(pkgbits.SyncLocalIdent)
+ w.pkg(w.p.curpkg)
+ w.String(tag.Value)
+ }
+ w.expr(guard.X)
+ } else {
+ tag := stmt.Tag
+
+ if tag != nil {
+ tagType = w.p.typeOf(tag)
+ } else {
+ tagType = types2.Typ[types2.Bool]
+ }
+
+ // Walk is going to emit comparisons between the tag value and
+ // each case expression, and we want these comparisons to always
+ // have the same type. If there are any case values that can't be
+ // converted to the tag value's type, then convert everything to
+ // `any` instead.
+ Outer:
+ for _, clause := range stmt.Body {
+ for _, cas := range unpackListExpr(clause.Cases) {
+ if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) {
+ tagType = types2.NewInterfaceType(nil, nil)
+ break Outer
+ }
+ }
+ }
+
+ if w.Bool(tag != nil) {
+ w.implicitConvExpr(tagType, tag)
+ }
+ }
+
+ w.Len(len(stmt.Body))
+ for i, clause := range stmt.Body {
+ if i > 0 {
+ w.closeScope(clause.Pos())
+ }
+ w.openScope(clause.Pos())
+
+ w.pos(clause)
+
+ cases := unpackListExpr(clause.Cases)
+ if iface != nil {
+ w.Len(len(cases))
+ for _, cas := range cases {
+ if w.Bool(isNil(w.p, cas)) {
+ continue
+ }
+ w.exprType(iface, cas)
+ }
+ } else {
+ // As if w.exprList(clause.Cases),
+ // but with implicit conversions to tagType.
+
+ w.Sync(pkgbits.SyncExprList)
+ w.Sync(pkgbits.SyncExprs)
+ w.Len(len(cases))
+ for _, cas := range cases {
+ w.implicitConvExpr(tagType, cas)
+ }
+ }
+
+ if obj, ok := w.p.info.Implicits[clause]; ok {
+ // TODO(mdempsky): These pos details are quirkish, but also
+ // necessary so the variable's position is correct for DWARF
+ // scope assignment later. It would probably be better for us to
+ // instead just set the variable's DWARF scoping info earlier so
+ // we can give it the correct position information.
+ pos := clause.Pos()
+ if typs := unpackListExpr(clause.Cases); len(typs) != 0 {
+ pos = typeExprEndPos(typs[len(typs)-1])
+ }
+ w.pos(pos)
+
+ obj := obj.(*types2.Var)
+ w.typ(obj.Type())
+ w.addLocal(obj)
+ }
+
+ w.stmts(clause.Body)
+ }
+ if len(stmt.Body) > 0 {
+ w.closeScope(stmt.Rbrace)
+ }
+
+ w.closeScope(stmt.Rbrace)
+}
+
+func (w *writer) label(label *syntax.Name) {
+ w.Sync(pkgbits.SyncLabel)
+
+ // TODO(mdempsky): Replace label strings with dense indices.
+ w.String(label.Value)
+}
+
+func (w *writer) optLabel(label *syntax.Name) {
+ w.Sync(pkgbits.SyncOptLabel)
+ if w.Bool(label != nil) {
+ w.label(label)
+ }
+}
+
+// @@@ Expressions
+
+// expr writes the given expression into the function body bitstream.
+func (w *writer) expr(expr syntax.Expr) {
+ base.Assertf(expr != nil, "missing expression")
+
+ expr = unparen(expr) // skip parens; unneeded after typecheck
+
+ obj, inst := lookupObj(w.p, expr)
+ targs := inst.TypeArgs
+
+ if tv, ok := w.p.maybeTypeAndValue(expr); ok {
+ if tv.IsType() {
+ w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr))
+ }
+
+ if tv.Value != nil {
+ w.Code(exprConst)
+ w.pos(expr)
+ typ := idealType(tv)
+ assert(typ != nil)
+ w.typ(typ)
+ w.Value(tv.Value)
+
+ // TODO(mdempsky): These details are only important for backend
+ // diagnostics. Explore writing them out separately.
+ w.op(constExprOp(expr))
+ w.String(syntax.String(expr))
+ return
+ }
+
+ if _, isNil := obj.(*types2.Nil); isNil {
+ w.Code(exprNil)
+ w.pos(expr)
+ w.typ(tv.Type)
+ return
+ }
+
+ // With shape types (and particular pointer shaping), we may have
+ // an expression of type "go.shape.*uint8", but need to reshape it
+ // to another shape-identical type to allow use in field
+ // selection, indexing, etc.
+ if typ := tv.Type; !tv.IsBuiltin() && !isTuple(typ) && !isUntyped(typ) {
+ w.Code(exprReshape)
+ w.typ(typ)
+ // fallthrough
+ }
+ }
+
+ if obj != nil {
+ if targs.Len() != 0 {
+ obj := obj.(*types2.Func)
+
+ w.Code(exprFuncInst)
+ w.pos(expr)
+ w.funcInst(obj, targs)
+ return
+ }
+
+ if isGlobal(obj) {
+ w.Code(exprGlobal)
+ w.obj(obj, nil)
+ return
+ }
+
+ obj := obj.(*types2.Var)
+ assert(!obj.IsField())
+
+ w.Code(exprLocal)
+ w.useLocal(expr.Pos(), obj)
+ return
+ }
+
+ switch expr := expr.(type) {
+ default:
+ w.p.unexpected("expression", expr)
+
+ case *syntax.CompositeLit:
+ w.Code(exprCompLit)
+ w.compLit(expr)
+
+ case *syntax.FuncLit:
+ w.Code(exprFuncLit)
+ w.funcLit(expr)
+
+ case *syntax.SelectorExpr:
+ sel, ok := w.p.info.Selections[expr]
+ assert(ok)
+
+ switch sel.Kind() {
+ default:
+ w.p.fatalf(expr, "unexpected selection kind: %v", sel.Kind())
+
+ case types2.FieldVal:
+ w.Code(exprFieldVal)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.selector(sel.Obj())
+
+ case types2.MethodVal:
+ w.Code(exprMethodVal)
+ typ := w.recvExpr(expr, sel)
+ w.pos(expr)
+ w.methodExpr(expr, typ, sel)
+
+ case types2.MethodExpr:
+ w.Code(exprMethodExpr)
+
+ tv := w.p.typeAndValue(expr.X)
+ assert(tv.IsType())
+
+ index := sel.Index()
+ implicits := index[:len(index)-1]
+
+ typ := tv.Type
+ w.typ(typ)
+
+ w.Len(len(implicits))
+ for _, ix := range implicits {
+ w.Len(ix)
+ typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type()
+ }
+
+ recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type()
+ if w.Bool(isPtrTo(typ, recv)) { // need deref
+ typ = recv
+ } else if w.Bool(isPtrTo(recv, typ)) { // need addr
+ typ = recv
+ }
+
+ w.pos(expr)
+ w.methodExpr(expr, typ, sel)
+ }
+
+ case *syntax.IndexExpr:
+ _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation
+
+ xtyp := w.p.typeOf(expr.X)
+
+ var keyType types2.Type
+ if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok {
+ keyType = mapType.Key()
+ }
+
+ w.Code(exprIndex)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.implicitConvExpr(keyType, expr.Index)
+ if keyType != nil {
+ w.rtype(xtyp)
+ }
+
+ case *syntax.SliceExpr:
+ w.Code(exprSlice)
+ w.expr(expr.X)
+ w.pos(expr)
+ for _, n := range &expr.Index {
+ w.optExpr(n)
+ }
+
+ case *syntax.AssertExpr:
+ iface := w.p.typeOf(expr.X)
+
+ w.Code(exprAssert)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.exprType(iface, expr.Type)
+ w.rtype(iface)
+
+ case *syntax.Operation:
+ if expr.Y == nil {
+ w.Code(exprUnaryOp)
+ w.op(unOps[expr.Op])
+ w.pos(expr)
+ w.expr(expr.X)
+ break
+ }
+
+ var commonType types2.Type
+ switch expr.Op {
+ case syntax.Shl, syntax.Shr:
+ // ok: operands are allowed to have different types
+ default:
+ xtyp := w.p.typeOf(expr.X)
+ ytyp := w.p.typeOf(expr.Y)
+ switch {
+ case types2.AssignableTo(xtyp, ytyp):
+ commonType = ytyp
+ case types2.AssignableTo(ytyp, xtyp):
+ commonType = xtyp
+ default:
+ w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp)
+ }
+ }
+
+ w.Code(exprBinaryOp)
+ w.op(binOps[expr.Op])
+ w.implicitConvExpr(commonType, expr.X)
+ w.pos(expr)
+ w.implicitConvExpr(commonType, expr.Y)
+
+ case *syntax.CallExpr:
+ tv := w.p.typeAndValue(expr.Fun)
+ if tv.IsType() {
+ assert(len(expr.ArgList) == 1)
+ assert(!expr.HasDots)
+ w.convertExpr(tv.Type, expr.ArgList[0], false)
+ break
+ }
+
+ var rtype types2.Type
+ if tv.IsBuiltin() {
+ switch obj, _ := lookupObj(w.p, expr.Fun); obj.Name() {
+ case "make":
+ assert(len(expr.ArgList) >= 1)
+ assert(!expr.HasDots)
+
+ w.Code(exprMake)
+ w.pos(expr)
+ w.exprType(nil, expr.ArgList[0])
+ w.exprs(expr.ArgList[1:])
+
+ typ := w.p.typeOf(expr)
+ switch coreType := types2.CoreType(typ).(type) {
+ default:
+ w.p.fatalf(expr, "unexpected core type: %v", coreType)
+ case *types2.Chan:
+ w.rtype(typ)
+ case *types2.Map:
+ w.rtype(typ)
+ case *types2.Slice:
+ w.rtype(sliceElem(typ))
+ }
+
+ return
+
+ case "new":
+ assert(len(expr.ArgList) == 1)
+ assert(!expr.HasDots)
+
+ w.Code(exprNew)
+ w.pos(expr)
+ w.exprType(nil, expr.ArgList[0])
+ return
+
+ case "append":
+ rtype = sliceElem(w.p.typeOf(expr))
+ case "copy":
+ typ := w.p.typeOf(expr.ArgList[0])
+ if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())"
+ typ = tuple.At(0).Type()
+ }
+ rtype = sliceElem(typ)
+ case "delete":
+ typ := w.p.typeOf(expr.ArgList[0])
+ if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())"
+ typ = tuple.At(0).Type()
+ }
+ rtype = typ
+ case "Slice":
+ rtype = sliceElem(w.p.typeOf(expr))
+ }
+ }
+
+ writeFunExpr := func() {
+ fun := unparen(expr.Fun)
+
+ if selector, ok := fun.(*syntax.SelectorExpr); ok {
+ if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
+ w.Bool(true) // method call
+ typ := w.recvExpr(selector, sel)
+ w.methodExpr(selector, typ, sel)
+ return
+ }
+ }
+
+ w.Bool(false) // not a method call (i.e., normal function call)
+
+ if obj, inst := lookupObj(w.p, fun); w.Bool(obj != nil && inst.TypeArgs.Len() != 0) {
+ obj := obj.(*types2.Func)
+
+ w.pos(fun)
+ w.funcInst(obj, inst.TypeArgs)
+ return
+ }
+
+ w.expr(fun)
+ }
+
+ sigType := types2.CoreType(tv.Type).(*types2.Signature)
+ paramTypes := sigType.Params()
+
+ w.Code(exprCall)
+ writeFunExpr()
+ w.pos(expr)
+
+ paramType := func(i int) types2.Type {
+ if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 {
+ return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem()
+ }
+ return paramTypes.At(i).Type()
+ }
+
+ w.multiExpr(expr, paramType, expr.ArgList)
+ w.Bool(expr.HasDots)
+ if rtype != nil {
+ w.rtype(rtype)
+ }
+ }
+}
+
+func sliceElem(typ types2.Type) types2.Type {
+ return types2.CoreType(typ).(*types2.Slice).Elem()
+}
+
+func (w *writer) optExpr(expr syntax.Expr) {
+ if w.Bool(expr != nil) {
+ w.expr(expr)
+ }
+}
+
+// recvExpr writes out expr.X, but handles any implicit addressing,
+// dereferencing, and field selections appropriate for the method
+// selection.
+func (w *writer) recvExpr(expr *syntax.SelectorExpr, sel *types2.Selection) types2.Type {
+ index := sel.Index()
+ implicits := index[:len(index)-1]
+
+ w.Code(exprRecv)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.Len(len(implicits))
+
+ typ := w.p.typeOf(expr.X)
+ for _, ix := range implicits {
+ typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type()
+ w.Len(ix)
+ }
+
+ recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type()
+ if w.Bool(isPtrTo(typ, recv)) { // needs deref
+ typ = recv
+ } else if w.Bool(isPtrTo(recv, typ)) { // needs addr
+ typ = recv
+ }
+
+ return typ
+}
+
+// funcInst writes a reference to an instantiated function.
+func (w *writer) funcInst(obj *types2.Func, targs *types2.TypeList) {
+ info := w.p.objInstIdx(obj, targs, w.dict)
+
+ // Type arguments list contains derived types; we can emit a static
+ // call to the shaped function, but need to dynamically compute the
+ // runtime dictionary pointer.
+ if w.Bool(info.anyDerived()) {
+ w.Len(w.dict.subdictIdx(info))
+ return
+ }
+
+ // Type arguments list is statically known; we can emit a static
+ // call with a statically reference to the respective runtime
+ // dictionary.
+ w.objInfo(info)
+}
+
+// methodExpr writes out a reference to the method selected by
+// expr. sel should be the corresponding types2.Selection, and recv
+// the type produced after any implicit addressing, dereferencing, and
+// field selection. (Note: recv might differ from sel.Obj()'s receiver
+// parameter in the case of interface types, and is needed for
+// handling type parameter methods.)
+func (w *writer) methodExpr(expr *syntax.SelectorExpr, recv types2.Type, sel *types2.Selection) {
+ fun := sel.Obj().(*types2.Func)
+ sig := fun.Type().(*types2.Signature)
+
+ w.typ(recv)
+ w.typ(sig)
+ w.pos(expr)
+ w.selector(fun)
+
+ // Method on a type parameter. These require an indirect call
+ // through the current function's runtime dictionary.
+ if typeParam, ok := recv.(*types2.TypeParam); w.Bool(ok) {
+ typeParamIdx := w.dict.typeParamIndex(typeParam)
+ methodInfo := w.p.selectorIdx(fun)
+
+ w.Len(w.dict.typeParamMethodExprIdx(typeParamIdx, methodInfo))
+ return
+ }
+
+ if isInterface(recv) != isInterface(sig.Recv().Type()) {
+ w.p.fatalf(expr, "isInterface inconsistency: %v and %v", recv, sig.Recv().Type())
+ }
+
+ if !isInterface(recv) {
+ if named, ok := deref2(recv).(*types2.Named); ok {
+ obj, targs := splitNamed(named)
+ info := w.p.objInstIdx(obj, targs, w.dict)
+
+ // Method on a derived receiver type. These can be handled by a
+ // static call to the shaped method, but require dynamically
+ // looking up the appropriate dictionary argument in the current
+ // function's runtime dictionary.
+ if w.p.hasImplicitTypeParams(obj) || info.anyDerived() {
+ w.Bool(true) // dynamic subdictionary
+ w.Len(w.dict.subdictIdx(info))
+ return
+ }
+
+ // Method on a fully known receiver type. These can be handled
+ // by a static call to the shaped method, and with a static
+ // reference to the receiver type's dictionary.
+ if targs.Len() != 0 {
+ w.Bool(false) // no dynamic subdictionary
+ w.Bool(true) // static dictionary
+ w.objInfo(info)
+ return
+ }
+ }
+ }
+
+ w.Bool(false) // no dynamic subdictionary
+ w.Bool(false) // no static dictionary
+}
+
+// multiExpr writes a sequence of expressions, where the i'th value is
+// implicitly converted to dstType(i). It also handles when exprs is a
+// single, multi-valued expression (e.g., the multi-valued argument in
+// an f(g()) call, or the RHS operand in a comma-ok assignment).
+func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) {
+ w.Sync(pkgbits.SyncMultiExpr)
+
+ if len(exprs) == 1 {
+ expr := exprs[0]
+ if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok {
+ assert(tuple.Len() > 1)
+ w.Bool(true) // N:1 assignment
+ w.pos(pos)
+ w.expr(expr)
+
+ w.Len(tuple.Len())
+ for i := 0; i < tuple.Len(); i++ {
+ src := tuple.At(i).Type()
+ // TODO(mdempsky): Investigate not writing src here. I think
+ // the reader should be able to infer it from expr anyway.
+ w.typ(src)
+ if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) {
+ if src == nil || dst == nil {
+ w.p.fatalf(pos, "src is %v, dst is %v", src, dst)
+ }
+ if !types2.AssignableTo(src, dst) {
+ w.p.fatalf(pos, "%v is not assignable to %v", src, dst)
+ }
+ w.typ(dst)
+ w.convRTTI(src, dst)
+ }
+ }
+ return
+ }
+ }
+
+ w.Bool(false) // N:N assignment
+ w.Len(len(exprs))
+ for i, expr := range exprs {
+ w.implicitConvExpr(dstType(i), expr)
+ }
+}
+
+// implicitConvExpr is like expr, but if dst is non-nil and different
+// from expr's type, then an implicit conversion operation is inserted
+// at expr's position.
+func (w *writer) implicitConvExpr(dst types2.Type, expr syntax.Expr) {
+ w.convertExpr(dst, expr, true)
+}
+
+func (w *writer) convertExpr(dst types2.Type, expr syntax.Expr, implicit bool) {
+ src := w.p.typeOf(expr)
+
+ // Omit implicit no-op conversions.
+ identical := dst == nil || types2.Identical(src, dst)
+ if implicit && identical {
+ w.expr(expr)
+ return
+ }
+
+ if implicit && !types2.AssignableTo(src, dst) {
+ w.p.fatalf(expr, "%v is not assignable to %v", src, dst)
+ }
+
+ w.Code(exprConvert)
+ w.Bool(implicit)
+ w.typ(dst)
+ w.pos(expr)
+ w.convRTTI(src, dst)
+ w.Bool(isTypeParam(dst))
+ w.Bool(identical)
+ w.expr(expr)
+}
+
+func (w *writer) compLit(lit *syntax.CompositeLit) {
+ typ := w.p.typeOf(lit)
+
+ w.Sync(pkgbits.SyncCompLit)
+ w.pos(lit)
+ w.typ(typ)
+
+ if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
+ typ = ptr.Elem()
+ }
+ var keyType, elemType types2.Type
+ var structType *types2.Struct
+ switch typ0 := typ; typ := types2.CoreType(typ).(type) {
+ default:
+ w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
+ case *types2.Array:
+ elemType = typ.Elem()
+ case *types2.Map:
+ w.rtype(typ0)
+ keyType, elemType = typ.Key(), typ.Elem()
+ case *types2.Slice:
+ elemType = typ.Elem()
+ case *types2.Struct:
+ structType = typ
+ }
+
+ w.Len(len(lit.ElemList))
+ for i, elem := range lit.ElemList {
+ elemType := elemType
+ if structType != nil {
+ if kv, ok := elem.(*syntax.KeyValueExpr); ok {
+ // use position of expr.Key rather than of elem (which has position of ':')
+ w.pos(kv.Key)
+ i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name))
+ elem = kv.Value
+ } else {
+ w.pos(elem)
+ }
+ elemType = structType.Field(i).Type()
+ w.Len(i)
+ } else {
+ if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
+ // use position of expr.Key rather than of elem (which has position of ':')
+ w.pos(kv.Key)
+ w.implicitConvExpr(keyType, kv.Key)
+ elem = kv.Value
+ }
+ }
+ w.pos(elem)
+ w.implicitConvExpr(elemType, elem)
+ }
+}
+
+func (w *writer) funcLit(expr *syntax.FuncLit) {
+ sig := w.p.typeOf(expr).(*types2.Signature)
+
+ body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict)
+
+ w.Sync(pkgbits.SyncFuncLit)
+ w.pos(expr)
+ w.signature(sig)
+
+ w.Len(len(closureVars))
+ for _, cv := range closureVars {
+ w.pos(cv.pos)
+ w.useLocal(cv.pos, cv.var_)
+ }
+
+ w.Reloc(pkgbits.RelocBody, body)
+}
+
+type posVar struct {
+ pos syntax.Pos
+ var_ *types2.Var
+}
+
+func (w *writer) exprList(expr syntax.Expr) {
+ w.Sync(pkgbits.SyncExprList)
+ w.exprs(unpackListExpr(expr))
+}
+
+func (w *writer) exprs(exprs []syntax.Expr) {
+ w.Sync(pkgbits.SyncExprs)
+ w.Len(len(exprs))
+ for _, expr := range exprs {
+ w.expr(expr)
+ }
+}
+
+// rtype writes information so that the reader can construct an
+// expression of type *runtime._type representing typ.
+func (w *writer) rtype(typ types2.Type) {
+ typ = types2.Default(typ)
+
+ info := w.p.typIdx(typ, w.dict)
+ w.rtypeInfo(info)
+}
+
+func (w *writer) rtypeInfo(info typeInfo) {
+ w.Sync(pkgbits.SyncRType)
+
+ if w.Bool(info.derived) {
+ w.Len(w.dict.rtypeIdx(info))
+ } else {
+ w.typInfo(info)
+ }
+}
+
+// varDictIndex writes out information for populating DictIndex for
+// the ir.Name that will represent obj.
+func (w *writer) varDictIndex(obj *types2.Var) {
+ info := w.p.typIdx(obj.Type(), w.dict)
+ if w.Bool(info.derived) {
+ w.Len(w.dict.rtypeIdx(info))
+ }
+}
+
+func isUntyped(typ types2.Type) bool {
+ basic, ok := typ.(*types2.Basic)
+ return ok && basic.Info()&types2.IsUntyped != 0
+}
+
+func isTuple(typ types2.Type) bool {
+ _, ok := typ.(*types2.Tuple)
+ return ok
+}
+
+func (w *writer) itab(typ, iface types2.Type) {
+ typ = types2.Default(typ)
+ iface = types2.Default(iface)
+
+ typInfo := w.p.typIdx(typ, w.dict)
+ ifaceInfo := w.p.typIdx(iface, w.dict)
+
+ w.rtypeInfo(typInfo)
+ w.rtypeInfo(ifaceInfo)
+ if w.Bool(typInfo.derived || ifaceInfo.derived) {
+ w.Len(w.dict.itabIdx(typInfo, ifaceInfo))
+ }
+}
+
+// convRTTI writes information so that the reader can construct
+// expressions for converting from src to dst.
+func (w *writer) convRTTI(src, dst types2.Type) {
+ w.Sync(pkgbits.SyncConvRTTI)
+ w.itab(src, dst)
+}
+
+func (w *writer) exprType(iface types2.Type, typ syntax.Expr) {
+ base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
+
+ tv := w.p.typeAndValue(typ)
+ assert(tv.IsType())
+
+ w.Sync(pkgbits.SyncExprType)
+ w.pos(typ)
+
+ if w.Bool(iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
+ w.itab(tv.Type, iface)
+ } else {
+ w.rtype(tv.Type)
+
+ info := w.p.typIdx(tv.Type, w.dict)
+ w.Bool(info.derived)
+ }
+}
+
+// isInterface reports whether typ is known to be an interface type.
+// If typ is a type parameter, then isInterface reports an internal
+// compiler error instead.
+func isInterface(typ types2.Type) bool {
+ if _, ok := typ.(*types2.TypeParam); ok {
+ // typ is a type parameter and may be instantiated as either a
+ // concrete or interface type, so the writer can't depend on
+ // knowing this.
+ base.Fatalf("%v is a type parameter", typ)
+ }
+
+ _, ok := typ.Underlying().(*types2.Interface)
+ return ok
+}
+
+// op writes an Op into the bitstream.
+func (w *writer) op(op ir.Op) {
+ // TODO(mdempsky): Remove in favor of explicit codes? Would make
+ // export data more stable against internal refactorings, but low
+ // priority at the moment.
+ assert(op != 0)
+ w.Sync(pkgbits.SyncOp)
+ w.Len(int(op))
+}
+
+// @@@ Package initialization
+
+// Caution: This code is still clumsy, because toolstash -cmp is
+// particularly sensitive to it.
+
+type typeDeclGen struct {
+ *syntax.TypeDecl
+ gen int
+
+ // Implicit type parameters in scope at this type declaration.
+ implicits []*types2.TypeName
+}
+
+type fileImports struct {
+ importedEmbed, importedUnsafe bool
+}
+
+// declCollector is a visitor type that collects compiler-needed
+// information about declarations that types2 doesn't track.
+//
+// Notably, it maps declared types and functions back to their
+// declaration statement, keeps track of implicit type parameters, and
+// assigns unique type "generation" numbers to local defined types.
+type declCollector struct {
+ pw *pkgWriter
+ typegen *int
+ file *fileImports
+ withinFunc bool
+ implicits []*types2.TypeName
+}
+
+func (c *declCollector) withTParams(obj types2.Object) *declCollector {
+ tparams := objTypeParams(obj)
+ n := tparams.Len()
+ if n == 0 {
+ return c
+ }
+
+ copy := *c
+ copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
+ for i := 0; i < n; i++ {
+ copy.implicits = append(copy.implicits, tparams.At(i).Obj())
+ }
+ return &copy
+}
+
+func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
+ pw := c.pw
+
+ switch n := n.(type) {
+ case *syntax.File:
+ pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
+
+ case *syntax.ImportDecl:
+ pw.checkPragmas(n.Pragma, 0, false)
+
+ switch pkgNameOf(pw.info, n).Imported().Path() {
+ case "embed":
+ c.file.importedEmbed = true
+ case "unsafe":
+ c.file.importedUnsafe = true
+ }
+
+ case *syntax.ConstDecl:
+ pw.checkPragmas(n.Pragma, 0, false)
+
+ case *syntax.FuncDecl:
+ pw.checkPragmas(n.Pragma, funcPragmas, false)
+
+ obj := pw.info.Defs[n.Name].(*types2.Func)
+ pw.funDecls[obj] = n
+
+ return c.withTParams(obj)
+
+ case *syntax.TypeDecl:
+ obj := pw.info.Defs[n.Name].(*types2.TypeName)
+ d := typeDeclGen{TypeDecl: n, implicits: c.implicits}
+
+ if n.Alias {
+ pw.checkPragmas(n.Pragma, 0, false)
+ } else {
+ pw.checkPragmas(n.Pragma, 0, false)
+
+ // Assign a unique ID to function-scoped defined types.
+ if c.withinFunc {
+ *c.typegen++
+ d.gen = *c.typegen
+ }
+ }
+
+ pw.typDecls[obj] = d
+
+ // TODO(mdempsky): Omit? Not strictly necessary; only matters for
+ // type declarations within function literals within parameterized
+ // type declarations, but types2 the function literals will be
+ // constant folded away.
+ return c.withTParams(obj)
+
+ case *syntax.VarDecl:
+ pw.checkPragmas(n.Pragma, 0, true)
+
+ if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 {
+ if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil {
+ pw.errorf(p.Embeds[0].Pos, "%s", err)
+ }
+ }
+
+ case *syntax.BlockStmt:
+ if !c.withinFunc {
+ copy := *c
+ copy.withinFunc = true
+ return &copy
+ }
+ }
+
+ return c
+}
+
+func (pw *pkgWriter) collectDecls(noders []*noder) {
+ var typegen int
+ for _, p := range noders {
+ var file fileImports
+
+ syntax.Walk(p.file, &declCollector{
+ pw: pw,
+ typegen: &typegen,
+ file: &file,
+ })
+
+ pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...)
+
+ for _, l := range p.linknames {
+ if !file.importedUnsafe {
+ pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
+ continue
+ }
+
+ switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
+ case *types2.Func, *types2.Var:
+ if _, ok := pw.linknames[obj]; !ok {
+ pw.linknames[obj] = l.remote
+ } else {
+ pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local)
+ }
+
+ default:
+ if types.AllowsGoVersion(1, 18) {
+ pw.errorf(l.pos, "//go:linkname must refer to declared function or variable")
+ }
+ }
+ }
+ }
+}
+
+func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) {
+ if p == nil {
+ return
+ }
+ pragma := p.(*pragmas)
+
+ for _, pos := range pragma.Pos {
+ if pos.Flag&^allowed != 0 {
+ pw.errorf(pos.Pos, "misplaced compiler directive")
+ }
+ }
+
+ if !embedOK {
+ for _, e := range pragma.Embeds {
+ pw.errorf(e.Pos, "misplaced go:embed directive")
+ }
+ }
+}
+
+func (w *writer) pkgInit(noders []*noder) {
+ w.Len(len(w.p.cgoPragmas))
+ for _, cgoPragma := range w.p.cgoPragmas {
+ w.Strings(cgoPragma)
+ }
+
+ w.Sync(pkgbits.SyncDecls)
+ for _, p := range noders {
+ for _, decl := range p.file.DeclList {
+ w.pkgDecl(decl)
+ }
+ }
+ w.Code(declEnd)
+
+ w.Sync(pkgbits.SyncEOF)
+}
+
+func (w *writer) pkgDecl(decl syntax.Decl) {
+ switch decl := decl.(type) {
+ default:
+ w.p.unexpected("declaration", decl)
+
+ case *syntax.ImportDecl:
+
+ case *syntax.ConstDecl:
+ w.Code(declOther)
+ w.pkgObjs(decl.NameList...)
+
+ case *syntax.FuncDecl:
+ if decl.Name.Value == "_" {
+ break // skip blank functions
+ }
+
+ obj := w.p.info.Defs[decl.Name].(*types2.Func)
+ sig := obj.Type().(*types2.Signature)
+
+ if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
+ break // skip generic functions
+ }
+
+ if recv := sig.Recv(); recv != nil {
+ w.Code(declMethod)
+ w.typ(recvBase(recv))
+ w.selector(obj)
+ break
+ }
+
+ w.Code(declFunc)
+ w.pkgObjs(decl.Name)
+
+ case *syntax.TypeDecl:
+ if len(decl.TParamList) != 0 {
+ break // skip generic type decls
+ }
+
+ if decl.Name.Value == "_" {
+ break // skip blank type decls
+ }
+
+ name := w.p.info.Defs[decl.Name].(*types2.TypeName)
+ // Skip type declarations for interfaces that are only usable as
+ // type parameter bounds.
+ if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
+ break
+ }
+
+ w.Code(declOther)
+ w.pkgObjs(decl.Name)
+
+ case *syntax.VarDecl:
+ w.Code(declVar)
+ w.pos(decl)
+ w.pkgObjs(decl.NameList...)
+
+ // TODO(mdempsky): It would make sense to use multiExpr here, but
+ // that results in IR that confuses pkginit/initorder.go. So we
+ // continue using exprList, and let typecheck handle inserting any
+ // implicit conversions. That's okay though, because package-scope
+ // assignments never require dictionaries.
+ w.exprList(decl.Values)
+
+ var embeds []pragmaEmbed
+ if p, ok := decl.Pragma.(*pragmas); ok {
+ embeds = p.Embeds
+ }
+ w.Len(len(embeds))
+ for _, embed := range embeds {
+ w.pos(embed.Pos)
+ w.Strings(embed.Patterns)
+ }
+ }
+}
+
+func (w *writer) pkgObjs(names ...*syntax.Name) {
+ w.Sync(pkgbits.SyncDeclNames)
+ w.Len(len(names))
+
+ for _, name := range names {
+ obj, ok := w.p.info.Defs[name]
+ assert(ok)
+
+ w.Sync(pkgbits.SyncDeclName)
+ w.obj(obj, nil)
+ }
+}
+
+// @@@ Helpers
+
+// hasImplicitTypeParams reports whether obj is a defined type with
+// implicit type parameters (e.g., declared within a generic function
+// or method).
+func (p *pkgWriter) hasImplicitTypeParams(obj *types2.TypeName) bool {
+ if obj.Pkg() == p.curpkg {
+ decl, ok := p.typDecls[obj]
+ assert(ok)
+ if len(decl.implicits) != 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// isDefinedType reports whether obj is a defined type.
+func isDefinedType(obj types2.Object) bool {
+ if obj, ok := obj.(*types2.TypeName); ok {
+ return !obj.IsAlias()
+ }
+ return false
+}
+
+// isGlobal reports whether obj was declared at package scope.
+//
+// Caveat: blank objects are not declared.
+func isGlobal(obj types2.Object) bool {
+ return obj.Parent() == obj.Pkg().Scope()
+}
+
+// lookupObj returns the object that expr refers to, if any. If expr
+// is an explicit instantiation of a generic object, then the instance
+// object is returned as well.
+func lookupObj(p *pkgWriter, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
+ if index, ok := expr.(*syntax.IndexExpr); ok {
+ args := unpackListExpr(index.Index)
+ if len(args) == 1 {
+ tv := p.typeAndValue(args[0])
+ if tv.IsValue() {
+ return // normal index expression
+ }
+ }
+
+ expr = index.X
+ }
+
+ // Strip package qualifier, if present.
+ if sel, ok := expr.(*syntax.SelectorExpr); ok {
+ if !isPkgQual(p.info, sel) {
+ return // normal selector expression
+ }
+ expr = sel.Sel
+ }
+
+ if name, ok := expr.(*syntax.Name); ok {
+ obj = p.info.Uses[name]
+ inst = p.info.Instances[name]
+ }
+ return
+}
+
+// isPkgQual reports whether the given selector expression is a
+// package-qualified identifier.
+func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool {
+ if name, ok := sel.X.(*syntax.Name); ok {
+ _, isPkgName := info.Uses[name].(*types2.PkgName)
+ return isPkgName
+ }
+ return false
+}
+
+// isNil reports whether expr is a (possibly parenthesized) reference
+// to the predeclared nil value.
+func isNil(p *pkgWriter, expr syntax.Expr) bool {
+ tv := p.typeAndValue(expr)
+ return tv.IsNil()
+}
+
+// recvBase returns the base type for the given receiver parameter.
+func recvBase(recv *types2.Var) *types2.Named {
+ typ := recv.Type()
+ if ptr, ok := typ.(*types2.Pointer); ok {
+ typ = ptr.Elem()
+ }
+ return typ.(*types2.Named)
+}
+
+// namesAsExpr returns a list of names as a syntax.Expr.
+func namesAsExpr(names []*syntax.Name) syntax.Expr {
+ if len(names) == 1 {
+ return names[0]
+ }
+
+ exprs := make([]syntax.Expr, len(names))
+ for i, name := range names {
+ exprs[i] = name
+ }
+ return &syntax.ListExpr{ElemList: exprs}
+}
+
+// fieldIndex returns the index of the struct field named by key.
+func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
+ field := info.Uses[key].(*types2.Var)
+
+ for i := 0; i < str.NumFields(); i++ {
+ if str.Field(i) == field {
+ return i
+ }
+ }
+
+ panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str))
+}
+
+// objTypeParams returns the type parameters on the given object.
+func objTypeParams(obj types2.Object) *types2.TypeParamList {
+ switch obj := obj.(type) {
+ case *types2.Func:
+ sig := obj.Type().(*types2.Signature)
+ if sig.Recv() != nil {
+ return sig.RecvTypeParams()
+ }
+ return sig.TypeParams()
+ case *types2.TypeName:
+ if !obj.IsAlias() {
+ return obj.Type().(*types2.Named).TypeParams()
+ }
+ }
+ return nil
+}
+
+// splitNamed decomposes a use of a defined type into its original
+// type definition and the type arguments used to instantiate it.
+func splitNamed(typ *types2.Named) (*types2.TypeName, *types2.TypeList) {
+ base.Assertf(typ.TypeParams().Len() == typ.TypeArgs().Len(), "use of uninstantiated type: %v", typ)
+
+ orig := typ.Origin()
+ base.Assertf(orig.TypeArgs() == nil, "origin %v of %v has type arguments", orig, typ)
+ base.Assertf(typ.Obj() == orig.Obj(), "%v has object %v, but %v has object %v", typ, typ.Obj(), orig, orig.Obj())
+
+ return typ.Obj(), typ.TypeArgs()
+}
+
+func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag {
+ if p == nil {
+ return 0
+ }
+ return p.(*pragmas).Flag
+}
+
+// isPtrTo reports whether from is the type *to.
+func isPtrTo(from, to types2.Type) bool {
+ ptr, ok := from.(*types2.Pointer)
+ return ok && types2.Identical(ptr.Elem(), to)
+}