Skip to content
Snippets Groups Projects
reader.go 59 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"
    )
    
    type pkgReader struct {
    
    
    	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 []pkgbits.Index
    
    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([]pkgbits.Index, pr.TotalElems()),
    
    	idx  pkgbits.Index
    
    func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader {
    
    	r := pri.pr.newReader(k, pri.idx, marker)
    
    func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
    
    		Decoder: pr.NewDecoder(k, idx, marker),
    
    	// 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
    
    
    	itabs []itabInfo2
    }
    
    type itabInfo2 struct {
    	typ  *types.Type
    	lsym *obj.LSym
    
    func setType(n ir.Node, typ *types.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(pkgbits.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(pkgbits.RelocPosBase)))
    
    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)
    
    	filename := absFilename
    
    	// For build artifact stability, the export data format only
    	// contains the "absolute" filename as returned by objabi.AbsFile.
    	// However, some tests (e.g., test/run.go's asmcheck tests) expect
    	// to see the full, original filename printed out. Re-expanding
    	// "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to
    	// satisfy this.
    	//
    	// TODO(mdempsky): De-duplicate this logic with similar logic in
    	// cmd/link/internal/ld's expandGoroot. However, this will probably
    	// require being more consistent about when we use native vs UNIX
    	// file paths.
    	const dollarGOROOT = "$GOROOT"
    
    	if buildcfg.GOROOT != "" && strings.HasPrefix(filename, dollarGOROOT) {
    
    		filename = buildcfg.GOROOT + filename[len(dollarGOROOT):]
    	}
    
    		b = src.NewFileBase(filename, absFilename)
    
    		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(pkgbits.SyncPkg)
    	return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
    
    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
    }
    
    func (r *reader) doPkg() *types.Pkg {
    
    	case "builtin":
    		return types.BuiltinPkg
    	case "unsafe":
    		return types.UnsafePkg
    
    	_ = r.Len() // was package height, but not necessary anymore.
    
    
    	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: pkgbits.Index(r.Len()), derived: true}
    
    	return typeInfo{idx: r.Reloc(pkgbits.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 {
    
    	r := pr.newReader(pkgbits.RelocType, idx, pkgbits.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 := 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 r.signature(types.LocalPkg, nil)
    
    		return types.NewSlice(r.typ())
    
    	case pkgbits.TypeInterface:
    
    		return r.interfaceType()
    
    	case pkgbits.TypeUnion:
    		return r.unionType()
    
    func (r *reader) unionType() *types.Type {
    	terms := make([]*types.Type, r.Len())
    	tildes := make([]bool, len(terms))
    	for i := range terms {
    		tildes[i] = r.Bool()
    		terms[i] = r.typ()
    	}
    	return types.NewUnion(terms, tildes)
    }
    
    
    func (r *reader) interfaceType() *types.Type {
    	tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
    
    
    	nmethods, nembeddeds := r.Len(), r.Len()
    
    	implicit := nmethods == 0 && nembeddeds == 1 && r.Bool()
    	assert(!implicit) // implicit interfaces only appear in constraints
    
    
    	fields := make([]*types.Field, nmethods+nembeddeds)
    	methods, embeddeds := fields[:nmethods], fields[nmethods:]
    
    	for i := range methods {
    		pos := r.pos()
    		pkg, sym := r.selector()
    		tpkg = pkg
    
    		mtyp := r.signature(pkg, types.FakeRecv())
    
    		methods[i] = types.NewField(pos, sym, mtyp)
    	}
    	for i := range embeddeds {
    		embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ())
    	}
    
    	if len(fields) == 0 {
    		return types.Types[types.TINTER] // empty interface
    	}
    
    	return types.NewInterface(tpkg, fields, false)
    
    }
    
    func (r *reader) structType() *types.Type {
    	tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
    
    	fields := make([]*types.Field, r.Len())
    
    	for i := range fields {
    		pos := r.pos()
    		pkg, sym := r.selector()
    		tpkg = pkg
    		ftyp := r.typ()
    
    		tag := r.String()
    		embedded := r.Bool()
    
    
    		f := types.NewField(pos, sym, ftyp)
    		f.Note = tag
    		if embedded {
    			f.Embedded = 1
    		}
    		fields[i] = f
    	}
    
    	return types.NewStruct(tpkg, fields)
    
    }
    
    func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
    
    	r.Sync(pkgbits.SyncSignature)
    
    
    	params := r.params(&tpkg)
    	results := r.params(&tpkg)
    
    		params[len(params)-1].SetIsDDD(true)
    	}
    
    	return types.NewSignature(tpkg, recv, nil, params, results)
    }
    
    func (r *reader) params(tpkg **types.Pkg) []*types.Field {
    
    	r.Sync(pkgbits.SyncParams)
    	fields := make([]*types.Field, r.Len())
    
    	for i := range fields {
    		*tpkg, fields[i] = r.param()
    	}
    	return fields
    }
    
    func (r *reader) param() (*types.Pkg, *types.Field) {
    
    
    	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(pkgbits.SyncObject)
    
    		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(pkgbits.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 pkgbits.Index, implicits, explicits []*types.Type) ir.Node {
    
    	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:
    
    			return sym.Def.(ir.Node)
    		}
    		if pri, ok := objReader[sym]; ok {
    			return pri.pr.objIdx(pri.idx, nil, explicits)
    		}
    		base.Fatalf("unresolved stub: %v", sym)
    	}
    
    	dict := pr.objDictIdx(sym, idx, implicits, explicits)
    
    
    	r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
    	rext := pr.newReader(pkgbits.RelocObjExt, idx, pkgbits.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")
    
    
    		name := do(ir.OTYPE, false)
    
    		name := do(ir.OLITERAL, false)
    
    		val := FixValue(typ, r.Value())
    
    		setType(name, typ)
    		setValue(name, val)
    
    			sym = Renameinit()
    
    		}
    		name := do(ir.ONAME, true)
    
    		setType(name, r.signature(sym.Pkg, nil))
    
    
    		name.Func = ir.NewFunc(r.pos())
    		name.Func.Nname = name
    
    
    		if r.hasTypeParams() {
    			name.Func.SetDupok(true)
    		}
    
    
    		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))
    
    		methods := make([]*types.Field, r.Len())
    
    		for i := range methods {
    
    		}
    		if len(methods) != 0 {
    			typ.Methods().Set(methods)
    		}
    
    
    		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 pkgbits.Index, implicits, explicits []*types.Type) *readerDict {
    
    	r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
    
    	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(pkgbits.SyncType)
    		if r.Bool() {
    			r.Len()
    
    			r.Reloc(pkgbits.RelocType)
    
    	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()}
    
    	dict.funcs = make([]objInfo, r.Len())
    
    	dict.funcsObj = make([]ir.Node, len(dict.funcs))
    	for i := range dict.funcs {
    
    		objIdx := r.Reloc(pkgbits.RelocObj)
    		targs := make([]typeInfo, r.Len())
    
    		for j := range targs {
    			targs[j] = r.typInfo()
    		}
    		dict.funcs[i] = objInfo{idx: objIdx, explicits: targs}
    	}
    
    
    	dict.itabs = make([]itabInfo2, r.Len())
    	for i := range dict.itabs {
    
    		typ := pr.typIdx(typeInfo{idx: pkgbits.Index(r.Len()), derived: true}, &dict, true)
    
    		ifaceInfo := r.typInfo()
    
    		var lsym *obj.LSym
    		if typ.IsInterface() {
    			lsym = reflectdata.TypeLinksym(typ)
    		} else {
    			iface := pr.typIdx(ifaceInfo, &dict, true)
    			lsym = reflectdata.ITabLsym(typ, iface)
    		}
    
    		dict.itabs[i] = itabInfo2{typ: typ, lsym: lsym}
    	}
    
    
    }
    
    func (r *reader) typeParamNames() {
    
    	r.Sync(pkgbits.SyncTypeParamNames)
    
    	for range r.dict.targs[r.dict.implicits:] {
    
    func (r *reader) method(rext *reader) *types.Field {
    
    	r.Sync(pkgbits.SyncMethod)
    
    	pos := r.pos()
    	pkg, sym := r.selector()
    	r.typeParamNames()
    	_, recv := r.param()
    	typ := r.signature(pkg, recv)
    
    	fnsym := sym
    	fnsym = ir.MethodSym(recv.Type, fnsym)
    	name := ir.NewNameAt(pos, fnsym)
    
    
    	name.Func = ir.NewFunc(r.pos())
    	name.Func.Nname = name
    
    
    	if r.hasTypeParams() {
    		name.Func.SetDupok(true)
    	}
    
    
    
    	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) {
    
    	if name := r.String(); name != "" {
    
    		sym = pkg.Lookup(name)
    	}
    	return
    }
    
    func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) {
    
    	r.Sync(pkgbits.SyncLocalIdent)
    
    	if name := r.String(); name != "" {
    
    		sym = pkg.Lookup(name)
    	}
    	return
    }
    
    func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) {
    
    	r.Sync(pkgbits.SyncSelector)
    
    	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(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)
    
    
    	if r.Bool() {
    		fn.ABI = obj.ABI(r.Uint64())
    
    
    		// Escape analysis.
    		for _, fs := range &types.RecvsParams {
    			for _, f := range fs(name.Type()).FieldSlice() {
    
    				Cost:            int32(r.Len()),
    				CanDelayResults: r.Bool(),
    
    }
    
    func (r *reader) typeExt(name *ir.Name) {
    
    	r.Sync(pkgbits.SyncTypeExt)
    
    		// 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(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 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
    
    
    func (r *reader) addBody(fn *ir.Func) {
    
    	pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict}
    
    	if fn.Nname.Defn == nil {
    		// Don't read in function body for imported functions.
    		// See comment in funcExt.
    		return
    	}
    
    
    		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)
    }
    
    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()