Newer
Older
// 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 (
"encoding/hex"
"fmt"
"go/constant"
"internal/pkgbits"
"strings"
"cmd/compile/internal/base"
"cmd/compile/internal/dwarfgen"
"cmd/compile/internal/inline"
"cmd/compile/internal/inline/interleaved"
"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/notsha256"
"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 []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([]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 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
func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx index, marker pkgbits.SyncMarker) *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 is used during inlining to suppress setting
// Field.Nname to the inlined copies of the parameters. This is
// necessary because we reuse the same types.Type as the original
// function, and most of the compiler still relies on field.Nname to
// find parameters/results.
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
}
// 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
Matthew Dempsky
committed
derived []derivedInfo // reloc index of the derived type's descriptor
derivedTypes []*types.Type // slice of previously computed derived types
// These slices correspond to entries in the runtime dictionary.
typeParamMethodExprs []readerMethodExprInfo
subdicts []objInfo
rtypes []typeInfo
itabs []itabInfo
}
type readerMethodExprInfo struct {
typeParamIdx int
method *types.Sym
}
func setType(n ir.Node, typ *types.Type) {
n.SetType(typ)
n.SetTypecheck(1)
}
func setValue(name *ir.Name, val constant.Value) {
name.SetVal(val)
name.Defn = nil
}
// @@@ Positions
// pos reads a position from the bitstream.
func (r *reader) pos() src.XPos {
return base.Ctxt.PosTable.XPos(r.pos0())
}
// origPos reads a position from the bitstream, and returns both the
// original raw position and an inlining-adjusted position.
func (r *reader) origPos() (origPos, inlPos src.XPos) {
r.suppressInlPos++
origPos = r.pos()
r.suppressInlPos--
inlPos = r.inlPos(origPos)
return
}
func (r *reader) pos0() src.Pos {
r.Sync(pkgbits.SyncPos)
if !r.Bool() {
return src.NoPos
}
posBase := r.posBase()
line := r.Uint()
col := r.Uint()
return src.MakePos(posBase, line, col)
}
// posBase reads a position base from the bitstream.
func (r *reader) posBase() *src.PosBase {
return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
// posBaseIdx returns the specified position base, reading it first if
// needed.
func (pr *pkgReader) posBaseIdx(idx 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):])
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 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 {
Matthew Dempsky
committed
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)
Matthew Dempsky
committed
}
func (r *reader) typInfo() typeInfo {
r.Sync(pkgbits.SyncType)
if r.Bool() {
return typeInfo{idx: 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 {
Matthew Dempsky
committed
idx := info.idx
var where **types.Type
Matthew Dempsky
committed
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()
if typ == nil {
base.Fatalf("doTyp returned nil for info=%v", info)
}
Cuong Manh Le
committed
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
// 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".
// TODO(mdempsky): Restore consistency check to make sure folding to
// "any" is safe. This is unfortunately tricky, because a pure
// interface can reference impure interfaces too, including
// cyclically (#60117).
if false {
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 {
methods[i] = types.NewField(r.pos(), r.selector(), r.signature(types.FakeRecv()))
}
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 {
field := types.NewField(r.pos(), r.selector(), r.typ())
field.Note = r.String()
if r.Bool() {
field.Embedded = 1
fields[i] = field
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)
params := make([]*types.Field, r.Len())
for i := range params {
params[i] = r.param()
return params
func (r *reader) param() *types.Field {
r.Sync(pkgbits.SyncParam)
return types.NewField(r.pos(), r.localIdent(), r.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)
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
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 index, implicits, explicits []*types.Type, shaped bool) ir.Node {
n, err := pr.objIdxMayFail(idx, implicits, explicits, shaped)
if err != nil {
base.Fatalf("%v", err)
}
return n
}
// objIdxMayFail is equivalent to objIdx, but returns an error rather than
// failing the build if this object requires type arguments and the incorrect
// number of type arguments were passed.
//
// Other sources of internal failure (such as duplicate definitions) still fail
// the build.
func (pr *pkgReader) objIdxMayFail(idx index, implicits, explicits []*types.Type, shaped bool) (ir.Node, error) {
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), nil
}
if pri, ok := objReader[sym]; ok {
return pri.pr.objIdxMayFail(pri.idx, nil, explicits, shaped)
return typecheck.LookupRuntime(sym.Name), nil
base.Fatalf("unresolved stub: %v", sym)
}
dict, err := pr.objDictIdx(sym, idx, implicits, explicits, shaped)
if err != nil {
return nil, err
}
sym = dict.baseSym
if !sym.IsBlank() && sym.Def != nil {
return sym.Def.(*ir.Name), nil
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()
Cuong Manh Le
committed
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)
// Clumsy dance: the r.typ() call here might recursively find this
// type alias name, before we've set its type (#66873). So we
// temporarily clear sym.Def and then restore it later, if still
// unset.
hack := sym.Def == name
if hack {
sym.Def = nil
}
typ := r.typ()
if hack {
if sym.Def != nil {
name = sym.Def.(*ir.Name)
assert(name.Type() == typ)
return name, nil
}
sym.Def = name
}
setType(name, typ)
name.SetAlias(true)
return name, nil
case pkgbits.ObjConst:
name := do(ir.OLITERAL, false)
val := FixValue(typ, r.Value())
setType(name, typ)
setValue(name, val)
return name, nil
case pkgbits.ObjFunc:
if sym.Name == "init" {
npos := r.pos()
setBasePos(npos)
r.typeParamNames()
typ := r.signature(nil)
fpos := r.pos()
fn := ir.NewFunc(fpos, npos, sym, typ)
name := fn.Nname
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
}
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, nil
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.SetMethods(methods)
if !r.dict.shaped {
r.needWrapper(typ)
}
return name, nil
case pkgbits.ObjVar:
name := do(ir.ONAME, false)
setType(name, r.typ())
rext.varExt(name)
return name, nil
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)
}
}
// For fully instantiated shape interface type, use it as-is. Otherwise, the instantiation
// involved recursive generic interface may cause mismatching in function signature, see issue #65362.
if targ.Kind() == types.TINTER && targ.IsFullyInstantiated() && targ.HasShape() {
return 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])
}
// Hash long type names to bound symbol name length seen by users,
// particularly for large protobuf structs (#65030).
uls := under.LinkString()
if base.Debug.MaxShapeLen != 0 &&
len(uls) > base.Debug.MaxShapeLen {
h := notsha256.Sum256([]byte(uls))
uls = hex.EncodeToString(h[:])
}
sym := types.ShapePkg.Lookup(uls)
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 index, implicits, explicits []*types.Type, shaped bool) (*readerDict, error) {
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) {
return nil, fmt.Errorf("%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.