Newer
Older
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package noder
import (
"fmt"
"go/constant"
"go/token"
"go/version"
"internal/pkgbits"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
"cmd/compile/internal/types2"
)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 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 {
pkgbits.PkgEncoder
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.
cgoPragmas [][]string
}
// 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 {
return &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))
}
return tv
}
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))
}
return tv.Type
}
// A writer provides APIs for writing out an individual element.
type writer struct {
p *pkgWriter
pkgbits.Encoder
// 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.
localsIdx map[*types2.Var]int
// 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).
Matthew Dempsky
committed
derived []derivedInfo
// derivedIdx maps a Type to its corresponding index within the
// derived slice, if present.
derivedIdx map[types2.Type]index
// 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 {
if implicit == typ {
return idx
}
}
return len(dict.implicits) + typ.Index()
Matthew Dempsky
committed
}
// A derivedInfo represents a reference to an encoded generic Go type.
Matthew Dempsky
committed
type derivedInfo struct {
idx index
needed bool // TODO(mdempsky): Remove.
Matthew Dempsky
committed
}
// 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.
Matthew Dempsky
committed
type typeInfo struct {
idx index
Matthew Dempsky
committed
derived bool
}
// An objInfo represents a reference to an encoded, instantiated (if
// applicable) Go object.
Matthew Dempsky
committed
type objInfo struct {
idx index // index for the generic function declaration
explicits []typeInfo // info for the type arguments
Matthew Dempsky
committed
}
// 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 {
pkgIdx index
nameIdx index
}
// anyDerived reports whether any of info's explicit type arguments
// are derived types.
Matthew Dempsky
committed
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).
Matthew Dempsky
committed
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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
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),
p: pw,
}
}
// @@@ Positions
// pos writes the position of p into the element bitstream.
func (w *writer) pos(p poser) {
w.Sync(pkgbits.SyncPos)
pos := p.Pos()
// TODO(mdempsky): Track down the remaining cases here and fix them.
if !w.Bool(pos.IsKnown()) {
return
}
// TODO(mdempsky): Delta encoding.
w.posBase(pos.Base())
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
w.String(trimFilename(b))
if !w.Bool(b.IsFileBase()) {
w.Uint(b.Line())
w.Uint(b.Col())
return w.Flush()
}
// @@@ Packages
// pkg writes a use of the given Package into the element bitstream.
func (w *writer) pkg(pkg *types2.Package) {
w.pkgRef(w.p.pkgIdx(pkg))
}
func (w *writer) pkgRef(idx index) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, idx)
// 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())
w.Len(len(pkg.Imports()))
for _, imp := range pkg.Imports() {
w.pkg(imp)
}
}
return w.Flush()
}
// @@@ Types
var (
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) {
Matthew Dempsky
committed
w.typInfo(w.p.typIdx(typ, w.dict))
}
// typInfo writes a use of the given type (specified as a typeInfo
// instead) into the bitstream.
Matthew Dempsky
committed
func (w *writer) typInfo(info typeInfo) {
w.Sync(pkgbits.SyncType)
if w.Bool(info.derived) {
w.derived = true
} else {
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.
Matthew Dempsky
committed
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 {
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: false}
}
if dict != nil {
if idx, ok := dict.derivedIdx[typ]; ok {
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: true}
}
w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
w.dict = dict
switch typ := typ.(type) {
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))
default:
// Handle "byte" and "rune" as references to their TypeNames.
obj := types2.Universe.Lookup(typ.Name()).(*types2.TypeName)
assert(obj.Type() == typ)
w.Code(pkgbits.TypeNamed)
w.namedType(obj, nil)
}
case *types2.Named:
w.Code(pkgbits.TypeNamed)
w.namedType(splitNamed(typ))
case *types2.Alias:
w.Code(pkgbits.TypeNamed)
case *types2.TypeParam:
w.derived = true
w.Code(pkgbits.TypeTypeParam)
w.Len(w.dict.typeParamIndex(typ))
case *types2.Array:
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.Code(pkgbits.TypeMap)
w.typ(typ.Key())
w.typ(typ.Elem())
case *types2.Pointer:
w.Code(pkgbits.TypePointer)
w.typ(typ.Elem())
case *types2.Signature:
Robert Griesemer
committed
base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
w.Code(pkgbits.TypeSignature)
w.signature(typ)
case *types2.Slice:
w.Code(pkgbits.TypeSlice)
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.TypeNamed)
w.obj(anyTypeName, nil)
break
}
w.Code(pkgbits.TypeInterface)
w.interfaceType(typ)
case *types2.Union:
w.Code(pkgbits.TypeUnion)
w.unionType(typ)
}
if w.derived {
idx := index(len(dict.derived))
dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})
dict.derivedIdx[typ] = idx
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: true}
}
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) {
w.Len(typ.NumFields())
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) {
w.Len(typ.Len())
Robert Griesemer
committed
for i := 0; i < typ.Len(); i++ {
t := typ.Term(i)
w.Bool(t.Tilde())
Robert Griesemer
committed
w.typ(t.Type())
}
}
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)
assert(sig.TypeParams() == nil)
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())
w.Bool(sig.Variadic())
}
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.Sync(pkgbits.SyncParam)
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))
}
Matthew Dempsky
committed
// 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))
Matthew Dempsky
committed
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 {
return idx
}
dict := &writerDict{
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)
w.dict = dict
wext.dict = dict
code := w.doObj(wext, obj)
w.Flush()
wext.Flush()
wname.qualifiedIdent(obj)
wname.Code(code)
wname.Flush()
wdict.objDict(obj, w.dict)
// 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 {
return pkgbits.ObjStub
}
switch obj := obj.(type) {
default:
w.p.unexpected("object", obj)
panic("unreachable")
case *types2.Const:
w.pos(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.typeParamNames(sig.TypeParams())
w.signature(sig)
w.pos(decl)
wext.funcExt(obj)
return pkgbits.ObjFunc
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)
return pkgbits.ObjAlias
}
named := obj.Type().(*types2.Named)
assert(named.TypeArgs() == nil)
w.typeParamNames(named.TypeParams())
wext.typeExt(obj)
w.typ(named.Underlying())
w.Len(named.NumMethods())
for i := 0; i < named.NumMethods(); i++ {
w.method(wext, named.Method(i))
return pkgbits.ObjType
case *types2.Var:
w.pos(obj)
w.typ(obj.Type())
wext.varExt(obj)
return pkgbits.ObjVar
// 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))
tparams := objTypeParams(obj)
Robert Griesemer
committed
ntparams := tparams.Len()
w.Len(ntparams)
Robert Griesemer
committed
for i := 0; i < ntparams; i++ {
Robert Griesemer
committed
w.typ(tparams.At(i).Constraint())
nderived := len(dict.derived)
w.Len(nderived)
for _, typ := range dict.derived {
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)
Robert Griesemer
committed
ntparams := tparams.Len()
for i := 0; i < ntparams; i++ {
Robert Griesemer
committed
tparam := tparams.At(i).Obj()
w.pos(tparam)
w.localIdent(tparam)
}
}
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.
wext.funcExt(meth)
}
// 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) {
w.Sync(pkgbits.SyncSym)
name := obj.Name()
if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
assert(ok)
if decl.gen != 0 {
// For local defined types, we embed a scope-disambiguation
// number directly into their name. types.SplitVargenSuffix then
// knows to look for this.