Skip to content
Snippets Groups Projects
writer.go 77.1 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 (
    	"fmt"
    
    	"internal/buildcfg"
    
    
    	"cmd/compile/internal/base"
    	"cmd/compile/internal/ir"
    	"cmd/compile/internal/syntax"
    
    	"cmd/compile/internal/types2"
    )
    
    
    // This file implements the Unified IR package writer and defines the
    // Unified IR export data format.
    //
    // Low-level coding details (e.g., byte-encoding of individual
    // primitive values, or handling element bitstreams and
    // cross-references) are handled by internal/pkgbits, so here we only
    // concern ourselves with higher-level worries like mapping Go
    // language constructs into elements.
    
    // There are two central types in the writing process: the "writer"
    // type handles writing out individual elements, while the "pkgWriter"
    // type keeps track of which elements have already been created.
    //
    // For each sort of "thing" (e.g., position, package, object, type)
    // that can be written into the export data, there are generally
    // several methods that work together:
    //
    // - writer.thing handles writing out a *use* of a thing, which often
    //   means writing a relocation to that thing's encoded index.
    //
    // - pkgWriter.thingIdx handles reserving an index for a thing, and
    //   writing out any elements needed for the thing.
    //
    // - writer.doThing handles writing out the *definition* of a thing,
    //   which in general is a mix of low-level coding primitives (e.g.,
    //   ints and strings) or uses of other things.
    //
    // A design goal of Unified IR is to have a single, canonical writer
    // implementation, but multiple reader implementations each tailored
    // to their respective needs. For example, within cmd/compile's own
    // backend, inlining is implemented largely by just re-running the
    // function body reading code.
    
    // TODO(mdempsky): Add an importer for Unified IR to the x/tools repo,
    // and better document the file format boundary between public and
    // private data.
    
    
    type index = pkgbits.Index
    
    func assert(p bool) { base.Assert(p) }
    
    
    // A pkgWriter constructs Unified IR export data from the results of
    // running the types2 type checker on a Go compilation unit.
    
    type pkgWriter struct {
    
    	m                     posMap
    	curpkg                *types2.Package
    	info                  *types2.Info
    	rangeFuncBodyClosures map[*syntax.FuncLit]bool // non-public information, e.g., which functions are closures range function bodies?
    
    	// Indices for previously written syntax and types2 things.
    
    
    	posBasesIdx map[*syntax.PosBase]index
    	pkgsIdx     map[*types2.Package]index
    	typsIdx     map[types2.Type]index
    	objsIdx     map[types2.Object]index
    
    
    	// Maps from types2.Objects back to their syntax.Decl.
    
    
    	funDecls map[*types2.Func]*syntax.FuncDecl
    	typDecls map[*types2.TypeName]typeDeclGen
    
    
    	// linknames maps package-scope objects to their linker symbol name,
    	// if specified by a //go:linkname directive.
    	linknames map[types2.Object]string
    
    	// cgoPragmas accumulates any //go:cgo_* pragmas that need to be
    	// passed through to cmd/link.
    
    // newPkgWriter returns an initialized pkgWriter for the specified
    // package.
    
    func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {
    
    		PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
    
    		m:                     m,
    		curpkg:                pkg,
    		info:                  info,
    		rangeFuncBodyClosures: otherInfo,
    
    		pkgsIdx: make(map[*types2.Package]index),
    		objsIdx: make(map[types2.Object]index),
    		typsIdx: make(map[types2.Type]index),
    
    		posBasesIdx: make(map[*syntax.PosBase]index),
    
    
    		funDecls: make(map[*types2.Func]*syntax.FuncDecl),
    		typDecls: make(map[*types2.TypeName]typeDeclGen),
    
    		linknames: make(map[types2.Object]string),
    	}
    }
    
    
    // errorf reports a user error about thing p.
    
    func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) {
    
    	base.ErrorfAt(pw.m.pos(p), 0, msg, args...)
    
    // fatalf reports an internal compiler error about thing p.
    
    func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) {
    	base.FatalfAt(pw.m.pos(p), msg, args...)
    }
    
    
    // unexpected reports a fatal error about a thing of unexpected
    // dynamic type.
    
    func (pw *pkgWriter) unexpected(what string, p poser) {
    	pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
    }
    
    
    func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
    
    	tv, ok := pw.maybeTypeAndValue(x)
    	if !ok {
    
    		pw.fatalf(x, "missing Types entry: %v", syntax.String(x))
    
    func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {
    	tv := x.GetTypeInfo()
    
    
    	// If x is a generic function whose type arguments are inferred
    	// from assignment context, then we need to find its inferred type
    	// in Info.Instances instead.
    	if name, ok := x.(*syntax.Name); ok {
    		if inst, ok := pw.info.Instances[name]; ok {
    			tv.Type = inst.Type
    		}
    	}
    
    
    	return tv, tv.Type != nil
    
    }
    
    // typeOf returns the Type of the given value expression.
    func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type {
    	tv := pw.typeAndValue(expr)
    
    	if !tv.IsValue() {
    		pw.fatalf(expr, "expected value: %v", syntax.String(expr))
    	}
    
    // A writer provides APIs for writing out an individual element.
    
    type writer struct {
    	p *pkgWriter
    
    
    	// sig holds the signature for the current function body, if any.
    	sig *types2.Signature
    
    
    	// TODO(mdempsky): We should be able to prune localsIdx whenever a
    	// scope closes, and then maybe we can just use the same map for
    	// storing the TypeParams too (as their TypeName instead).
    
    
    	// localsIdx tracks any local variables declared within this
    	// function body. It's unused for writing out non-body things.
    
    	// closureVars tracks any free variables that are referenced by this
    	// function body. It's unused for writing out non-body things.
    	closureVars    []posVar
    	closureVarsIdx map[*types2.Var]int // index of previously seen free variables
    
    	dict *writerDict
    
    	// derived tracks whether the type being written out references any
    	// type parameters. It's unused for writing non-type things.
    
    	derived bool
    }
    
    // A writerDict tracks types and objects that are used by a declaration.
    type writerDict struct {
    
    	// implicits is a slice of type parameters from the enclosing
    	// declarations.
    	implicits []*types2.TypeParam
    
    
    	// derived is a slice of type indices for computing derived types
    	// (i.e., types that depend on the declaration's type parameters).
    
    
    	// derivedIdx maps a Type to its corresponding index within the
    	// derived slice, if present.
    
    
    	// These slices correspond to entries in the runtime dictionary.
    	typeParamMethodExprs []writerMethodExprInfo
    	subdicts             []objInfo
    	rtypes               []typeInfo
    	itabs                []itabInfo
    }
    
    type itabInfo struct {
    	typ   typeInfo
    	iface typeInfo
    }
    
    // typeParamIndex returns the index of the given type parameter within
    // the dictionary. This may differ from typ.Index() when there are
    // implicit type parameters due to defined types declared within a
    // generic function or method.
    func (dict *writerDict) typeParamIndex(typ *types2.TypeParam) int {
    	for idx, implicit := range dict.implicits {
    
    			return idx
    		}
    	}
    
    	return len(dict.implicits) + typ.Index()
    
    // A derivedInfo represents a reference to an encoded generic Go type.
    
    	needed bool // TODO(mdempsky): Remove.
    
    // A typeInfo represents a reference to an encoded Go type.
    //
    // If derived is true, then the typeInfo represents a generic Go type
    // that contains type parameters. In this case, idx is an index into
    // the readerDict.derived{,Types} arrays.
    //
    // Otherwise, the typeInfo represents a non-generic Go type, and idx
    // is an index into the reader.typs array instead.
    
    // An objInfo represents a reference to an encoded, instantiated (if
    // applicable) Go object.
    
    	idx       index      // index for the generic function declaration
    	explicits []typeInfo // info for the type arguments
    
    // A selectorInfo represents a reference to an encoded field or method
    // name (i.e., objects that can only be accessed using selector
    // expressions).
    type selectorInfo struct {
    
    // anyDerived reports whether any of info's explicit type arguments
    // are derived types.
    
    func (info objInfo) anyDerived() bool {
    	for _, explicit := range info.explicits {
    		if explicit.derived {
    			return true
    		}
    	}
    	return false
    }
    
    
    // equals reports whether info and other represent the same Go object
    // (i.e., same base object and identical type arguments, if any).
    
    func (info objInfo) equals(other objInfo) bool {
    	if info.idx != other.idx {
    		return false
    	}
    	assert(len(info.explicits) == len(other.explicits))
    	for i, targ := range info.explicits {
    		if targ != other.explicits[i] {
    			return false
    		}
    	}
    	return true
    
    type writerMethodExprInfo struct {
    	typeParamIdx int
    	methodInfo   selectorInfo
    }
    
    // typeParamMethodExprIdx returns the index where the given encoded
    // method expression function pointer appears within this dictionary's
    // type parameters method expressions section, adding it if necessary.
    func (dict *writerDict) typeParamMethodExprIdx(typeParamIdx int, methodInfo selectorInfo) int {
    	newInfo := writerMethodExprInfo{typeParamIdx, methodInfo}
    
    	for idx, oldInfo := range dict.typeParamMethodExprs {
    		if oldInfo == newInfo {
    			return idx
    		}
    	}
    
    	idx := len(dict.typeParamMethodExprs)
    	dict.typeParamMethodExprs = append(dict.typeParamMethodExprs, newInfo)
    	return idx
    }
    
    // subdictIdx returns the index where the given encoded object's
    // runtime dictionary appears within this dictionary's subdictionary
    // section, adding it if necessary.
    func (dict *writerDict) subdictIdx(newInfo objInfo) int {
    	for idx, oldInfo := range dict.subdicts {
    		if oldInfo.equals(newInfo) {
    			return idx
    		}
    	}
    
    	idx := len(dict.subdicts)
    	dict.subdicts = append(dict.subdicts, newInfo)
    	return idx
    }
    
    // rtypeIdx returns the index where the given encoded type's
    // *runtime._type value appears within this dictionary's rtypes
    // section, adding it if necessary.
    func (dict *writerDict) rtypeIdx(newInfo typeInfo) int {
    	for idx, oldInfo := range dict.rtypes {
    		if oldInfo == newInfo {
    			return idx
    		}
    	}
    
    	idx := len(dict.rtypes)
    	dict.rtypes = append(dict.rtypes, newInfo)
    	return idx
    }
    
    // itabIdx returns the index where the given encoded type pair's
    // *runtime.itab value appears within this dictionary's itabs section,
    // adding it if necessary.
    func (dict *writerDict) itabIdx(typInfo, ifaceInfo typeInfo) int {
    	newInfo := itabInfo{typInfo, ifaceInfo}
    
    	for idx, oldInfo := range dict.itabs {
    		if oldInfo == newInfo {
    			return idx
    		}
    	}
    
    	idx := len(dict.itabs)
    	dict.itabs = append(dict.itabs, newInfo)
    	return idx
    }
    
    
    func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer {
    
    		Encoder: pw.NewEncoder(k, marker),
    
    // pos writes the position of p into the element bitstream.
    
    func (w *writer) pos(p poser) {
    
    	pos := p.Pos()
    
    	// TODO(mdempsky): Track down the remaining cases here and fix them.
    
    	if !w.Bool(pos.IsKnown()) {
    
    	// TODO(mdempsky): Delta encoding.
    
    	w.Uint(pos.Line())
    	w.Uint(pos.Col())
    
    // posBase writes a reference to the given PosBase into the element
    // bitstream.
    
    func (w *writer) posBase(b *syntax.PosBase) {
    
    	w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b))
    
    // posBaseIdx returns the index for the given PosBase.
    
    func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) index {
    
    	if idx, ok := pw.posBasesIdx[b]; ok {
    		return idx
    	}
    
    
    	w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase)
    	w.p.posBasesIdx[b] = w.Idx
    
    	if !w.Bool(b.IsFileBase()) {
    
    		w.Uint(b.Line())
    		w.Uint(b.Col())
    
    // pkg writes a use of the given Package into the element bitstream.
    
    func (w *writer) pkg(pkg *types2.Package) {
    
    func (w *writer) pkgRef(idx index) {
    
    // pkgIdx returns the index for the given package, adding it to the
    // package export data if needed.
    
    func (pw *pkgWriter) pkgIdx(pkg *types2.Package) index {
    
    	if idx, ok := pw.pkgsIdx[pkg]; ok {
    		return idx
    	}
    
    
    	w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
    	pw.pkgsIdx[pkg] = w.Idx
    
    	// The universe and package unsafe need to be handled specially by
    	// importers anyway, so we serialize them using just their package
    	// path. This ensures that readers don't confuse them for
    	// user-defined packages.
    	switch pkg {
    	case nil: // universe
    		w.String("builtin") // same package path used by godoc
    	case types2.Unsafe:
    		w.String("unsafe")
    	default:
    
    		// TODO(mdempsky): Write out pkg.Path() for curpkg too.
    
    		var path string
    		if pkg != w.p.curpkg {
    			path = pkg.Path()
    		}
    
    		base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path)
    
    		w.String(path)
    		w.String(pkg.Name())
    
    		for _, imp := range pkg.Imports() {
    			w.pkg(imp)
    		}
    	}
    
    
    	anyTypeName        = types2.Universe.Lookup("any").(*types2.TypeName)
    	comparableTypeName = types2.Universe.Lookup("comparable").(*types2.TypeName)
    	runeTypeName       = types2.Universe.Lookup("rune").(*types2.TypeName)
    
    // typ writes a use of the given type into the bitstream.
    
    func (w *writer) typ(typ types2.Type) {
    
    // typInfo writes a use of the given type (specified as a typeInfo
    // instead) into the bitstream.
    
    	w.Sync(pkgbits.SyncType)
    	if w.Bool(info.derived) {
    
    		w.Len(int(info.idx))
    
    		w.Reloc(pkgbits.RelocType, info.idx)
    
    // typIdx returns the index where the export data description of type
    // can be read back in. If no such index exists yet, it's created.
    //
    // typIdx also reports whether typ is a derived type; that is, whether
    // its identity depends on type parameters.
    
    func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
    
    	// Strip non-global aliases, because they only appear in inline
    	// bodies anyway. Otherwise, they can cause types.Sym collisions
    	// (e.g., "main.C" for both of the local type aliases in
    	// test/fixedbugs/issue50190.go).
    	for {
    		if alias, ok := typ.(*types2.Alias); ok && !isGlobal(alias.Obj()) {
    			typ = alias.Rhs()
    		} else {
    			break
    		}
    	}
    
    
    	if idx, ok := pw.typsIdx[typ]; ok {
    
    	}
    	if dict != nil {
    		if idx, ok := dict.derivedIdx[typ]; ok {
    
    	w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
    
    	default:
    		base.Fatalf("unexpected type: %v (%T)", typ, typ)
    
    	case *types2.Basic:
    
    		switch kind := typ.Kind(); {
    		case kind == types2.Invalid:
    			base.Fatalf("unexpected types2.Invalid")
    
    		case types2.Typ[kind] == typ:
    
    			w.Code(pkgbits.TypeBasic)
    			w.Len(int(kind))
    
    			// Handle "byte" and "rune" as references to their TypeNames.
    
    			obj := types2.Universe.Lookup(typ.Name()).(*types2.TypeName)
    
    		w.Code(pkgbits.TypeNamed)
    		w.namedType(splitNamed(typ))
    
    		w.namedType(splitAlias(typ))
    
    		w.Code(pkgbits.TypeTypeParam)
    
    		w.Len(w.dict.typeParamIndex(typ))
    
    		w.Code(pkgbits.TypeArray)
    		w.Uint64(uint64(typ.Len()))
    
    		w.typ(typ.Elem())
    
    	case *types2.Chan:
    
    		w.Code(pkgbits.TypeChan)
    		w.Len(int(typ.Dir()))
    
    		w.typ(typ.Elem())
    
    	case *types2.Map:
    
    		w.typ(typ.Key())
    		w.typ(typ.Elem())
    
    	case *types2.Pointer:
    
    		w.Code(pkgbits.TypePointer)
    
    		w.typ(typ.Elem())
    
    	case *types2.Signature:
    
    		base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
    
    		w.Code(pkgbits.TypeSignature)
    
    		w.signature(typ)
    
    	case *types2.Slice:
    
    		w.typ(typ.Elem())
    
    	case *types2.Struct:
    
    		w.Code(pkgbits.TypeStruct)
    
    		w.structType(typ)
    
    	case *types2.Interface:
    
    		// Handle "any" as reference to its TypeName.
    
    		// The underlying "any" interface is canonical, so this logic handles both
    		// GODEBUG=gotypesalias=1 (when any is represented as a types2.Alias), and
    		// gotypesalias=0.
    		if types2.Unalias(typ) == types2.Unalias(anyTypeName.Type()) {
    
    		w.Code(pkgbits.TypeInterface)
    
    		w.interfaceType(typ)
    
    	case *types2.Union:
    
    		dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})
    
    	pw.typsIdx[typ] = w.Idx
    	return typeInfo{idx: w.Flush(), derived: false}
    
    // namedType writes a use of the given named type into the bitstream.
    func (w *writer) namedType(obj *types2.TypeName, targs *types2.TypeList) {
    	// Named types that are declared within a generic function (and
    	// thus have implicit type parameters) are always derived types.
    	if w.p.hasImplicitTypeParams(obj) {
    		w.derived = true
    	}
    
    	w.obj(obj, targs)
    }
    
    
    func (w *writer) structType(typ *types2.Struct) {
    
    	for i := 0; i < typ.NumFields(); i++ {
    		f := typ.Field(i)
    		w.pos(f)
    		w.selector(f)
    		w.typ(f.Type())
    
    		w.String(typ.Tag(i))
    		w.Bool(f.Embedded())
    
    	}
    }
    
    func (w *writer) unionType(typ *types2.Union) {
    
    	for i := 0; i < typ.Len(); i++ {
    		t := typ.Term(i)
    
    	}
    }
    
    func (w *writer) interfaceType(typ *types2.Interface) {
    
    	// If typ has no embedded types but it's not a basic interface, then
    	// the natural description we write out below will fail to
    	// reconstruct it.
    	if typ.NumEmbeddeds() == 0 && !typ.IsMethodSet() {
    		// Currently, this can only happen for the underlying Interface of
    		// "comparable", which is needed to handle type declarations like
    		// "type C comparable".
    		assert(typ == comparableTypeName.Type().(*types2.Named).Underlying())
    
    		// Export as "interface{ comparable }".
    		w.Len(0)                         // NumExplicitMethods
    		w.Len(1)                         // NumEmbeddeds
    		w.Bool(false)                    // IsImplicit
    		w.typ(comparableTypeName.Type()) // EmbeddedType(0)
    		return
    	}
    
    
    	w.Len(typ.NumExplicitMethods())
    	w.Len(typ.NumEmbeddeds())
    
    	if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 {
    		w.Bool(typ.IsImplicit())
    	} else {
    		// Implicit interfaces always have 0 explicit methods and 1
    		// embedded type, so we skip writing out the implicit flag
    		// otherwise as a space optimization.
    		assert(!typ.IsImplicit())
    	}
    
    
    	for i := 0; i < typ.NumExplicitMethods(); i++ {
    		m := typ.ExplicitMethod(i)
    		sig := m.Type().(*types2.Signature)
    
    
    		w.pos(m)
    		w.selector(m)
    		w.signature(sig)
    	}
    
    	for i := 0; i < typ.NumEmbeddeds(); i++ {
    		w.typ(typ.EmbeddedType(i))
    	}
    }
    
    func (w *writer) signature(sig *types2.Signature) {
    
    	w.Sync(pkgbits.SyncSignature)
    
    	w.params(sig.Params())
    	w.params(sig.Results())
    
    }
    
    func (w *writer) params(typ *types2.Tuple) {
    
    	w.Sync(pkgbits.SyncParams)
    	w.Len(typ.Len())
    
    	for i := 0; i < typ.Len(); i++ {
    		w.param(typ.At(i))
    	}
    }
    
    func (w *writer) param(param *types2.Var) {
    
    	w.pos(param)
    	w.localIdent(param)
    	w.typ(param.Type())
    }
    
    // @@@ Objects
    
    
    // obj writes a use of the given object into the bitstream.
    //
    // If obj is a generic object, then explicits are the explicit type
    // arguments used to instantiate it (i.e., used to substitute the
    // object's own declared type parameters).
    
    func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
    
    	w.objInfo(w.p.objInstIdx(obj, explicits, w.dict))
    }
    
    // objInfo writes a use of the given encoded object into the
    // bitstream.
    func (w *writer) objInfo(info objInfo) {
    
    	w.Sync(pkgbits.SyncObject)
    
    	if w.Version().Has(pkgbits.DerivedFuncInstance) {
    		w.Bool(false)
    	}
    
    	w.Reloc(pkgbits.RelocObj, info.idx)
    
    	w.Len(len(info.explicits))
    
    	for _, info := range info.explicits {
    		w.typInfo(info)
    
    // objInstIdx returns the indices for an object and a corresponding
    // list of type arguments used to instantiate it, adding them to the
    // export data as needed.
    func (pw *pkgWriter) objInstIdx(obj types2.Object, explicits *types2.TypeList, dict *writerDict) objInfo {
    	explicitInfos := make([]typeInfo, explicits.Len())
    	for i := range explicitInfos {
    		explicitInfos[i] = pw.typIdx(explicits.At(i), dict)
    	}
    	return objInfo{idx: pw.objIdx(obj), explicits: explicitInfos}
    }
    
    
    // objIdx returns the index for the given Object, adding it to the
    // export data as needed.
    
    func (pw *pkgWriter) objIdx(obj types2.Object) index {
    
    	// TODO(mdempsky): Validate that obj is a global object (or a local
    	// defined type, which we hoist to global scope anyway).
    
    	if idx, ok := pw.objsIdx[obj]; ok {
    
    		derivedIdx: make(map[types2.Type]index),
    
    	}
    
    	if isDefinedType(obj) && obj.Pkg() == pw.curpkg {
    		decl, ok := pw.typDecls[obj.(*types2.TypeName)]
    		assert(ok)
    		dict.implicits = decl.implicits
    	}
    
    
    	// We encode objects into 4 elements across different sections, all
    	// sharing the same index:
    	//
    	// - RelocName has just the object's qualified name (i.e.,
    	//   Object.Pkg and Object.Name) and the CodeObj indicating what
    	//   specific type of Object it is (Var, Func, etc).
    	//
    	// - RelocObj has the remaining public details about the object,
    	//   relevant to go/types importers.
    	//
    	// - RelocObjExt has additional private details about the object,
    	//   which are only relevant to cmd/compile itself. This is
    	//   separated from RelocObj so that go/types importers are
    	//   unaffected by internal compiler changes.
    	//
    	// - RelocObjDict has public details about the object's type
    	//   parameters and derived type's used by the object. This is
    	//   separated to facilitate the eventual introduction of
    	//   shape-based stenciling.
    	//
    	// TODO(mdempsky): Re-evaluate whether RelocName still makes sense
    	// to keep separate from RelocObj.
    
    
    	w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1)
    	wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1)
    	wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1)
    	wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1)
    
    	pw.objsIdx[obj] = w.Idx // break cycles
    
    	assert(wext.Idx == w.Idx)
    	assert(wname.Idx == w.Idx)
    	assert(wdict.Idx == w.Idx)
    
    	wname.Code(code)
    	wname.Flush()
    
    // doObj writes the RelocObj definition for obj to w, and the
    // RelocObjExt definition to wext.
    
    func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj {
    
    	if obj.Pkg() != w.p.curpkg {
    
    	}
    
    	switch obj := obj.(type) {
    	default:
    		w.p.unexpected("object", obj)
    
    		w.Value(obj.Val())
    		return pkgbits.ObjConst
    
    
    	case *types2.Func:
    		decl, ok := w.p.funDecls[obj]
    		assert(ok)
    		sig := obj.Type().(*types2.Signature)
    
    		w.pos(obj)
    
    		w.signature(sig)
    		w.pos(decl)
    
    
    	case *types2.TypeName:
    		if obj.IsAlias() {
    			w.pos(obj)
    
    			t := obj.Type()
    			if alias, ok := t.(*types2.Alias); ok { // materialized alias
    				t = alias.Rhs()
    			}
    			w.typ(t)
    
    		}
    
    		named := obj.Type().(*types2.Named)
    
    		w.typeParamNames(named.TypeParams())
    
    		w.typ(named.Underlying())
    
    		for i := 0; i < named.NumMethods(); i++ {
    
    	case *types2.Var:
    		w.pos(obj)
    		w.typ(obj.Type())
    
    // objDict writes the dictionary needed for reading the given object.
    func (w *writer) objDict(obj types2.Object, dict *writerDict) {
    	// TODO(mdempsky): Split objDict into multiple entries? reader.go
    	// doesn't care about the type parameter bounds, and reader2.go
    	// doesn't care about referenced functions.
    
    	w.dict = dict // TODO(mdempsky): This is a bit sketchy.
    
    	w.Len(len(dict.implicits))
    
    		w.Reloc(pkgbits.RelocType, typ.idx)
    		w.Bool(typ.needed)
    
    	// Write runtime dictionary information.
    	//
    	// N.B., the go/types importer reads up to the section, but doesn't
    	// read any further, so it's safe to change. (See TODO above.)
    
    
    	// For each type parameter, write out whether the constraint is a
    	// basic interface. This is used to determine how aggressively we
    	// can shape corresponding type arguments.
    	//
    	// This is somewhat redundant with writing out the full type
    	// parameter constraints above, but the compiler currently skips
    	// over those. Also, we don't care about the *declared* constraints,
    	// but how the type parameters are actually *used*. E.g., if a type
    	// parameter is constrained to `int | uint` but then never used in
    	// arithmetic/conversions/etc, we could shape those together.
    	for _, implicit := range dict.implicits {
    
    		w.Bool(implicit.Underlying().(*types2.Interface).IsMethodSet())
    
    	}
    	for i := 0; i < ntparams; i++ {
    		tparam := tparams.At(i)
    		w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet())
    	}
    
    
    	w.Len(len(dict.typeParamMethodExprs))
    	for _, info := range dict.typeParamMethodExprs {
    		w.Len(info.typeParamIdx)
    		w.selectorInfo(info.methodInfo)
    	}
    
    	w.Len(len(dict.subdicts))
    	for _, info := range dict.subdicts {
    		w.objInfo(info)
    	}
    
    	w.Len(len(dict.rtypes))
    	for _, info := range dict.rtypes {
    		w.typInfo(info)
    	}
    
    	w.Len(len(dict.itabs))
    	for _, info := range dict.itabs {
    		w.typInfo(info.typ)
    		w.typInfo(info.iface)
    	}
    
    
    	assert(len(dict.derived) == nderived)
    
    func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
    
    	w.Sync(pkgbits.SyncTypeParamNames)
    
    	ntparams := tparams.Len()
    	for i := 0; i < ntparams; i++ {
    
    func (w *writer) method(wext *writer, meth *types2.Func) {
    
    	decl, ok := w.p.funDecls[meth]
    	assert(ok)
    	sig := meth.Type().(*types2.Signature)
    
    
    	w.Sync(pkgbits.SyncMethod)
    
    	w.pos(meth)
    	w.selector(meth)
    
    	w.typeParamNames(sig.RecvTypeParams())
    
    	w.param(sig.Recv())
    	w.signature(sig)
    
    	w.pos(decl) // XXX: Hack to workaround linker limitations.
    
    }
    
    // qualifiedIdent writes out the name of an object declared at package
    // scope. (For now, it's also used to refer to local defined types.)
    func (w *writer) qualifiedIdent(obj types2.Object) {
    
    	if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
    
    		decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
    		assert(ok)
    
    			// For local defined types, we embed a scope-disambiguation
    			// number directly into their name. types.SplitVargenSuffix then
    			// knows to look for this.