diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/compile/internal/noder | |
parent | Initial commit. (diff) | |
download | golang-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')
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(¶ms, 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 © +} + +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 © + } + } + + 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) +} |