Skip to content
Snippets Groups Projects
reader.go 111 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2021 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package noder
    
    import (
    
    	"internal/buildcfg"
    
    	"path/filepath"
    
    	"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/reflectdata"
    
    	"cmd/compile/internal/staticinit"
    
    	"cmd/compile/internal/typecheck"
    	"cmd/compile/internal/types"
    
    // This file implements cmd/compile backend's reader for the Unified
    // IR export data.
    
    // A pkgReader reads Unified IR export data.
    
    type pkgReader struct {
    
    	// 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.
    
    func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
    
    		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 {
    
    	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)
    
    func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx index, marker pkgbits.SyncMarker) *reader {
    
    		Decoder: pr.NewDecoder(k, idx, marker),
    
    // A reader provides APIs for reading an individual element.
    
    	// 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.
    
    	// 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.
    
    	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) {
    
    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)
    
    	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)
    
    		line := r.Uint()
    		col := r.Uint()
    
    		b = src.NewLinePragmaBase(pos, filename, absFilename, line, col)
    
    // 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()
    
    // doPkg reads a package definition from the bitstream.
    
    func (r *reader) doPkg() *types.Pkg {
    
    	case "builtin":
    		return types.BuiltinPkg
    	case "unsafe":
    		return types.UnsafePkg
    
    
    	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)
    
    	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 {
    
    	if info.derived {
    		where = &dict.derivedTypes[idx]
    		idx = dict.derived[idx].idx
    
    	} else {
    		where = &pr.typs[idx]
    	}
    
    	if typ := *where; typ != nil {
    
    	r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
    
    	if typ == nil {
    		base.Fatalf("doTyp returned nil for info=%v", info)
    	}
    
    	// 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()]
    
    		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)
    
    		return types.NewMap(r.typ(), r.typ())
    
    		return types.NewPtr(r.typ())
    
    	case pkgbits.TypeSignature:
    
    		return types.NewSlice(r.typ())
    
    	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")
    
    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())
    
    		field := types.NewField(r.pos(), r.selector(), r.typ())
    		field.Note = r.String()
    		if r.Bool() {
    			field.Embedded = 1
    
    	return types.NewStruct(fields)
    
    func (r *reader) signature(recv *types.Field) *types.Type {
    
    	r.Sync(pkgbits.SyncSignature)
    
    	params := r.params()
    	results := r.params()
    
    		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()
    
    func (r *reader) param() *types.Field {
    
    	return types.NewField(r.pos(), r.localIdent(), r.typ())
    
    // 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 {
    
    	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)
    
    
    	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)
    
    	tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
    
    	if tag == pkgbits.ObjStub {
    
    		assert(!sym.IsBlank())
    		switch sym.Pkg {
    
    		case types.BuiltinPkg, types.UnsafePkg:
    
    		}
    		if pri, ok := objReader[sym]; ok {
    
    			return pri.pr.objIdxMayFail(pri.idx, nil, explicits, shaped)
    
    		if sym.Pkg.Path == "runtime" {
    
    			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 {
    
    	r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
    	rext := pr.newReader(pkgbits.RelocObjExt, idx, pkgbits.SyncObject1)
    
    	do := func(op ir.Op, hasTParams bool) *ir.Name {
    		pos := r.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")
    
    
    		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 := do(ir.OLITERAL, false)
    
    		val := FixValue(typ, r.Value())
    
    		setType(name, typ)
    		setValue(name, val)
    
    			sym = Renameinit()
    
    		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)
    				})
    			}
    
    		name := do(ir.OTYPE, true)
    		typ := types.NewNamed(name)
    
    		if r.hasTypeParams() && r.dict.shaped {
    			typ.SetHasShape(true)
    		}
    
    
    		// Important: We need to do this before SetUnderlying.
    
    
    		// We need to defer CheckSize until we've called SetUnderlying to
    		// handle recursive types.
    		types.DeferCheckSize()
    
    		typ.SetUnderlying(r.typWrapped(false))
    
    		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 {
    
    		name := do(ir.ONAME, false)
    
    func (dict *readerDict) mangle(sym *types.Sym) *types.Sym {
    	if !dict.hasTypeParams() {
    
    	// 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)
    
    
    	for i, targ := range dict.targs {
    
    	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.
    
    	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)
    
    	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.