Skip to content
Snippets Groups Projects
reader.go 54.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • // UNREVIEWED
    
    // Copyright 2021 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package noder
    
    import (
    	"bytes"
    	"fmt"
    	"go/constant"
    
    	"internal/buildcfg"
    
    	"strings"
    
    	"cmd/compile/internal/base"
    	"cmd/compile/internal/deadcode"
    	"cmd/compile/internal/dwarfgen"
    
    	"cmd/compile/internal/inline"
    
    	"cmd/compile/internal/ir"
    
    	"cmd/compile/internal/reflectdata"
    
    	"cmd/compile/internal/typecheck"
    	"cmd/compile/internal/types"
    	"cmd/internal/obj"
    	"cmd/internal/src"
    )
    
    // TODO(mdempsky): Suppress duplicate type/const errors that can arise
    // during typecheck due to naive type substitution (e.g., see #42758).
    // I anticipate these will be handled as a consequence of adding
    // dictionaries support, so it's probably not important to focus on
    // this until after that's done.
    
    type pkgReader struct {
    	pkgDecoder
    
    	posBases []*src.PosBase
    	pkgs     []*types.Pkg
    	typs     []*types.Type
    
    	// offset for rewriting the given index into the output,
    	// but bitwise inverted so we can detect if we're missing the entry or not.
    	newindex []int
    }
    
    func newPkgReader(pr pkgDecoder) *pkgReader {
    	return &pkgReader{
    		pkgDecoder: pr,
    
    		posBases: make([]*src.PosBase, pr.numElems(relocPosBase)),
    		pkgs:     make([]*types.Pkg, pr.numElems(relocPkg)),
    		typs:     make([]*types.Type, pr.numElems(relocType)),
    
    		newindex: make([]int, pr.totalElems()),
    	}
    }
    
    type pkgReaderIndex struct {
    
    }
    
    func (pri pkgReaderIndex) asReader(k reloc, marker syncMarker) *reader {
    	r := pri.pr.newReader(k, pri.idx, marker)
    
    	return r
    }
    
    func (pr *pkgReader) newReader(k reloc, idx int, marker syncMarker) *reader {
    	return &reader{
    		decoder: pr.newDecoder(k, idx, marker),
    		p:       pr,
    	}
    }
    
    type reader struct {
    	decoder
    
    	p *pkgReader
    
    
    	// 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
    
    	// 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
    
    	delayResults bool
    
    	// Label to return to.
    	retlabel *types.Sym
    
    	inlvars, retvars ir.Nodes
    }
    
    
    type readerDict struct {
    	// 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
    
    	funcs    []objInfo
    	funcsObj []ir.Node
    
    func setType(n ir.Node, typ *types.Type) {
    
    	n.SetType(typ)
    	n.SetTypecheck(1)
    
    	if name, ok := n.(*ir.Name); ok {
    		name.SetWalkdef(1)
    		name.Ntype = ir.TypeNode(name.Type())
    	}
    }
    
    
    func setValue(name *ir.Name, val constant.Value) {
    
    	name.SetVal(val)
    	name.Defn = nil
    }
    
    // @@@ Positions
    
    func (r *reader) pos() src.XPos {
    	return base.Ctxt.PosTable.XPos(r.pos0())
    }
    
    func (r *reader) pos0() src.Pos {
    	r.sync(syncPos)
    	if !r.bool() {
    		return src.NoPos
    	}
    
    	posBase := r.posBase()
    	line := r.uint()
    	col := r.uint()
    	return src.MakePos(posBase, line, col)
    }
    
    func (r *reader) posBase() *src.PosBase {
    	return r.inlPosBase(r.p.posBaseIdx(r.reloc(relocPosBase)))
    }
    
    func (pr *pkgReader) posBaseIdx(idx int) *src.PosBase {
    	if b := pr.posBases[idx]; b != nil {
    		return b
    	}
    
    	r := pr.newReader(relocPosBase, idx, syncPosBase)
    	var b *src.PosBase
    
    
    	absFilename := r.string()
    	filename := absFilename
    
    	// For build artifact stability, the export data format only
    	// contains the "absolute" filename as returned by objabi.AbsFile.
    	// However, some tests (e.g., test/run.go's asmcheck tests) expect
    	// to see the full, original filename printed out. Re-expanding
    	// "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to
    	// satisfy this.
    	//
    	// TODO(mdempsky): De-duplicate this logic with similar logic in
    	// cmd/link/internal/ld's expandGoroot. However, this will probably
    	// require being more consistent about when we use native vs UNIX
    	// file paths.
    	const dollarGOROOT = "$GOROOT"
    	if strings.HasPrefix(filename, dollarGOROOT) {
    		filename = 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
    }
    
    func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
    	if r.inlCall == nil {
    		return oldBase
    	}
    
    	if newBase, ok := r.inlPosBases[oldBase]; ok {
    		return newBase
    	}
    
    	newBase := src.NewInliningBase(oldBase, r.inlTreeIndex)
    	r.inlPosBases[oldBase] = newBase
    	return newBase
    }
    
    func (r *reader) updatePos(xpos src.XPos) src.XPos {
    	pos := base.Ctxt.PosTable.Pos(xpos)
    	pos.SetBase(r.inlPosBase(pos.Base()))
    	return base.Ctxt.PosTable.XPos(pos)
    }
    
    func (r *reader) origPos(xpos src.XPos) src.XPos {
    	if r.inlCall == nil {
    		return xpos
    	}
    
    	pos := base.Ctxt.PosTable.Pos(xpos)
    	for old, new := range r.inlPosBases {
    		if pos.Base() == new {
    			pos.SetBase(old)
    			return base.Ctxt.PosTable.XPos(pos)
    		}
    	}
    
    	base.FatalfAt(xpos, "pos base missing from inlPosBases")
    	panic("unreachable")
    }
    
    // @@@ Packages
    
    func (r *reader) pkg() *types.Pkg {
    	r.sync(syncPkg)
    	return r.p.pkgIdx(r.reloc(relocPkg))
    }
    
    func (pr *pkgReader) pkgIdx(idx int) *types.Pkg {
    	if pkg := pr.pkgs[idx]; pkg != nil {
    		return pkg
    	}
    
    	pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg()
    	pr.pkgs[idx] = pkg
    	return pkg
    }
    
    func (r *reader) doPkg() *types.Pkg {
    	path := r.string()
    	if path == "builtin" {
    		return types.BuiltinPkg
    	}
    	if path == "" {
    		path = r.p.pkgPath
    	}
    
    	name := r.string()
    	height := r.len()
    
    	pkg := types.NewPkg(path, "")
    
    	if pkg.Name == "" {
    		pkg.Name = name
    	} else {
    		assert(pkg.Name == name)
    	}
    
    	if pkg.Height == 0 {
    		pkg.Height = height
    	} else {
    		assert(pkg.Height == height)
    	}
    
    	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)
    
    	return typeInfo{idx: r.reloc(relocType), derived: false}
    
    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 {
    
    		return typ
    	}
    
    	r := pr.newReader(relocType, idx, syncTypeIdx)
    
    	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 := codeType(r.code(syncType)); tag {
    	default:
    		panic(fmt.Sprintf("unexpected type: %v", tag))
    
    	case typeBasic:
    		return *basics[r.len()]
    
    	case typeNamed:
    		obj := r.obj()
    		assert(obj.Op() == ir.OTYPE)
    		return obj.Type()
    
    	case typeTypeParam:
    
    
    	case typeArray:
    		len := int64(r.uint64())
    		return types.NewArray(r.typ(), len)
    	case typeChan:
    		dir := dirs[r.len()]
    		return types.NewChan(r.typ(), dir)
    	case typeMap:
    		return types.NewMap(r.typ(), r.typ())
    	case typePointer:
    		return types.NewPtr(r.typ())
    	case typeSignature:
    		return r.signature(types.LocalPkg, nil)
    	case typeSlice:
    		return types.NewSlice(r.typ())
    	case typeStruct:
    		return r.structType()
    	case typeInterface:
    		return r.interfaceType()
    	}
    }
    
    func (r *reader) interfaceType() *types.Type {
    	tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
    
    	nmethods, nembeddeds := r.len(), r.len()
    
    	fields := make([]*types.Field, nmethods+nembeddeds)
    	methods, embeddeds := fields[:nmethods], fields[nmethods:]
    
    	for i := range methods {
    		pos := r.pos()
    		pkg, sym := r.selector()
    		tpkg = pkg
    
    		mtyp := r.signature(pkg, types.FakeRecv())
    
    		methods[i] = types.NewField(pos, sym, mtyp)
    	}
    	for i := range embeddeds {
    		embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ())
    	}
    
    	if len(fields) == 0 {
    		return types.Types[types.TINTER] // empty interface
    	}
    
    	return types.NewInterface(tpkg, fields, false)
    
    }
    
    func (r *reader) structType() *types.Type {
    	tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
    	fields := make([]*types.Field, r.len())
    	for i := range fields {
    		pos := r.pos()
    		pkg, sym := r.selector()
    		tpkg = pkg
    		ftyp := r.typ()
    		tag := r.string()
    		embedded := r.bool()
    
    		f := types.NewField(pos, sym, ftyp)
    		f.Note = tag
    		if embedded {
    			f.Embedded = 1
    		}
    		fields[i] = f
    	}
    
    	return types.NewStruct(tpkg, fields)
    
    }
    
    func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
    	r.sync(syncSignature)
    
    	params := r.params(&tpkg)
    	results := r.params(&tpkg)
    	if r.bool() { // variadic
    		params[len(params)-1].SetIsDDD(true)
    	}
    
    	return types.NewSignature(tpkg, recv, nil, params, results)
    }
    
    func (r *reader) params(tpkg **types.Pkg) []*types.Field {
    	r.sync(syncParams)
    	fields := make([]*types.Field, r.len())
    	for i := range fields {
    		*tpkg, fields[i] = r.param()
    	}
    	return fields
    }
    
    func (r *reader) param() (*types.Pkg, *types.Field) {
    	r.sync(syncParam)
    
    	pos := r.pos()
    	pkg, sym := r.localIdent()
    	typ := r.typ()
    
    	return pkg, types.NewField(pos, sym, typ)
    }
    
    // @@@ Objects
    
    var objReader = map[*types.Sym]pkgReaderIndex{}
    
    func (r *reader) obj() ir.Node {
    	r.sync(syncObject)
    
    
    	if r.bool() {
    		idx := r.len()
    		obj := r.dict.funcsObj[idx]
    		if obj == nil {
    			fn := r.dict.funcs[idx]
    			targs := make([]*types.Type, len(fn.explicits))
    			for i, targ := range fn.explicits {
    
    				targs[i] = r.p.typIdx(targ, r.dict, true)
    
    			}
    
    			obj = r.p.objIdx(fn.idx, nil, targs)
    			assert(r.dict.funcsObj[idx] == nil)
    			r.dict.funcsObj[idx] = obj
    		}
    		return obj
    	}
    
    
    	idx := r.reloc(relocObj)
    
    	explicits := make([]*types.Type, r.len())
    	for i := range explicits {
    		explicits[i] = r.typ()
    	}
    
    
    	var implicits []*types.Type
    	if r.dict != nil {
    		implicits = r.dict.targs
    	}
    
    	return r.p.objIdx(idx, implicits, explicits)
    
    }
    
    func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node {
    
    	rname := pr.newReader(relocName, idx, syncObject1)
    	_, sym := rname.qualifiedIdent()
    	tag := codeObj(rname.code(syncCodeObj))
    
    	if tag == 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)
    		}
    		if haveLegacyImports {
    
    			return typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
    		}
    		base.Fatalf("unresolved stub: %v", sym)
    	}
    
    	dict := pr.objDictIdx(sym, idx, implicits, explicits)
    
    	r := pr.newReader(relocObj, idx, syncObject1)
    
    	rext := pr.newReader(relocObjExt, idx, syncObject1)
    
    	sym = r.mangle(sym)
    	if !sym.IsBlank() && sym.Def != nil {
    		return sym.Def.(*ir.Name)
    	}
    
    
    	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")
    
    	case objAlias:
    		name := do(ir.OTYPE, false)
    
    		name.SetAlias(true)
    		return name
    
    	case objConst:
    		name := do(ir.OLITERAL, false)
    
    		typ := r.typ()
    		val := FixValue(typ, r.value())
    
    		setType(name, typ)
    		setValue(name, val)
    
    		return name
    
    	case objFunc:
    		if sym.Name == "init" {
    			sym = renameinit()
    		}
    		name := do(ir.ONAME, true)
    
    		setType(name, r.signature(sym.Pkg, nil))
    
    
    		name.Func = ir.NewFunc(r.pos())
    		name.Func.Nname = name
    
    
    		return name
    
    	case objType:
    		name := do(ir.OTYPE, true)
    		typ := types.NewNamed(name)
    
    
    		// 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))
    
    		types.ResumeCheckSize()
    
    		methods := make([]*types.Field, r.len())
    		for i := range methods {
    
    		}
    		if len(methods) != 0 {
    			typ.Methods().Set(methods)
    		}
    
    
    		return name
    
    	case objVar:
    		name := do(ir.ONAME, false)
    
    		return name
    	}
    }
    
    func (r *reader) mangle(sym *types.Sym) *types.Sym {
    
    		return sym
    	}
    
    	var buf bytes.Buffer
    	buf.WriteString(sym.Name)
    	buf.WriteByte('[')
    
    	for i, targ := range r.dict.targs {
    		if i > 0 {
    			if i == r.dict.implicits {
    				buf.WriteByte(';')
    			} else {
    
    	}
    	buf.WriteByte(']')
    	return sym.Pkg.Lookup(buf.String())
    }
    
    
    func (pr *pkgReader) objDictIdx(sym *types.Sym, idx int, implicits, explicits []*types.Type) *readerDict {
    	r := pr.newReader(relocObjDict, idx, syncObject1)
    
    	var dict readerDict
    
    
    	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
    
    	// For stenciling, we can just skip over the type parameters.
    
    	for range dict.targs[dict.implicits:] {
    
    		// Skip past bounds without actually evaluating them.
    		r.sync(syncType)
    
    
    	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(relocType), r.bool()}
    	}
    
    	dict.funcs = make([]objInfo, r.len())
    	dict.funcsObj = make([]ir.Node, len(dict.funcs))
    	for i := range dict.funcs {
    		objIdx := r.reloc(relocObj)
    		targs := make([]typeInfo, r.len())
    		for j := range targs {
    			targs[j] = r.typInfo()
    		}
    		dict.funcs[i] = objInfo{idx: objIdx, explicits: targs}
    	}
    
    	return &dict
    
    }
    
    func (r *reader) typeParamNames() {
    	r.sync(syncTypeParamNames)
    
    
    	for range r.dict.targs[r.dict.implicits:] {
    
    func (r *reader) method(rext *reader) *types.Field {
    
    	r.sync(syncMethod)
    	pos := r.pos()
    	pkg, sym := r.selector()
    	r.typeParamNames()
    	_, recv := r.param()
    	typ := r.signature(pkg, recv)
    
    	fnsym := sym
    	fnsym = ir.MethodSym(recv.Type, fnsym)
    	name := ir.NewNameAt(pos, fnsym)
    
    
    	name.Func = ir.NewFunc(r.pos())
    	name.Func.Nname = name
    
    
    
    	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(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(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(syncSelector)
    	origPkg = r.pkg()
    	name := r.string()
    	pkg := origPkg
    	if types.IsExported(name) {
    		pkg = types.LocalPkg
    	}
    	sym = pkg.Lookup(name)
    	return
    }
    
    
    	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) {
    	r.sync(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)
    
    
    	if r.bool() {
    		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(),
    			}
    
    	}
    	r.sync(syncEOF)
    }
    
    func (r *reader) typeExt(name *ir.Name) {
    	r.sync(syncTypeExt)
    
    	typ := name.Type()
    
    
    		// 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())
    	if name.Pragma()&ir.NotInHeap != 0 {
    		typ.SetNotInHeap(true)
    	}
    
    	typecheck.SetBaseTypeIndex(typ, r.int64(), r.int64())
    }
    
    func (r *reader) varExt(name *ir.Name) {
    	r.sync(syncVarExt)
    	r.linkname(name)
    }
    
    func (r *reader) linkname(name *ir.Name) {
    	assert(name.Op() == ir.ONAME)
    	r.sync(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(syncPragma)
    	return ir.PragmaFlag(r.int())
    }
    
    // @@@ Function bodies
    
    // bodyReader tracks where the serialized IR for a function's body can
    // be found.
    var bodyReader = map[*ir.Func]pkgReaderIndex{}
    
    // todoBodies holds the list of function bodies that still need to be
    // constructed.
    var todoBodies []*ir.Func
    
    
    // todoBodiesDone signals that we constructed all function in todoBodies.
    // This is necessary to prevent reader.addBody adds thing to todoBodies
    // when nested inlining happens.
    var todoBodiesDone = false
    
    
    func (r *reader) addBody(fn *ir.Func) {
    	pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict}
    
    	if fn.Nname.Defn == nil {
    		// Don't read in function body for imported functions.
    		// See comment in funcExt.
    		return
    	}
    
    
    	if r.curfn == nil && !todoBodiesDone {
    
    		todoBodies = append(todoBodies, fn)
    		return
    	}
    
    	pri.funcBody(fn)
    }
    
    func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
    	r := pri.asReader(relocBody, syncFuncBody)
    	r.funcBody(fn)
    }
    
    func (r *reader) funcBody(fn *ir.Func) {
    	r.curfn = fn
    
    	ir.WithFunc(fn, func() {
    		r.funcargs(fn)
    
    
    		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)
    }
    
    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)