go/src/cmd/compile/internal/noder/reader.go
Matthew Dempsky f0b1563535 cmd/compile/internal/types: remove unneeded functionality
This CL removes a handful of features that were only needed for the
pre-unified frontends.

In particular, Type.Pkg was a hack for iexport so that
go/types.Var.Pkg could be precisely populated for struct fields and
signature parameters by gcimporter, but it's no longer necessary with
the unified export data format because we now write export data
directly from types2-supplied type descriptors.

Several other features (e.g., OrigType, implicit interfaces, type
parameters on signatures) are no longer relevant to the unified
frontend, because it only uses types1 to represent instantiated
generic types.

Updates #57410.

Change-Id: I84fd1da5e0b65d2ab91d244a7bb593821ee916e7
Reviewed-on: https://go-review.googlesource.com/c/go/+/458622
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2023-01-26 21:56:49 +00:00

3993 lines
108 KiB
Go

// 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"
"path/filepath"
"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())
}
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.
//
// The export data format only ever uses slash paths
// (for cross-operating-system reproducible builds),
// but error messages need to use native paths (backslash on Windows)
// as if they had been specified on the command line.
// (The go command always passes native paths to the compiler.)
const dollarGOROOT = "$GOROOT"
if buildcfg.GOROOT != "" && strings.HasPrefix(filename, dollarGOROOT) {
filename = filepath.FromSlash(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(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 {
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()
_, sym := r.selector()
mtyp := r.signature(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(fields)
}
func (r *reader) structType() *types.Type {
fields := make([]*types.Field, r.Len())
for i := range fields {
pos := r.pos()
_, sym := r.selector()
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(fields)
}
func (r *reader) signature(recv *types.Field) *types.Type {
r.Sync(pkgbits.SyncSignature)
params := r.params()
results := r.params()
if r.Bool() { // variadic
params[len(params)-1].SetIsDDD(true)
}
return types.NewSignature(recv, params, results)
}
func (r *reader) params() []*types.Field {
r.Sync(pkgbits.SyncParams)
fields := make([]*types.Field, r.Len())
for i := range fields {
_, 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(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()
_, sym := r.selector()
r.typeParamNames()
_, recv := r.param()
typ := r.signature(recv)
name := ir.NewNameAt(pos, ir.MethodSym(recv.Type, sym))
setType(name, typ)
name.Func = ir.NewFunc(r.pos())
name.Func.Nname = name
if r.hasTypeParams() {
name.Func.SetDupok(true)
if r.dict.shaped {
typ = shapeSig(name.Func, r.dict)
setType(name, typ)
}
}
rext.funcExt(name, sym)
meth := types.NewField(name.Func.Pos(), sym, typ)
meth.Nname = name
meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0)
return meth
}
func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) {
r.Sync(pkgbits.SyncSym)
pkg = r.pkg()
if name := r.String(); name != "" {
sym = pkg.Lookup(name)
}
return
}
func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) {
r.Sync(pkgbits.SyncLocalIdent)
pkg = r.pkg()
if name := r.String(); name != "" {
sym = pkg.Lookup(name)
}
return
}
func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) {
r.Sync(pkgbits.SyncSelector)
origPkg = r.pkg()
name := r.String()
pkg := origPkg
if types.IsExported(name) {
pkg = types.LocalPkg
}
sym = pkg.Lookup(name)
return
}
func (r *reader) hasTypeParams() bool {
return r.dict.hasTypeParams()
}
func (dict *readerDict) hasTypeParams() bool {
return dict != nil && len(dict.targs) != 0
}
// @@@ Compiler extensions
func (r *reader) funcExt(name *ir.Name, method *types.Sym) {
r.Sync(pkgbits.SyncFuncExt)
name.Class = 0 // so MarkFunc doesn't complain
ir.MarkFunc(name)
fn := name.Func
// XXX: Workaround because linker doesn't know how to copy Pos.
if !fn.Pos().IsKnown() {
fn.SetPos(name.Pos())
}
// Normally, we only compile local functions, which saves redundant compilation work.
// n.Defn is not nil for local functions, and is nil for imported function. But for
// generic functions, we might have an instantiation that no other package has seen before.
// So we need to be conservative and compile it again.
//
// That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function.
// TODO(mdempsky,cuonglm): find a cleaner way to handle this.
if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() {
name.Defn = fn
}
fn.Pragma = r.pragmaFlag()
r.linkname(name)
typecheck.Func(fn)
if r.Bool() {
assert(name.Defn == nil)
fn.ABI = obj.ABI(r.Uint64())
// Escape analysis.
for _, fs := range &types.RecvsParams {
for _, f := range fs(name.Type()).FieldSlice() {
f.Note = r.String()
}
}
if r.Bool() {
fn.Inl = &ir.Inline{
Cost: int32(r.Len()),
CanDelayResults: r.Bool(),
}
}
} else {
r.addBody(name.Func, method)
}
r.Sync(pkgbits.SyncEOF)
}
func (r *reader) typeExt(name *ir.Name) {
r.Sync(pkgbits.SyncTypeExt)
typ := name.Type()
if r.hasTypeParams() {
// Set "RParams" (really type arguments here, not parameters) so
// this type is treated as "fully instantiated". This ensures the
// type descriptor is written out as DUPOK and method wrappers are
// generated even for imported types.
var targs []*types.Type
targs = append(targs, r.dict.targs...)
typ.SetRParams(targs)
}
name.SetPragma(r.pragmaFlag())
typecheck.SetBaseTypeIndex(typ, r.Int64(), r.Int64())
}
func (r *reader) varExt(name *ir.Name) {
r.Sync(pkgbits.SyncVarExt)
r.linkname(name)
}
func (r *reader) linkname(name *ir.Name) {
assert(name.Op() == ir.ONAME)
r.Sync(pkgbits.SyncLinkname)
if idx := r.Int64(); idx >= 0 {
lsym := name.Linksym()
lsym.SymIdx = int32(idx)
lsym.Set(obj.AttrIndexed, true)
} else {
name.Sym().Linkname = r.String()
}
}
func (r *reader) pragmaFlag() ir.PragmaFlag {
r.Sync(pkgbits.SyncPragma)
return ir.PragmaFlag(r.Int())
}
// @@@ Function bodies
// bodyReader tracks where the serialized IR for a local or imported,
// generic function's body can be found.
var bodyReader = map[*ir.Func]pkgReaderIndex{}
// importBodyReader tracks where the serialized IR for an imported,
// static (i.e., non-generic) function body can be read.
var importBodyReader = map[*types.Sym]pkgReaderIndex{}
// bodyReaderFor returns the pkgReaderIndex for reading fn's
// serialized IR, and whether one was found.
func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) {
if fn.Nname.Defn != nil {
pri, ok = bodyReader[fn]
base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available
} else {
pri, ok = importBodyReader[fn.Sym()]
}
return
}
// todoDicts holds the list of dictionaries that still need their
// runtime dictionary objects constructed.
var todoDicts []func()
// todoBodies holds the list of function bodies that still need to be
// constructed.
var todoBodies []*ir.Func
// addBody reads a function body reference from the element bitstream,
// and associates it with fn.
func (r *reader) addBody(fn *ir.Func, method *types.Sym) {
// addBody should only be called for local functions or imported
// generic functions; see comment in funcExt.
assert(fn.Nname.Defn != nil)
idx := r.Reloc(pkgbits.RelocBody)
pri := pkgReaderIndex{r.p, idx, r.dict, method, nil}
bodyReader[fn] = pri
if r.curfn == nil {
todoBodies = append(todoBodies, fn)
return
}
pri.funcBody(fn)
}
func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
r.funcBody(fn)
}
// funcBody reads a function body definition from the element
// bitstream, and populates fn with it.
func (r *reader) funcBody(fn *ir.Func) {
r.curfn = fn
r.closureVars = fn.ClosureVars
if len(r.closureVars) != 0 && r.hasTypeParams() {
r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
}
ir.WithFunc(fn, func() {
r.funcargs(fn)
if r.syntheticBody(fn.Pos()) {
return
}
if !r.Bool() {
return
}
body := r.stmts()
if body == nil {
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))}
}
fn.Body = body
fn.Endlineno = r.pos()
})
r.marker.WriteTo(fn)
}
// syntheticBody adds a synthetic body to r.curfn if appropriate, and
// reports whether it did.
func (r *reader) syntheticBody(pos src.XPos) bool {
if r.synthetic != nil {
r.synthetic(pos, r)
return true
}
// If this function has type parameters and isn't shaped, then we
// just tail call its corresponding shaped variant.
if r.hasTypeParams() && !r.dict.shaped {
r.callShaped(pos)
return true
}
return false
}
// callShaped emits a tail call to r.shapedFn, passing along the
// arguments to the current function.
func (r *reader) callShaped(pos src.XPos) {
shapedObj := r.dict.shapedObj
assert(shapedObj != nil)
var shapedFn ir.Node
if r.methodSym == nil {
// Instantiating a generic function; shapedObj is the shaped
// function itself.
assert(shapedObj.Op() == ir.ONAME && shapedObj.Class == ir.PFUNC)
shapedFn = shapedObj
} else {
// Instantiating a generic type's method; shapedObj is the shaped
// type, so we need to select it's corresponding method.
shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym)
}
recvs, params := r.syntheticArgs(pos)
// Construct the arguments list: receiver (if any), then runtime
// dictionary, and finally normal parameters.
//
// Note: For simplicity, shaped methods are added as normal methods
// on their shaped types. So existing code (e.g., packages ir and
// typecheck) expects the shaped type to appear as the receiver
// parameter (or first parameter, as a method expression). Hence
// putting the dictionary parameter after that is the least invasive
// solution at the moment.
var args ir.Nodes
args.Append(recvs...)
args.Append(typecheck.Expr(ir.NewAddrExpr(pos, r.p.dictNameOf(r.dict))))
args.Append(params...)
r.syntheticTailCall(pos, shapedFn, args)
}
// syntheticArgs returns the recvs and params arguments passed to the
// current function.
func (r *reader) syntheticArgs(pos src.XPos) (recvs, params ir.Nodes) {
sig := r.curfn.Nname.Type()
inlVarIdx := 0
addParams := func(out *ir.Nodes, params []*types.Field) {
for _, param := range params {
var arg ir.Node
if param.Nname != nil {
name := param.Nname.(*ir.Name)
if !ir.IsBlank(name) {
if r.inlCall != nil {
// During inlining, we want the respective inlvar where we
// assigned the callee's arguments.
arg = r.inlvars[inlVarIdx]
} else {
// Otherwise, we can use the parameter itself directly.
base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn)
arg = name
}
}
}
// For anonymous and blank parameters, we don't have an *ir.Name
// to use as the argument. However, since we know the shaped
// function won't use the value either, we can just pass the
// zero value. (Also unfortunately, we don't have an easy
// zero-value IR node; so we use a default-initialized temporary
// variable.)
if arg == nil {
tmp := typecheck.TempAt(pos, r.curfn, param.Type)
r.curfn.Body.Append(
typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)),
typecheck.Stmt(ir.NewAssignStmt(pos, tmp, nil)),
)
arg = tmp
}
out.Append(arg)
inlVarIdx++
}
}
addParams(&recvs, sig.Recvs().FieldSlice())
addParams(&params, sig.Params().FieldSlice())
return
}
// syntheticTailCall emits a tail call to fn, passing the given
// arguments list.
func (r *reader) syntheticTailCall(pos src.XPos, fn ir.Node, args ir.Nodes) {
// Mark the function as a wrapper so it doesn't show up in stack
// traces.
r.curfn.SetWrapper(true)
call := typecheck.Call(pos, fn, args, fn.Type().IsVariadic()).(*ir.CallExpr)
var stmt ir.Node
if fn.Type().NumResults() != 0 {
stmt = typecheck.Stmt(ir.NewReturnStmt(pos, []ir.Node{call}))
} else {
stmt = call
}
r.curfn.Body.Append(stmt)
}
// dictNameOf returns the runtime dictionary corresponding to dict.
func (pr *pkgReader) dictNameOf(dict *readerDict) *ir.Name {
pos := base.AutogeneratedPos
// Check that we only instantiate runtime dictionaries with real types.
base.AssertfAt(!dict.shaped, pos, "runtime dictionary of shaped object %v", dict.baseSym)
sym := dict.baseSym.Pkg.Lookup(objabi.GlobalDictPrefix + "." + dict.baseSym.Name)
if sym.Def != nil {
return sym.Def.(*ir.Name)
}
name := ir.NewNameAt(pos, sym)
name.Class = ir.PEXTERN
sym.Def = name // break cycles with mutual subdictionaries
lsym := name.Linksym()
ot := 0
assertOffset := func(section string, offset int) {
base.AssertfAt(ot == offset*types.PtrSize, pos, "writing section %v at offset %v, but it should be at %v*%v", section, ot, offset, types.PtrSize)
}
assertOffset("type param method exprs", dict.typeParamMethodExprsOffset())
for _, info := range dict.typeParamMethodExprs {
typeParam := dict.targs[info.typeParamIdx]
method := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(typeParam), info.method)).(*ir.SelectorExpr)
assert(method.Op() == ir.OMETHEXPR)
rsym := method.FuncName().Linksym()
assert(rsym.ABI() == obj.ABIInternal) // must be ABIInternal; see ir.OCFUNC in ssagen/ssa.go
ot = objw.SymPtr(lsym, ot, rsym, 0)
}
assertOffset("subdictionaries", dict.subdictsOffset())
for _, info := range dict.subdicts {
explicits := pr.typListIdx(info.explicits, dict)
// Careful: Due to subdictionary cycles, name may not be fully
// initialized yet.
name := pr.objDictName(info.idx, dict.targs, explicits)
ot = objw.SymPtr(lsym, ot, name.Linksym(), 0)
}
assertOffset("rtypes", dict.rtypesOffset())
for _, info := range dict.rtypes {
typ := pr.typIdx(info, dict, true)
ot = objw.SymPtr(lsym, ot, reflectdata.TypeLinksym(typ), 0)
// TODO(mdempsky): Double check this.
reflectdata.MarkTypeUsedInInterface(typ, lsym)
}
// For each (typ, iface) pair, we write the *runtime.itab pointer
// for the pair. For pairs that don't actually require an itab
// (i.e., typ is an interface, or iface is an empty interface), we
// write a nil pointer instead. This is wasteful, but rare in
// practice (e.g., instantiating a type parameter with an interface
// type).
assertOffset("itabs", dict.itabsOffset())
for _, info := range dict.itabs {
typ := pr.typIdx(info.typ, dict, true)
iface := pr.typIdx(info.iface, dict, true)
if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() {
ot = objw.SymPtr(lsym, ot, reflectdata.ITabLsym(typ, iface), 0)
} else {
ot += types.PtrSize
}
// TODO(mdempsky): Double check this.
reflectdata.MarkTypeUsedInInterface(typ, lsym)
reflectdata.MarkTypeUsedInInterface(iface, lsym)
}
objw.Global(lsym, int32(ot), obj.DUPOK|obj.RODATA)
name.SetType(dict.varType())
name.SetTypecheck(1)
return name
}
// typeParamMethodExprsOffset returns the offset of the runtime
// dictionary's type parameter method expressions section, in words.
func (dict *readerDict) typeParamMethodExprsOffset() int {
return 0
}
// subdictsOffset returns the offset of the runtime dictionary's
// subdictionary section, in words.
func (dict *readerDict) subdictsOffset() int {
return dict.typeParamMethodExprsOffset() + len(dict.typeParamMethodExprs)
}
// rtypesOffset returns the offset of the runtime dictionary's rtypes
// section, in words.
func (dict *readerDict) rtypesOffset() int {
return dict.subdictsOffset() + len(dict.subdicts)
}
// itabsOffset returns the offset of the runtime dictionary's itabs
// section, in words.
func (dict *readerDict) itabsOffset() int {
return dict.rtypesOffset() + len(dict.rtypes)
}
// numWords returns the total number of words that comprise dict's
// runtime dictionary variable.
func (dict *readerDict) numWords() int64 {
return int64(dict.itabsOffset() + len(dict.itabs))
}
// varType returns the type of dict's runtime dictionary variable.
func (dict *readerDict) varType() *types.Type {
return types.NewArray(types.Types[types.TUINTPTR], dict.numWords())
}
func (r *reader) funcargs(fn *ir.Func) {
sig := fn.Nname.Type()
if recv := sig.Recv(); recv != nil {
r.funcarg(recv, recv.Sym, ir.PPARAM)
}
for _, param := range sig.Params().FieldSlice() {
r.funcarg(param, param.Sym, ir.PPARAM)
}
for i, param := range sig.Results().FieldSlice() {
sym := types.OrigSym(param.Sym)
if sym == nil || sym.IsBlank() {
prefix := "~r"
if r.inlCall != nil {
prefix = "~R"
} else if sym != nil {
prefix = "~b"
}
sym = typecheck.LookupNum(prefix, i)
}
r.funcarg(param, sym, ir.PPARAMOUT)
}
}
func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
if sym == nil {
assert(ctxt == ir.PPARAM)
if r.inlCall != nil {
r.inlvars.Append(ir.BlankNode)
}
return
}
name := ir.NewNameAt(r.inlPos(param.Pos), sym)
setType(name, param.Type)
r.addLocal(name, ctxt)
if r.inlCall == nil {
if !r.funarghack {
param.Sym = sym
param.Nname = name
}
} else {
if ctxt == ir.PPARAMOUT {
r.retvars.Append(name)
} else {
r.inlvars.Append(name)
}
}
}
func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
if name.Sym().Name == dictParamName {
r.dictParam = name
} else {
if r.synthetic == nil {
r.Sync(pkgbits.SyncAddLocal)
if r.p.SyncMarkers() {
want := r.Int()
if have := len(r.locals); have != want {
base.FatalfAt(name.Pos(), "locals table has desynced")
}
}
r.varDictIndex(name)
}
r.locals = append(r.locals, name)
}
name.SetUsed(true)
// TODO(mdempsky): Move earlier.
if ir.IsBlank(name) {
return
}
if r.inlCall != nil {
if ctxt == ir.PAUTO {
name.SetInlLocal(true)
} else {
name.SetInlFormal(true)
ctxt = ir.PAUTO
}
// TODO(mdempsky): Rethink this hack.
if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 {
name.SetPos(r.inlCall.Pos())
name.SetInlFormal(false)
name.SetInlLocal(false)
}
}
name.Class = ctxt
name.Curfn = r.curfn
r.curfn.Dcl = append(r.curfn.Dcl, name)
if ctxt == ir.PAUTO {
name.SetFrameOffset(0)
}
}
func (r *reader) useLocal() *ir.Name {
r.Sync(pkgbits.SyncUseObjLocal)
if r.Bool() {
return r.locals[r.Len()]
}
return r.closureVars[r.Len()]
}
func (r *reader) openScope() {
r.Sync(pkgbits.SyncOpenScope)
pos := r.pos()
if base.Flag.Dwarf {
r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl))
r.marker.Push(pos)
}
}
func (r *reader) closeScope() {
r.Sync(pkgbits.SyncCloseScope)
r.lastCloseScopePos = r.pos()
r.closeAnotherScope()
}
// closeAnotherScope is like closeScope, but it reuses the same mark
// position as the last closeScope call. This is useful for "for" and
// "if" statements, as their implicit blocks always end at the same
// position as an explicit block.
func (r *reader) closeAnotherScope() {
r.Sync(pkgbits.SyncCloseAnotherScope)
if base.Flag.Dwarf {
scopeVars := r.scopeVars[len(r.scopeVars)-1]
r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
// Quirkish: noder decides which scopes to keep before
// typechecking, whereas incremental typechecking during IR
// construction can result in new autotemps being allocated. To
// produce identical output, we ignore autotemps here for the
// purpose of deciding whether to retract the scope.
//
// This is important for net/http/fcgi, because it contains:
//
// var body io.ReadCloser
// if len(content) > 0 {
// body, req.pw = io.Pipe()
// } else { … }
//
// Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
// variable at the call site.
//
// Noder does not preserve the scope where the io.Pipe() call
// resides, because it doesn't contain any declared variables in
// source. So the ~R0 variable ends up being assigned to the
// enclosing scope instead.
//
// However, typechecking this assignment also introduces
// autotemps, because io.Pipe's results need conversion before
// they can be assigned to their respective destination variables.
//
// TODO(mdempsky): We should probably just keep all scopes, and
// let dwarfgen take care of pruning them instead.
retract := true
for _, n := range r.curfn.Dcl[scopeVars:] {
if !n.AutoTemp() {
retract = false
break
}
}
if retract {
// no variables were declared in this scope, so we can retract it.
r.marker.Unpush()
} else {
r.marker.Pop(r.lastCloseScopePos)
}
}
}
// @@@ Statements
func (r *reader) stmt() ir.Node {
switch stmts := r.stmts(); len(stmts) {
case 0:
return nil
case 1:
return stmts[0]
default:
return ir.NewBlockStmt(stmts[0].Pos(), stmts)
}
}
func (r *reader) stmts() []ir.Node {
assert(ir.CurFunc == r.curfn)
var res ir.Nodes
r.Sync(pkgbits.SyncStmts)
for {
tag := codeStmt(r.Code(pkgbits.SyncStmt1))
if tag == stmtEnd {
r.Sync(pkgbits.SyncStmtsEnd)
return res
}
if n := r.stmt1(tag, &res); n != nil {
res.Append(typecheck.Stmt(n))
}
}
}
func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
var label *types.Sym
if n := len(*out); n > 0 {
if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok {
label = ls.Label
}
}
switch tag {
default:
panic("unexpected statement")
case stmtAssign:
pos := r.pos()
names, lhs := r.assignList()
rhs := r.multiExpr()
if len(rhs) == 0 {
for _, name := range names {
as := ir.NewAssignStmt(pos, name, nil)
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
out.Append(typecheck.Stmt(as))
}
return nil
}
if len(lhs) == 1 && len(rhs) == 1 {
n := ir.NewAssignStmt(pos, lhs[0], rhs[0])
n.Def = r.initDefn(n, names)
return n
}
n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
n.Def = r.initDefn(n, names)
return n
case stmtAssignOp:
op := r.op()
lhs := r.expr()
pos := r.pos()
rhs := r.expr()
return ir.NewAssignOpStmt(pos, op, lhs, rhs)
case stmtIncDec:
op := r.op()
lhs := r.expr()
pos := r.pos()
n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one))
n.IncDec = true
return n
case stmtBlock:
out.Append(r.blockStmt()...)
return nil
case stmtBranch:
pos := r.pos()
op := r.op()
sym := r.optLabel()
return ir.NewBranchStmt(pos, op, sym)
case stmtCall:
pos := r.pos()
op := r.op()
call := r.expr()
return ir.NewGoDeferStmt(pos, op, call)
case stmtExpr:
return r.expr()
case stmtFor:
return r.forStmt(label)
case stmtIf:
return r.ifStmt()
case stmtLabel:
pos := r.pos()
sym := r.label()
return ir.NewLabelStmt(pos, sym)
case stmtReturn:
pos := r.pos()
results := r.multiExpr()
return ir.NewReturnStmt(pos, results)
case stmtSelect:
return r.selectStmt(label)
case stmtSend:
pos := r.pos()
ch := r.expr()
value := r.expr()
return ir.NewSendStmt(pos, ch, value)
case stmtSwitch:
return r.switchStmt(label)
}
}
func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
lhs := make([]ir.Node, r.Len())
var names []*ir.Name
for i := range lhs {
expr, def := r.assign()
lhs[i] = expr
if def {
names = append(names, expr.(*ir.Name))
}
}
return names, lhs
}
// assign returns an assignee expression. It also reports whether the
// returned expression is a newly declared variable.
func (r *reader) assign() (ir.Node, bool) {
switch tag := codeAssign(r.Code(pkgbits.SyncAssign)); tag {
default:
panic("unhandled assignee expression")
case assignBlank:
return typecheck.AssignExpr(ir.BlankNode), false
case assignDef:
pos := r.pos()
setBasePos(pos)
_, sym := r.localIdent()
typ := r.typ()
name := ir.NewNameAt(pos, sym)
setType(name, typ)
r.addLocal(name, ir.PAUTO)
return name, true
case assignExpr:
return r.expr(), false
}
}
func (r *reader) blockStmt() []ir.Node {
r.Sync(pkgbits.SyncBlockStmt)
r.openScope()
stmts := r.stmts()
r.closeScope()
return stmts
}
func (r *reader) forStmt(label *types.Sym) ir.Node {
r.Sync(pkgbits.SyncForStmt)
r.openScope()
if r.Bool() {
pos := r.pos()
rang := ir.NewRangeStmt(pos, nil, nil, nil, nil)
rang.Label = label
names, lhs := r.assignList()
if len(lhs) >= 1 {
rang.Key = lhs[0]
if len(lhs) >= 2 {
rang.Value = lhs[1]
}
}
rang.Def = r.initDefn(rang, names)
rang.X = r.expr()
if rang.X.Type().IsMap() {
rang.RType = r.rtype(pos)
}
if rang.Key != nil && !ir.IsBlank(rang.Key) {
rang.KeyTypeWord, rang.KeySrcRType = r.convRTTI(pos)
}
if rang.Value != nil && !ir.IsBlank(rang.Value) {
rang.ValueTypeWord, rang.ValueSrcRType = r.convRTTI(pos)
}
rang.Body = r.blockStmt()
r.closeAnotherScope()
return rang
}
pos := r.pos()
init := r.stmt()
cond := r.optExpr()
post := r.stmt()
body := r.blockStmt()
r.closeAnotherScope()
stmt := ir.NewForStmt(pos, init, cond, post, body)
stmt.Label = label
return stmt
}
func (r *reader) ifStmt() ir.Node {
r.Sync(pkgbits.SyncIfStmt)
r.openScope()
pos := r.pos()
init := r.stmts()
cond := r.expr()
then := r.blockStmt()
els := r.stmts()
n := ir.NewIfStmt(pos, cond, then, els)
n.SetInit(init)
r.closeAnotherScope()
return n
}
func (r *reader) selectStmt(label *types.Sym) ir.Node {
r.Sync(pkgbits.SyncSelectStmt)
pos := r.pos()
clauses := make([]*ir.CommClause, r.Len())
for i := range clauses {
if i > 0 {
r.closeScope()
}
r.openScope()
pos := r.pos()
comm := r.stmt()
body := r.stmts()
// "case i = <-c: ..." may require an implicit conversion (e.g.,
// see fixedbugs/bug312.go). Currently, typecheck throws away the
// implicit conversion and relies on it being reinserted later,
// but that would lose any explicit RTTI operands too. To preserve
// RTTI, we rewrite this as "case tmp := <-c: i = tmp; ...".
if as, ok := comm.(*ir.AssignStmt); ok && as.Op() == ir.OAS && !as.Def {
if conv, ok := as.Y.(*ir.ConvExpr); ok && conv.Op() == ir.OCONVIFACE {
base.AssertfAt(conv.Implicit(), conv.Pos(), "expected implicit conversion: %v", conv)
recv := conv.X
base.AssertfAt(recv.Op() == ir.ORECV, recv.Pos(), "expected receive expression: %v", recv)
tmp := r.temp(pos, recv.Type())
// Replace comm with `tmp := <-c`.
tmpAs := ir.NewAssignStmt(pos, tmp, recv)
tmpAs.Def = true
tmpAs.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
comm = tmpAs
// Change original assignment to `i = tmp`, and prepend to body.
conv.X = tmp
body = append([]ir.Node{as}, body...)
}
}
// multiExpr will have desugared a comma-ok receive expression
// into a separate statement. However, the rest of the compiler
// expects comm to be the OAS2RECV statement itself, so we need to
// shuffle things around to fit that pattern.
if as2, ok := comm.(*ir.AssignListStmt); ok && as2.Op() == ir.OAS2 {
init := ir.TakeInit(as2.Rhs[0])
base.AssertfAt(len(init) == 1 && init[0].Op() == ir.OAS2RECV, as2.Pos(), "unexpected assignment: %+v", as2)
comm = init[0]
body = append([]ir.Node{as2}, body...)
}
clauses[i] = ir.NewCommStmt(pos, comm, body)
}
if len(clauses) > 0 {
r.closeScope()
}
n := ir.NewSelectStmt(pos, clauses)
n.Label = label
return n
}
func (r *reader) switchStmt(label *types.Sym) ir.Node {
r.Sync(pkgbits.SyncSwitchStmt)
r.openScope()
pos := r.pos()
init := r.stmt()
var tag ir.Node
var ident *ir.Ident
var iface *types.Type
if r.Bool() {
pos := r.pos()
if r.Bool() {
pos := r.pos()
_, sym := r.localIdent()
ident = ir.NewIdent(pos, sym)
}
x := r.expr()
iface = x.Type()
tag = ir.NewTypeSwitchGuard(pos, ident, x)
} else {
tag = r.optExpr()
}
clauses := make([]*ir.CaseClause, r.Len())
for i := range clauses {
if i > 0 {
r.closeScope()
}
r.openScope()
pos := r.pos()
var cases, rtypes []ir.Node
if iface != nil {
cases = make([]ir.Node, r.Len())
if len(cases) == 0 {
cases = nil // TODO(mdempsky): Unclear if this matters.
}
for i := range cases {
if r.Bool() { // case nil
cases[i] = typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
} else {
cases[i] = r.exprType()
}
}
} else {
cases = r.exprList()
// For `switch { case any(true): }` (e.g., issue 3980 in
// test/switch.go), the backend still creates a mixed bool/any
// comparison, and we need to explicitly supply the RTTI for the
// comparison.
//
// TODO(mdempsky): Change writer.go to desugar "switch {" into
// "switch true {", which we already handle correctly.
if tag == nil {
for i, cas := range cases {
if cas.Type().IsEmptyInterface() {
for len(rtypes) < i {
rtypes = append(rtypes, nil)
}
rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL]))
}
}
}
}
clause := ir.NewCaseStmt(pos, cases, nil)
clause.RTypes = rtypes
if ident != nil {
pos := r.pos()
typ := r.typ()
name := ir.NewNameAt(pos, ident.Sym())
setType(name, typ)
r.addLocal(name, ir.PAUTO)
clause.Var = name
name.Defn = tag
}
clause.Body = r.stmts()
clauses[i] = clause
}
if len(clauses) > 0 {
r.closeScope()
}
r.closeScope()
n := ir.NewSwitchStmt(pos, tag, clauses)
n.Label = label
if init != nil {
n.SetInit([]ir.Node{init})
}
return n
}
func (r *reader) label() *types.Sym {
r.Sync(pkgbits.SyncLabel)
name := r.String()
if r.inlCall != nil {
name = fmt.Sprintf("~%s·%d", name, inlgen)
}
return typecheck.Lookup(name)
}
func (r *reader) optLabel() *types.Sym {
r.Sync(pkgbits.SyncOptLabel)
if r.Bool() {
return r.label()
}
return nil
}
// initDefn marks the given names as declared by defn and populates
// its Init field with ODCL nodes. It then reports whether any names
// were so declared, which can be used to initialize defn.Def.
func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
if len(names) == 0 {
return false
}
init := make([]ir.Node, len(names))
for i, name := range names {
name.Defn = defn
init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
}
defn.SetInit(init)
return true
}
// @@@ Expressions
// expr reads and returns a typechecked expression.
func (r *reader) expr() (res ir.Node) {
defer func() {
if res != nil && res.Typecheck() == 0 {
base.FatalfAt(res.Pos(), "%v missed typecheck", res)
}
}()
switch tag := codeExpr(r.Code(pkgbits.SyncExpr)); tag {
default:
panic("unhandled expression")
case exprLocal:
return typecheck.Expr(r.useLocal())
case exprGlobal:
// Callee instead of Expr allows builtins
// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
return typecheck.Callee(r.obj())
case exprFuncInst:
pos := r.pos()
wrapperFn, baseFn, dictPtr := r.funcInst(pos)
if wrapperFn != nil {
return wrapperFn
}
return r.curry(pos, 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()
pos := r.pos()
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)
assert(n.Selection == wrapperFn.Selection)
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(pos, 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
}
pos := r.pos()
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(pos, 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(pos 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(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(pos, 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(pos 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(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(pos, typ, false, captured, addBody)
}
// syntheticClosure constructs a synthetic function literal for
// currying dictionary arguments. pos is the position used for the
// closure. 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(pos 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
}
fn := ir.NewClosureFunc(pos, r.curfn != nil)
fn.SetWrapper(true)
clo := fn.OClosure
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(pos, n, &init)
ir.NewClosureVar(pos, 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(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, 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(pos, 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(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) {
// 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(recv, 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(recv, params, results)
}