diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/writer.go')
-rw-r--r-- | src/cmd/compile/internal/noder/writer.go | 3003 |
1 files changed, 3003 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go new file mode 100644 index 0000000..e5894c9 --- /dev/null +++ b/src/cmd/compile/internal/noder/writer.go @@ -0,0 +1,3003 @@ +// 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/buildcfg" + "internal/pkgbits" + "os" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types" + "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. + +// 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 + + // Indices for previously written syntax and types2 things. + + posBasesIdx map[*syntax.PosBase]pkgbits.Index + pkgsIdx map[*types2.Package]pkgbits.Index + typsIdx map[types2.Type]pkgbits.Index + objsIdx map[types2.Object]pkgbits.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) *pkgWriter { + return &pkgWriter{ + PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), + + m: m, + curpkg: pkg, + info: info, + + pkgsIdx: make(map[*types2.Package]pkgbits.Index), + objsIdx: make(map[types2.Object]pkgbits.Index), + typsIdx: make(map[types2.Type]pkgbits.Index), + + posBasesIdx: make(map[*syntax.PosBase]pkgbits.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 []*types2.TypeName + + // derived is a slice of type indices for computing derived types + // (i.e., types that depend on the declaration's type parameters). + derived []derivedInfo + + // derivedIdx maps a Type to its corresponding index within the + // derived slice, if present. + derivedIdx map[types2.Type]pkgbits.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 types2.Unalias(implicit.Type()).(*types2.TypeParam) == typ { + return idx + } + } + + return len(dict.implicits) + typ.Index() +} + +// A derivedInfo represents a reference to an encoded generic Go type. +type derivedInfo struct { + idx pkgbits.Index + 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. +type typeInfo struct { + idx pkgbits.Index + derived bool +} + +// An objInfo represents a reference to an encoded, instantiated (if +// applicable) Go object. +type objInfo struct { + idx pkgbits.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 { + pkgIdx pkgbits.Index + nameIdx pkgbits.Index +} + +// 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 { + return &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) pkgbits.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.pos(b) + 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 pkgbits.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) pkgbits.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) { + w.typInfo(w.p.typIdx(typ, w.dict)) +} + +// typInfo writes a use of the given type (specified as a typeInfo +// instead) into the bitstream. +func (w *writer) typInfo(info typeInfo) { + w.Sync(pkgbits.SyncType) + if w.Bool(info.derived) { + w.Len(int(info.idx)) + 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. +func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo { + if idx, ok := pw.typsIdx[typ]; ok { + return typeInfo{idx: idx, derived: false} + } + if dict != nil { + if idx, ok := dict.derivedIdx[typ]; ok { + return typeInfo{idx: idx, derived: true} + } + } + + w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx) + w.dict = dict + + switch typ := types2.Unalias(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()) + assert(obj.Type() == typ) + + w.Code(pkgbits.TypeNamed) + w.obj(obj, nil) + } + + case *types2.Named: + obj, targs := splitNamed(typ) + + // Defined 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.Code(pkgbits.TypeNamed) + w.obj(obj, targs) + + 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: + 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. + if typ == 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 := pkgbits.Index(len(dict.derived)) + dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()}) + dict.derivedIdx[typ] = idx + return typeInfo{idx: idx, derived: true} + } + + pw.typsIdx[typ] = w.Idx + return typeInfo{idx: w.Flush(), derived: false} +} + +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()) + for i := 0; i < typ.Len(); i++ { + t := typ.Term(i) + w.Bool(t.Tilde()) + 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)) +} + +// objInfo writes a use of the given encoded object into the +// bitstream. +func (w *writer) objInfo(info objInfo) { + w.Sync(pkgbits.SyncObject) + w.Bool(false) // TODO(mdempsky): Remove; was derived func inst. + 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) pkgbits.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]pkgbits.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) + wdict.Flush() + + return w.Idx +} + +// 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.typ(obj.Type()) + 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) + w.typ(obj.Type()) + return pkgbits.ObjAlias + } + + named := obj.Type().(*types2.Named) + assert(named.TypeArgs() == nil) + + w.pos(obj) + 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) + ntparams := tparams.Len() + w.Len(ntparams) + for i := 0; i < ntparams; i++ { + 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 { + tparam := types2.Unalias(implicit.Type()).(*types2.TypeParam) + w.Bool(tparam.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++ { + 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. + // + // TODO(mdempsky): Find a better solution; this is terrible. + name = fmt.Sprintf("%s·%v", name, decl.gen) + } + } + + w.pkg(obj.Pkg()) + w.String(name) +} + +// TODO(mdempsky): We should be able to omit pkg from both localIdent +// and selector, because they should always be known from context. +// However, past frustrations with this optimization in iexport make +// me a little nervous to try it again. + +// localIdent writes the name of a locally declared object (i.e., +// objects that can only be accessed by non-qualified name, within the +// context of a particular function). +func (w *writer) localIdent(obj types2.Object) { + assert(!isGlobal(obj)) + w.Sync(pkgbits.SyncLocalIdent) + w.pkg(obj.Pkg()) + w.String(obj.Name()) +} + +// selector writes the name of a field or method (i.e., objects that +// can only be accessed using selector expressions). +func (w *writer) selector(obj types2.Object) { + w.selectorInfo(w.p.selectorIdx(obj)) +} + +func (w *writer) selectorInfo(info selectorInfo) { + w.Sync(pkgbits.SyncSelector) + w.pkgRef(info.pkgIdx) + w.StringRef(info.nameIdx) +} + +func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo { + pkgIdx := pw.pkgIdx(obj.Pkg()) + nameIdx := pw.StringIdx(obj.Name()) + return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx} +} + +// @@@ Compiler extensions + +func (w *writer) funcExt(obj *types2.Func) { + decl, ok := w.p.funDecls[obj] + assert(ok) + + // TODO(mdempsky): Extend these pragma validation flags to account + // for generics. E.g., linkname probably doesn't make sense at + // least. + + pragma := asPragmaFlag(decl.Pragma) + if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 { + w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined") + } + wi := asWasmImport(decl.Pragma) + + if decl.Body != nil { + if pragma&ir.Noescape != 0 { + w.p.errorf(decl, "can only use //go:noescape with external func implementations") + } + if wi != nil { + w.p.errorf(decl, "can only use //go:wasmimport with external func implementations") + } + if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 { + // Stack growth can't handle uintptr arguments that may + // be pointers (as we don't know which are pointers + // when creating the stack map). Thus uintptrkeepalive + // functions (and all transitive callees) must be + // nosplit. + // + // N.B. uintptrescapes implies uintptrkeepalive but it + // is OK since the arguments must escape to the heap. + // + // TODO(prattmic): Add recursive nosplit check of callees. + // TODO(prattmic): Functions with no body (i.e., + // assembly) must also be nosplit, but we can't check + // that here. + w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit") + } + } else { + if base.Flag.Complete || decl.Name.Value == "init" { + // Linknamed functions are allowed to have no body. Hopefully + // the linkname target has a body. See issue 23311. + // Wasmimport functions are also allowed to have no body. + if _, ok := w.p.linknames[obj]; !ok && wi == nil { + w.p.errorf(decl, "missing function body") + } + } + } + + sig, block := obj.Type().(*types2.Signature), decl.Body + body, closureVars := w.p.bodyIdx(sig, block, w.dict) + if len(closureVars) > 0 { + fmt.Fprintln(os.Stderr, "CLOSURE", closureVars) + } + assert(len(closureVars) == 0) + + w.Sync(pkgbits.SyncFuncExt) + w.pragmaFlag(pragma) + w.linkname(obj) + + if buildcfg.GOARCH == "wasm" { + if wi != nil { + w.String(wi.Module) + w.String(wi.Name) + } else { + w.String("") + w.String("") + } + } + + w.Bool(false) // stub extension + w.Reloc(pkgbits.RelocBody, body) + w.Sync(pkgbits.SyncEOF) +} + +func (w *writer) typeExt(obj *types2.TypeName) { + decl, ok := w.p.typDecls[obj] + assert(ok) + + w.Sync(pkgbits.SyncTypeExt) + + w.pragmaFlag(asPragmaFlag(decl.Pragma)) + + // No LSym.SymIdx info yet. + w.Int64(-1) + w.Int64(-1) +} + +func (w *writer) varExt(obj *types2.Var) { + w.Sync(pkgbits.SyncVarExt) + w.linkname(obj) +} + +func (w *writer) linkname(obj types2.Object) { + w.Sync(pkgbits.SyncLinkname) + w.Int64(-1) + w.String(w.p.linknames[obj]) +} + +func (w *writer) pragmaFlag(p ir.PragmaFlag) { + w.Sync(pkgbits.SyncPragma) + w.Int(int(p)) +} + +// @@@ Function bodies + +// bodyIdx returns the index for the given function body (specified by +// block), adding it to the export data +func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) { + w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody) + w.sig = sig + w.dict = dict + + w.declareParams(sig) + if w.Bool(block != nil) { + w.stmts(block.List) + w.pos(block.Rbrace) + } + + return w.Flush(), w.closureVars +} + +func (w *writer) declareParams(sig *types2.Signature) { + addLocals := func(params *types2.Tuple) { + for i := 0; i < params.Len(); i++ { + w.addLocal(params.At(i)) + } + } + + if recv := sig.Recv(); recv != nil { + w.addLocal(recv) + } + addLocals(sig.Params()) + addLocals(sig.Results()) +} + +// addLocal records the declaration of a new local variable. +func (w *writer) addLocal(obj *types2.Var) { + idx := len(w.localsIdx) + + w.Sync(pkgbits.SyncAddLocal) + if w.p.SyncMarkers() { + w.Int(idx) + } + w.varDictIndex(obj) + + if w.localsIdx == nil { + w.localsIdx = make(map[*types2.Var]int) + } + w.localsIdx[obj] = idx +} + +// useLocal writes a reference to the given local or free variable +// into the bitstream. +func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { + w.Sync(pkgbits.SyncUseObjLocal) + + if idx, ok := w.localsIdx[obj]; w.Bool(ok) { + w.Len(idx) + return + } + + idx, ok := w.closureVarsIdx[obj] + if !ok { + if w.closureVarsIdx == nil { + w.closureVarsIdx = make(map[*types2.Var]int) + } + idx = len(w.closureVars) + w.closureVars = append(w.closureVars, posVar{pos, obj}) + w.closureVarsIdx[obj] = idx + } + w.Len(idx) +} + +func (w *writer) openScope(pos syntax.Pos) { + w.Sync(pkgbits.SyncOpenScope) + w.pos(pos) +} + +func (w *writer) closeScope(pos syntax.Pos) { + w.Sync(pkgbits.SyncCloseScope) + w.pos(pos) + w.closeAnotherScope() +} + +func (w *writer) closeAnotherScope() { + w.Sync(pkgbits.SyncCloseAnotherScope) +} + +// @@@ Statements + +// stmt writes the given statement into the function body bitstream. +func (w *writer) stmt(stmt syntax.Stmt) { + var stmts []syntax.Stmt + if stmt != nil { + stmts = []syntax.Stmt{stmt} + } + w.stmts(stmts) +} + +func (w *writer) stmts(stmts []syntax.Stmt) { + dead := false + w.Sync(pkgbits.SyncStmts) + for _, stmt := range stmts { + if dead { + // Any statements after a terminating statement are safe to + // omit, at least until the next labeled statement. + if _, ok := stmt.(*syntax.LabeledStmt); !ok { + continue + } + } + w.stmt1(stmt) + dead = w.p.terminates(stmt) + } + w.Code(stmtEnd) + w.Sync(pkgbits.SyncStmtsEnd) +} + +func (w *writer) stmt1(stmt syntax.Stmt) { + switch stmt := stmt.(type) { + default: + w.p.unexpected("statement", stmt) + + case nil, *syntax.EmptyStmt: + return + + case *syntax.AssignStmt: + switch { + case stmt.Rhs == nil: + w.Code(stmtIncDec) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + + case stmt.Op != 0 && stmt.Op != syntax.Def: + w.Code(stmtAssignOp) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + + var typ types2.Type + if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr { + typ = w.p.typeOf(stmt.Lhs) + } + w.implicitConvExpr(typ, stmt.Rhs) + + default: + w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) + } + + case *syntax.BlockStmt: + w.Code(stmtBlock) + w.blockStmt(stmt) + + case *syntax.BranchStmt: + w.Code(stmtBranch) + w.pos(stmt) + w.op(branchOps[stmt.Tok]) + w.optLabel(stmt.Label) + + case *syntax.CallStmt: + w.Code(stmtCall) + w.pos(stmt) + w.op(callOps[stmt.Tok]) + w.expr(stmt.Call) + if stmt.Tok == syntax.Defer { + w.optExpr(stmt.DeferAt) + } + + case *syntax.DeclStmt: + for _, decl := range stmt.DeclList { + w.declStmt(decl) + } + + case *syntax.ExprStmt: + w.Code(stmtExpr) + w.expr(stmt.X) + + case *syntax.ForStmt: + w.Code(stmtFor) + w.forStmt(stmt) + + case *syntax.IfStmt: + w.Code(stmtIf) + w.ifStmt(stmt) + + case *syntax.LabeledStmt: + w.Code(stmtLabel) + w.pos(stmt) + w.label(stmt.Label) + w.stmt1(stmt.Stmt) + + case *syntax.ReturnStmt: + w.Code(stmtReturn) + w.pos(stmt) + + resultTypes := w.sig.Results() + dstType := func(i int) types2.Type { + return resultTypes.At(i).Type() + } + w.multiExpr(stmt, dstType, syntax.UnpackListExpr(stmt.Results)) + + case *syntax.SelectStmt: + w.Code(stmtSelect) + w.selectStmt(stmt) + + case *syntax.SendStmt: + chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan) + + w.Code(stmtSend) + w.pos(stmt) + w.expr(stmt.Chan) + w.implicitConvExpr(chanType.Elem(), stmt.Value) + + case *syntax.SwitchStmt: + w.Code(stmtSwitch) + w.switchStmt(stmt) + } +} + +func (w *writer) assignList(expr syntax.Expr) { + exprs := syntax.UnpackListExpr(expr) + w.Len(len(exprs)) + + for _, expr := range exprs { + w.assign(expr) + } +} + +func (w *writer) assign(expr syntax.Expr) { + expr = syntax.Unparen(expr) + + if name, ok := expr.(*syntax.Name); ok { + if name.Value == "_" { + w.Code(assignBlank) + return + } + + if obj, ok := w.p.info.Defs[name]; ok { + obj := obj.(*types2.Var) + + w.Code(assignDef) + w.pos(obj) + w.localIdent(obj) + w.typ(obj.Type()) + + // TODO(mdempsky): Minimize locals index size by deferring + // this until the variables actually come into scope. + w.addLocal(obj) + return + } + } + + w.Code(assignExpr) + w.expr(expr) +} + +func (w *writer) declStmt(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ConstDecl, *syntax.TypeDecl: + + case *syntax.VarDecl: + w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values) + } +} + +// assignStmt writes out an assignment for "lhs = rhs". +func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { + lhs := syntax.UnpackListExpr(lhs0) + rhs := syntax.UnpackListExpr(rhs0) + + w.Code(stmtAssign) + w.pos(pos) + + // As if w.assignList(lhs0). + w.Len(len(lhs)) + for _, expr := range lhs { + w.assign(expr) + } + + dstType := func(i int) types2.Type { + dst := lhs[i] + + // Finding dstType is somewhat involved, because for VarDecl + // statements, the Names are only added to the info.{Defs,Uses} + // maps, not to info.Types. + if name, ok := syntax.Unparen(dst).(*syntax.Name); ok { + if name.Value == "_" { + return nil // ok: no implicit conversion + } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { + return def.Type() + } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { + return use.Type() + } else { + w.p.fatalf(dst, "cannot find type of destination object: %v", dst) + } + } + + return w.p.typeOf(dst) + } + + w.multiExpr(pos, dstType, rhs) +} + +func (w *writer) blockStmt(stmt *syntax.BlockStmt) { + w.Sync(pkgbits.SyncBlockStmt) + w.openScope(stmt.Pos()) + w.stmts(stmt.List) + w.closeScope(stmt.Rbrace) +} + +func (w *writer) forStmt(stmt *syntax.ForStmt) { + w.Sync(pkgbits.SyncForStmt) + w.openScope(stmt.Pos()) + + if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { + w.pos(rang) + w.assignList(rang.Lhs) + w.expr(rang.X) + + xtyp := w.p.typeOf(rang.X) + if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap { + w.rtype(xtyp) + } + { + lhs := syntax.UnpackListExpr(rang.Lhs) + assign := func(i int, src types2.Type) { + if i >= len(lhs) { + return + } + dst := syntax.Unparen(lhs[i]) + if name, ok := dst.(*syntax.Name); ok && name.Value == "_" { + return + } + + var dstType types2.Type + if rang.Def { + // For `:=` assignments, the LHS names only appear in Defs, + // not Types (as used by typeOf). + dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type() + } else { + dstType = w.p.typeOf(dst) + } + + w.convRTTI(src, dstType) + } + + keyType, valueType := types2.RangeKeyVal(w.p.typeOf(rang.X)) + assign(0, keyType) + assign(1, valueType) + } + + } else { + if stmt.Cond != nil && w.p.staticBool(&stmt.Cond) < 0 { // always false + stmt.Post = nil + stmt.Body.List = nil + } + + w.pos(stmt) + w.stmt(stmt.Init) + w.optExpr(stmt.Cond) + w.stmt(stmt.Post) + } + + w.blockStmt(stmt.Body) + w.Bool(w.distinctVars(stmt)) + w.closeAnotherScope() +} + +func (w *writer) distinctVars(stmt *syntax.ForStmt) bool { + lv := base.Debug.LoopVar + fileVersion := w.p.info.FileVersions[stmt.Pos().Base()] + is122 := fileVersion == "" || version.Compare(fileVersion, "go1.22") >= 0 + + // Turning off loopvar for 1.22 is only possible with loopvarhash=qn + // + // Debug.LoopVar values to be preserved for 1.21 compatibility are 1 and 2, + // which are also set (=1) by GOEXPERIMENT=loopvar. The knobs for turning on + // the new, unshared, loopvar behavior apply to versions less than 1.21 because + // (1) 1.21 also did that and (2) this is believed to be the likely use case; + // anyone checking to see if it affects their code will just run the GOEXPERIMENT + // but will not also update all their go.mod files to 1.21. + // + // -gcflags=-d=loopvar=3 enables logging for 1.22 but does not turn loopvar on for <= 1.21. + + return is122 || lv > 0 && lv != 3 +} + +func (w *writer) ifStmt(stmt *syntax.IfStmt) { + cond := w.p.staticBool(&stmt.Cond) + + w.Sync(pkgbits.SyncIfStmt) + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + w.expr(stmt.Cond) + w.Int(cond) + if cond >= 0 { + w.blockStmt(stmt.Then) + } else { + w.pos(stmt.Then.Rbrace) + } + if cond <= 0 { + w.stmt(stmt.Else) + } + w.closeAnotherScope() +} + +func (w *writer) selectStmt(stmt *syntax.SelectStmt) { + w.Sync(pkgbits.SyncSelectStmt) + + w.pos(stmt) + w.Len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + w.stmt(clause.Comm) + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } +} + +func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { + w.Sync(pkgbits.SyncSwitchStmt) + + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + + var iface, tagType types2.Type + if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { + iface = w.p.typeOf(guard.X) + + w.pos(guard) + if tag := guard.Lhs; w.Bool(tag != nil) { + w.pos(tag) + + // Like w.localIdent, but we don't have a types2.Object. + w.Sync(pkgbits.SyncLocalIdent) + w.pkg(w.p.curpkg) + w.String(tag.Value) + } + w.expr(guard.X) + } else { + tag := stmt.Tag + + var tagValue constant.Value + if tag != nil { + tv := w.p.typeAndValue(tag) + tagType = tv.Type + tagValue = tv.Value + } else { + tagType = types2.Typ[types2.Bool] + tagValue = constant.MakeBool(true) + } + + if tagValue != nil { + // If the switch tag has a constant value, look for a case + // clause that we always branch to. + func() { + var target *syntax.CaseClause + Outer: + for _, clause := range stmt.Body { + if clause.Cases == nil { + target = clause + } + for _, cas := range syntax.UnpackListExpr(clause.Cases) { + tv := w.p.typeAndValue(cas) + if tv.Value == nil { + return // non-constant case; give up + } + if constant.Compare(tagValue, token.EQL, tv.Value) { + target = clause + break Outer + } + } + } + // We've found the target clause, if any. + + if target != nil { + if hasFallthrough(target.Body) { + return // fallthrough is tricky; give up + } + + // Rewrite as single "default" case. + target.Cases = nil + stmt.Body = []*syntax.CaseClause{target} + } else { + stmt.Body = nil + } + + // Clear switch tag (i.e., replace with implicit "true"). + tag = nil + stmt.Tag = nil + tagType = types2.Typ[types2.Bool] + }() + } + + // Walk is going to emit comparisons between the tag value and + // each case expression, and we want these comparisons to always + // have the same type. If there are any case values that can't be + // converted to the tag value's type, then convert everything to + // `any` instead. + Outer: + for _, clause := range stmt.Body { + for _, cas := range syntax.UnpackListExpr(clause.Cases) { + if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { + tagType = types2.NewInterfaceType(nil, nil) + break Outer + } + } + } + + if w.Bool(tag != nil) { + w.implicitConvExpr(tagType, tag) + } + } + + w.Len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + + cases := syntax.UnpackListExpr(clause.Cases) + if iface != nil { + w.Len(len(cases)) + for _, cas := range cases { + if w.Bool(isNil(w.p, cas)) { + continue + } + w.exprType(iface, cas) + } + } else { + // As if w.exprList(clause.Cases), + // but with implicit conversions to tagType. + + w.Sync(pkgbits.SyncExprList) + w.Sync(pkgbits.SyncExprs) + w.Len(len(cases)) + for _, cas := range cases { + w.implicitConvExpr(tagType, cas) + } + } + + if obj, ok := w.p.info.Implicits[clause]; ok { + // TODO(mdempsky): These pos details are quirkish, but also + // necessary so the variable's position is correct for DWARF + // scope assignment later. It would probably be better for us to + // instead just set the variable's DWARF scoping info earlier so + // we can give it the correct position information. + pos := clause.Pos() + if typs := syntax.UnpackListExpr(clause.Cases); len(typs) != 0 { + pos = typeExprEndPos(typs[len(typs)-1]) + } + w.pos(pos) + + obj := obj.(*types2.Var) + w.typ(obj.Type()) + w.addLocal(obj) + } + + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } + + w.closeScope(stmt.Rbrace) +} + +func (w *writer) label(label *syntax.Name) { + w.Sync(pkgbits.SyncLabel) + + // TODO(mdempsky): Replace label strings with dense indices. + w.String(label.Value) +} + +func (w *writer) optLabel(label *syntax.Name) { + w.Sync(pkgbits.SyncOptLabel) + if w.Bool(label != nil) { + w.label(label) + } +} + +// @@@ Expressions + +// expr writes the given expression into the function body bitstream. +func (w *writer) expr(expr syntax.Expr) { + base.Assertf(expr != nil, "missing expression") + + expr = syntax.Unparen(expr) // skip parens; unneeded after typecheck + + obj, inst := lookupObj(w.p, expr) + targs := inst.TypeArgs + + if tv, ok := w.p.maybeTypeAndValue(expr); ok { + if tv.IsRuntimeHelper() { + if pkg := obj.Pkg(); pkg != nil && pkg.Name() == "runtime" { + objName := obj.Name() + w.Code(exprRuntimeBuiltin) + w.String(objName) + return + } + } + + if tv.IsType() { + w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr)) + } + + if tv.Value != nil { + w.Code(exprConst) + w.pos(expr) + typ := idealType(tv) + assert(typ != nil) + w.typ(typ) + w.Value(tv.Value) + return + } + + if _, isNil := obj.(*types2.Nil); isNil { + w.Code(exprZero) + w.pos(expr) + w.typ(tv.Type) + return + } + + // With shape types (and particular pointer shaping), we may have + // an expression of type "go.shape.*uint8", but need to reshape it + // to another shape-identical type to allow use in field + // selection, indexing, etc. + if typ := tv.Type; !tv.IsBuiltin() && !isTuple(typ) && !isUntyped(typ) { + w.Code(exprReshape) + w.typ(typ) + // fallthrough + } + } + + if obj != nil { + if targs.Len() != 0 { + obj := obj.(*types2.Func) + + w.Code(exprFuncInst) + w.pos(expr) + w.funcInst(obj, targs) + return + } + + if isGlobal(obj) { + w.Code(exprGlobal) + w.obj(obj, nil) + return + } + + obj := obj.(*types2.Var) + assert(!obj.IsField()) + + w.Code(exprLocal) + w.useLocal(expr.Pos(), obj) + return + } + + switch expr := expr.(type) { + default: + w.p.unexpected("expression", expr) + + case *syntax.CompositeLit: + w.Code(exprCompLit) + w.compLit(expr) + + case *syntax.FuncLit: + w.Code(exprFuncLit) + w.funcLit(expr) + + case *syntax.SelectorExpr: + sel, ok := w.p.info.Selections[expr] + assert(ok) + + switch sel.Kind() { + default: + w.p.fatalf(expr, "unexpected selection kind: %v", sel.Kind()) + + case types2.FieldVal: + w.Code(exprFieldVal) + w.expr(expr.X) + w.pos(expr) + w.selector(sel.Obj()) + + case types2.MethodVal: + w.Code(exprMethodVal) + typ := w.recvExpr(expr, sel) + w.pos(expr) + w.methodExpr(expr, typ, sel) + + case types2.MethodExpr: + w.Code(exprMethodExpr) + + tv := w.p.typeAndValue(expr.X) + assert(tv.IsType()) + + index := sel.Index() + implicits := index[:len(index)-1] + + typ := tv.Type + w.typ(typ) + + w.Len(len(implicits)) + for _, ix := range implicits { + w.Len(ix) + typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type() + } + + recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type() + if w.Bool(isPtrTo(typ, recv)) { // need deref + typ = recv + } else if w.Bool(isPtrTo(recv, typ)) { // need addr + typ = recv + } + + w.pos(expr) + w.methodExpr(expr, typ, sel) + } + + case *syntax.IndexExpr: + _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation + + xtyp := w.p.typeOf(expr.X) + + var keyType types2.Type + if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok { + keyType = mapType.Key() + } + + w.Code(exprIndex) + w.expr(expr.X) + w.pos(expr) + w.implicitConvExpr(keyType, expr.Index) + if keyType != nil { + w.rtype(xtyp) + } + + case *syntax.SliceExpr: + w.Code(exprSlice) + w.expr(expr.X) + w.pos(expr) + for _, n := range &expr.Index { + w.optExpr(n) + } + + case *syntax.AssertExpr: + iface := w.p.typeOf(expr.X) + + w.Code(exprAssert) + w.expr(expr.X) + w.pos(expr) + w.exprType(iface, expr.Type) + w.rtype(iface) + + case *syntax.Operation: + if expr.Y == nil { + w.Code(exprUnaryOp) + w.op(unOps[expr.Op]) + w.pos(expr) + w.expr(expr.X) + break + } + + var commonType types2.Type + switch expr.Op { + case syntax.Shl, syntax.Shr: + // ok: operands are allowed to have different types + default: + xtyp := w.p.typeOf(expr.X) + ytyp := w.p.typeOf(expr.Y) + switch { + case types2.AssignableTo(xtyp, ytyp): + commonType = ytyp + case types2.AssignableTo(ytyp, xtyp): + commonType = xtyp + default: + w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp) + } + } + + w.Code(exprBinaryOp) + w.op(binOps[expr.Op]) + w.implicitConvExpr(commonType, expr.X) + w.pos(expr) + w.implicitConvExpr(commonType, expr.Y) + + case *syntax.CallExpr: + tv := w.p.typeAndValue(expr.Fun) + if tv.IsType() { + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + w.convertExpr(tv.Type, expr.ArgList[0], false) + break + } + + var rtype types2.Type + if tv.IsBuiltin() { + switch obj, _ := lookupObj(w.p, syntax.Unparen(expr.Fun)); obj.Name() { + case "make": + assert(len(expr.ArgList) >= 1) + assert(!expr.HasDots) + + w.Code(exprMake) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + w.exprs(expr.ArgList[1:]) + + typ := w.p.typeOf(expr) + switch coreType := types2.CoreType(typ).(type) { + default: + w.p.fatalf(expr, "unexpected core type: %v", coreType) + case *types2.Chan: + w.rtype(typ) + case *types2.Map: + w.rtype(typ) + case *types2.Slice: + w.rtype(sliceElem(typ)) + } + + return + + case "new": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprNew) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + return + + case "Sizeof": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprSizeof) + w.pos(expr) + w.typ(w.p.typeOf(expr.ArgList[0])) + return + + case "Alignof": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprAlignof) + w.pos(expr) + w.typ(w.p.typeOf(expr.ArgList[0])) + return + + case "Offsetof": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + selector := syntax.Unparen(expr.ArgList[0]).(*syntax.SelectorExpr) + index := w.p.info.Selections[selector].Index() + + w.Code(exprOffsetof) + w.pos(expr) + w.typ(deref2(w.p.typeOf(selector.X))) + w.Len(len(index) - 1) + for _, idx := range index { + w.Len(idx) + } + return + + case "append": + rtype = sliceElem(w.p.typeOf(expr)) + case "copy": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())" + typ = tuple.At(0).Type() + } + rtype = sliceElem(typ) + case "delete": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())" + typ = tuple.At(0).Type() + } + rtype = typ + case "Slice": + rtype = sliceElem(w.p.typeOf(expr)) + } + } + + writeFunExpr := func() { + fun := syntax.Unparen(expr.Fun) + + if selector, ok := fun.(*syntax.SelectorExpr); ok { + if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { + w.Bool(true) // method call + typ := w.recvExpr(selector, sel) + w.methodExpr(selector, typ, sel) + return + } + } + + w.Bool(false) // not a method call (i.e., normal function call) + + if obj, inst := lookupObj(w.p, fun); w.Bool(obj != nil && inst.TypeArgs.Len() != 0) { + obj := obj.(*types2.Func) + + w.pos(fun) + w.funcInst(obj, inst.TypeArgs) + return + } + + w.expr(fun) + } + + sigType := types2.CoreType(tv.Type).(*types2.Signature) + paramTypes := sigType.Params() + + w.Code(exprCall) + writeFunExpr() + w.pos(expr) + + paramType := func(i int) types2.Type { + if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 { + return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() + } + return paramTypes.At(i).Type() + } + + w.multiExpr(expr, paramType, expr.ArgList) + w.Bool(expr.HasDots) + if rtype != nil { + w.rtype(rtype) + } + } +} + +func sliceElem(typ types2.Type) types2.Type { + return types2.CoreType(typ).(*types2.Slice).Elem() +} + +func (w *writer) optExpr(expr syntax.Expr) { + if w.Bool(expr != nil) { + w.expr(expr) + } +} + +// recvExpr writes out expr.X, but handles any implicit addressing, +// dereferencing, and field selections appropriate for the method +// selection. +func (w *writer) recvExpr(expr *syntax.SelectorExpr, sel *types2.Selection) types2.Type { + index := sel.Index() + implicits := index[:len(index)-1] + + w.Code(exprRecv) + w.expr(expr.X) + w.pos(expr) + w.Len(len(implicits)) + + typ := w.p.typeOf(expr.X) + for _, ix := range implicits { + typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type() + w.Len(ix) + } + + recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type() + if w.Bool(isPtrTo(typ, recv)) { // needs deref + typ = recv + } else if w.Bool(isPtrTo(recv, typ)) { // needs addr + typ = recv + } + + return typ +} + +// funcInst writes a reference to an instantiated function. +func (w *writer) funcInst(obj *types2.Func, targs *types2.TypeList) { + info := w.p.objInstIdx(obj, targs, w.dict) + + // Type arguments list contains derived types; we can emit a static + // call to the shaped function, but need to dynamically compute the + // runtime dictionary pointer. + if w.Bool(info.anyDerived()) { + w.Len(w.dict.subdictIdx(info)) + return + } + + // Type arguments list is statically known; we can emit a static + // call with a statically reference to the respective runtime + // dictionary. + w.objInfo(info) +} + +// methodExpr writes out a reference to the method selected by +// expr. sel should be the corresponding types2.Selection, and recv +// the type produced after any implicit addressing, dereferencing, and +// field selection. (Note: recv might differ from sel.Obj()'s receiver +// parameter in the case of interface types, and is needed for +// handling type parameter methods.) +func (w *writer) methodExpr(expr *syntax.SelectorExpr, recv types2.Type, sel *types2.Selection) { + fun := sel.Obj().(*types2.Func) + sig := fun.Type().(*types2.Signature) + + w.typ(recv) + w.typ(sig) + w.pos(expr) + w.selector(fun) + + // Method on a type parameter. These require an indirect call + // through the current function's runtime dictionary. + if typeParam, ok := types2.Unalias(recv).(*types2.TypeParam); w.Bool(ok) { + typeParamIdx := w.dict.typeParamIndex(typeParam) + methodInfo := w.p.selectorIdx(fun) + + w.Len(w.dict.typeParamMethodExprIdx(typeParamIdx, methodInfo)) + return + } + + if isInterface(recv) != isInterface(sig.Recv().Type()) { + w.p.fatalf(expr, "isInterface inconsistency: %v and %v", recv, sig.Recv().Type()) + } + + if !isInterface(recv) { + if named, ok := types2.Unalias(deref2(recv)).(*types2.Named); ok { + obj, targs := splitNamed(named) + info := w.p.objInstIdx(obj, targs, w.dict) + + // Method on a derived receiver type. These can be handled by a + // static call to the shaped method, but require dynamically + // looking up the appropriate dictionary argument in the current + // function's runtime dictionary. + if w.p.hasImplicitTypeParams(obj) || info.anyDerived() { + w.Bool(true) // dynamic subdictionary + w.Len(w.dict.subdictIdx(info)) + return + } + + // Method on a fully known receiver type. These can be handled + // by a static call to the shaped method, and with a static + // reference to the receiver type's dictionary. + if targs.Len() != 0 { + w.Bool(false) // no dynamic subdictionary + w.Bool(true) // static dictionary + w.objInfo(info) + return + } + } + } + + w.Bool(false) // no dynamic subdictionary + w.Bool(false) // no static dictionary +} + +// multiExpr writes a sequence of expressions, where the i'th value is +// implicitly converted to dstType(i). It also handles when exprs is a +// single, multi-valued expression (e.g., the multi-valued argument in +// an f(g()) call, or the RHS operand in a comma-ok assignment). +func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) { + w.Sync(pkgbits.SyncMultiExpr) + + if len(exprs) == 1 { + expr := exprs[0] + if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok { + assert(tuple.Len() > 1) + w.Bool(true) // N:1 assignment + w.pos(pos) + w.expr(expr) + + w.Len(tuple.Len()) + for i := 0; i < tuple.Len(); i++ { + src := tuple.At(i).Type() + // TODO(mdempsky): Investigate not writing src here. I think + // the reader should be able to infer it from expr anyway. + w.typ(src) + if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) { + if src == nil || dst == nil { + w.p.fatalf(pos, "src is %v, dst is %v", src, dst) + } + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.typ(dst) + w.convRTTI(src, dst) + } + } + return + } + } + + w.Bool(false) // N:N assignment + w.Len(len(exprs)) + for i, expr := range exprs { + w.implicitConvExpr(dstType(i), expr) + } +} + +// implicitConvExpr is like expr, but if dst is non-nil and different +// from expr's type, then an implicit conversion operation is inserted +// at expr's position. +func (w *writer) implicitConvExpr(dst types2.Type, expr syntax.Expr) { + w.convertExpr(dst, expr, true) +} + +func (w *writer) convertExpr(dst types2.Type, expr syntax.Expr, implicit bool) { + src := w.p.typeOf(expr) + + // Omit implicit no-op conversions. + identical := dst == nil || types2.Identical(src, dst) + if implicit && identical { + w.expr(expr) + return + } + + if implicit && !types2.AssignableTo(src, dst) { + w.p.fatalf(expr, "%v is not assignable to %v", src, dst) + } + + w.Code(exprConvert) + w.Bool(implicit) + w.typ(dst) + w.pos(expr) + w.convRTTI(src, dst) + w.Bool(isTypeParam(dst)) + w.Bool(identical) + w.expr(expr) +} + +func (w *writer) compLit(lit *syntax.CompositeLit) { + typ := w.p.typeOf(lit) + + w.Sync(pkgbits.SyncCompLit) + w.pos(lit) + w.typ(typ) + + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { + typ = ptr.Elem() + } + var keyType, elemType types2.Type + var structType *types2.Struct + switch typ0 := typ; typ := types2.CoreType(typ).(type) { + default: + w.p.fatalf(lit, "unexpected composite literal type: %v", typ) + case *types2.Array: + elemType = typ.Elem() + case *types2.Map: + w.rtype(typ0) + keyType, elemType = typ.Key(), typ.Elem() + case *types2.Slice: + elemType = typ.Elem() + case *types2.Struct: + structType = typ + } + + w.Len(len(lit.ElemList)) + for i, elem := range lit.ElemList { + elemType := elemType + if structType != nil { + if kv, ok := elem.(*syntax.KeyValueExpr); ok { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name)) + elem = kv.Value + } else { + w.pos(elem) + } + elemType = structType.Field(i).Type() + w.Len(i) + } else { + if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + w.implicitConvExpr(keyType, kv.Key) + elem = kv.Value + } + } + w.pos(elem) + w.implicitConvExpr(elemType, elem) + } +} + +func (w *writer) funcLit(expr *syntax.FuncLit) { + sig := w.p.typeOf(expr).(*types2.Signature) + + body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict) + + w.Sync(pkgbits.SyncFuncLit) + w.pos(expr) + w.signature(sig) + + w.Len(len(closureVars)) + for _, cv := range closureVars { + w.pos(cv.pos) + w.useLocal(cv.pos, cv.var_) + } + + w.Reloc(pkgbits.RelocBody, body) +} + +type posVar struct { + pos syntax.Pos + var_ *types2.Var +} + +func (p posVar) String() string { + return p.pos.String() + ":" + p.var_.String() +} + +func (w *writer) exprList(expr syntax.Expr) { + w.Sync(pkgbits.SyncExprList) + w.exprs(syntax.UnpackListExpr(expr)) +} + +func (w *writer) exprs(exprs []syntax.Expr) { + w.Sync(pkgbits.SyncExprs) + w.Len(len(exprs)) + for _, expr := range exprs { + w.expr(expr) + } +} + +// rtype writes information so that the reader can construct an +// expression of type *runtime._type representing typ. +func (w *writer) rtype(typ types2.Type) { + typ = types2.Default(typ) + + info := w.p.typIdx(typ, w.dict) + w.rtypeInfo(info) +} + +func (w *writer) rtypeInfo(info typeInfo) { + w.Sync(pkgbits.SyncRType) + + if w.Bool(info.derived) { + w.Len(w.dict.rtypeIdx(info)) + } else { + w.typInfo(info) + } +} + +// varDictIndex writes out information for populating DictIndex for +// the ir.Name that will represent obj. +func (w *writer) varDictIndex(obj *types2.Var) { + info := w.p.typIdx(obj.Type(), w.dict) + if w.Bool(info.derived) { + w.Len(w.dict.rtypeIdx(info)) + } +} + +func isUntyped(typ types2.Type) bool { + basic, ok := types2.Unalias(typ).(*types2.Basic) + return ok && basic.Info()&types2.IsUntyped != 0 +} + +func isTuple(typ types2.Type) bool { + _, ok := typ.(*types2.Tuple) + return ok +} + +func (w *writer) itab(typ, iface types2.Type) { + typ = types2.Default(typ) + iface = types2.Default(iface) + + typInfo := w.p.typIdx(typ, w.dict) + ifaceInfo := w.p.typIdx(iface, w.dict) + + w.rtypeInfo(typInfo) + w.rtypeInfo(ifaceInfo) + if w.Bool(typInfo.derived || ifaceInfo.derived) { + w.Len(w.dict.itabIdx(typInfo, ifaceInfo)) + } +} + +// convRTTI writes information so that the reader can construct +// expressions for converting from src to dst. +func (w *writer) convRTTI(src, dst types2.Type) { + w.Sync(pkgbits.SyncConvRTTI) + w.itab(src, dst) +} + +func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { + base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) + + tv := w.p.typeAndValue(typ) + assert(tv.IsType()) + + w.Sync(pkgbits.SyncExprType) + w.pos(typ) + + if w.Bool(iface != nil && !iface.Underlying().(*types2.Interface).Empty()) { + w.itab(tv.Type, iface) + } else { + w.rtype(tv.Type) + + info := w.p.typIdx(tv.Type, w.dict) + w.Bool(info.derived) + } +} + +// isInterface reports whether typ is known to be an interface type. +// If typ is a type parameter, then isInterface reports an internal +// compiler error instead. +func isInterface(typ types2.Type) bool { + if _, ok := types2.Unalias(typ).(*types2.TypeParam); ok { + // typ is a type parameter and may be instantiated as either a + // concrete or interface type, so the writer can't depend on + // knowing this. + base.Fatalf("%v is a type parameter", typ) + } + + _, ok := typ.Underlying().(*types2.Interface) + return ok +} + +// op writes an Op into the bitstream. +func (w *writer) op(op ir.Op) { + // TODO(mdempsky): Remove in favor of explicit codes? Would make + // export data more stable against internal refactorings, but low + // priority at the moment. + assert(op != 0) + w.Sync(pkgbits.SyncOp) + w.Len(int(op)) +} + +// @@@ Package initialization + +// Caution: This code is still clumsy, because toolstash -cmp is +// particularly sensitive to it. + +type typeDeclGen struct { + *syntax.TypeDecl + gen int + + // Implicit type parameters in scope at this type declaration. + implicits []*types2.TypeName +} + +type fileImports struct { + importedEmbed, importedUnsafe bool +} + +// declCollector is a visitor type that collects compiler-needed +// information about declarations that types2 doesn't track. +// +// Notably, it maps declared types and functions back to their +// declaration statement, keeps track of implicit type parameters, and +// assigns unique type "generation" numbers to local defined types. +type declCollector struct { + pw *pkgWriter + typegen *int + file *fileImports + withinFunc bool + implicits []*types2.TypeName +} + +func (c *declCollector) withTParams(obj types2.Object) *declCollector { + tparams := objTypeParams(obj) + n := tparams.Len() + if n == 0 { + return c + } + + copy := *c + copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)] + for i := 0; i < n; i++ { + copy.implicits = append(copy.implicits, tparams.At(i).Obj()) + } + return © +} + +func (c *declCollector) Visit(n syntax.Node) syntax.Visitor { + pw := c.pw + + switch n := n.(type) { + case *syntax.File: + pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false) + + case *syntax.ImportDecl: + pw.checkPragmas(n.Pragma, 0, false) + + switch pw.info.PkgNameOf(n).Imported().Path() { + case "embed": + c.file.importedEmbed = true + case "unsafe": + c.file.importedUnsafe = true + } + + case *syntax.ConstDecl: + pw.checkPragmas(n.Pragma, 0, false) + + case *syntax.FuncDecl: + pw.checkPragmas(n.Pragma, funcPragmas, false) + + obj := pw.info.Defs[n.Name].(*types2.Func) + pw.funDecls[obj] = n + + return c.withTParams(obj) + + case *syntax.TypeDecl: + obj := pw.info.Defs[n.Name].(*types2.TypeName) + d := typeDeclGen{TypeDecl: n, implicits: c.implicits} + + if n.Alias { + pw.checkPragmas(n.Pragma, 0, false) + } else { + pw.checkPragmas(n.Pragma, 0, false) + + // Assign a unique ID to function-scoped defined types. + if c.withinFunc { + *c.typegen++ + d.gen = *c.typegen + } + } + + pw.typDecls[obj] = d + + // TODO(mdempsky): Omit? Not strictly necessary; only matters for + // type declarations within function literals within parameterized + // type declarations, but types2 the function literals will be + // constant folded away. + return c.withTParams(obj) + + case *syntax.VarDecl: + pw.checkPragmas(n.Pragma, 0, true) + + if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 { + if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil { + pw.errorf(p.Embeds[0].Pos, "%s", err) + } + } + + case *syntax.BlockStmt: + if !c.withinFunc { + copy := *c + copy.withinFunc = true + return © + } + } + + return c +} + +func (pw *pkgWriter) collectDecls(noders []*noder) { + var typegen int + for _, p := range noders { + var file fileImports + + syntax.Walk(p.file, &declCollector{ + pw: pw, + typegen: &typegen, + file: &file, + }) + + pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...) + + for _, l := range p.linknames { + if !file.importedUnsafe { + pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") + continue + } + + switch obj := pw.curpkg.Scope().Lookup(l.local).(type) { + case *types2.Func, *types2.Var: + if _, ok := pw.linknames[obj]; !ok { + pw.linknames[obj] = l.remote + } else { + pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local) + } + + default: + if types.AllowsGoVersion(1, 18) { + pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") + } + } + } + } +} + +func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) { + if p == nil { + return + } + pragma := p.(*pragmas) + + for _, pos := range pragma.Pos { + if pos.Flag&^allowed != 0 { + pw.errorf(pos.Pos, "misplaced compiler directive") + } + } + + if !embedOK { + for _, e := range pragma.Embeds { + pw.errorf(e.Pos, "misplaced go:embed directive") + } + } +} + +func (w *writer) pkgInit(noders []*noder) { + w.Len(len(w.p.cgoPragmas)) + for _, cgoPragma := range w.p.cgoPragmas { + w.Strings(cgoPragma) + } + + w.pkgInitOrder() + + w.Sync(pkgbits.SyncDecls) + for _, p := range noders { + for _, decl := range p.file.DeclList { + w.pkgDecl(decl) + } + } + w.Code(declEnd) + + w.Sync(pkgbits.SyncEOF) +} + +func (w *writer) pkgInitOrder() { + // TODO(mdempsky): Write as a function body instead? + w.Len(len(w.p.info.InitOrder)) + for _, init := range w.p.info.InitOrder { + w.Len(len(init.Lhs)) + for _, v := range init.Lhs { + w.obj(v, nil) + } + w.expr(init.Rhs) + } +} + +func (w *writer) pkgDecl(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ImportDecl: + + case *syntax.ConstDecl: + w.Code(declOther) + w.pkgObjs(decl.NameList...) + + case *syntax.FuncDecl: + if decl.Name.Value == "_" { + break // skip blank functions + } + + obj := w.p.info.Defs[decl.Name].(*types2.Func) + sig := obj.Type().(*types2.Signature) + + if sig.RecvTypeParams() != nil || sig.TypeParams() != nil { + break // skip generic functions + } + + if recv := sig.Recv(); recv != nil { + w.Code(declMethod) + w.typ(recvBase(recv)) + w.selector(obj) + break + } + + w.Code(declFunc) + w.pkgObjs(decl.Name) + + case *syntax.TypeDecl: + if len(decl.TParamList) != 0 { + break // skip generic type decls + } + + if decl.Name.Value == "_" { + break // skip blank type decls + } + + name := w.p.info.Defs[decl.Name].(*types2.TypeName) + // Skip type declarations for interfaces that are only usable as + // type parameter bounds. + if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() { + break + } + + w.Code(declOther) + w.pkgObjs(decl.Name) + + case *syntax.VarDecl: + w.Code(declVar) + w.pkgObjs(decl.NameList...) + + var embeds []pragmaEmbed + if p, ok := decl.Pragma.(*pragmas); ok { + embeds = p.Embeds + } + w.Len(len(embeds)) + for _, embed := range embeds { + w.pos(embed.Pos) + w.Strings(embed.Patterns) + } + } +} + +func (w *writer) pkgObjs(names ...*syntax.Name) { + w.Sync(pkgbits.SyncDeclNames) + w.Len(len(names)) + + for _, name := range names { + obj, ok := w.p.info.Defs[name] + assert(ok) + + w.Sync(pkgbits.SyncDeclName) + w.obj(obj, nil) + } +} + +// @@@ Helpers + +// staticBool analyzes a boolean expression and reports whether it's +// always true (positive result), always false (negative result), or +// unknown (zero). +// +// It also simplifies the expression while preserving semantics, if +// possible. +func (pw *pkgWriter) staticBool(ep *syntax.Expr) int { + if val := pw.typeAndValue(*ep).Value; val != nil { + if constant.BoolVal(val) { + return +1 + } else { + return -1 + } + } + + if e, ok := (*ep).(*syntax.Operation); ok { + switch e.Op { + case syntax.Not: + return pw.staticBool(&e.X) + + case syntax.AndAnd: + x := pw.staticBool(&e.X) + if x < 0 { + *ep = e.X + return x + } + + y := pw.staticBool(&e.Y) + if x > 0 || y < 0 { + if pw.typeAndValue(e.X).Value != nil { + *ep = e.Y + } + return y + } + + case syntax.OrOr: + x := pw.staticBool(&e.X) + if x > 0 { + *ep = e.X + return x + } + + y := pw.staticBool(&e.Y) + if x < 0 || y > 0 { + if pw.typeAndValue(e.X).Value != nil { + *ep = e.Y + } + return y + } + } + } + + return 0 +} + +// hasImplicitTypeParams reports whether obj is a defined type with +// implicit type parameters (e.g., declared within a generic function +// or method). +func (pw *pkgWriter) hasImplicitTypeParams(obj *types2.TypeName) bool { + if obj.Pkg() == pw.curpkg { + decl, ok := pw.typDecls[obj] + assert(ok) + if len(decl.implicits) != 0 { + return true + } + } + return false +} + +// isDefinedType reports whether obj is a defined type. +func isDefinedType(obj types2.Object) bool { + if obj, ok := obj.(*types2.TypeName); ok { + return !obj.IsAlias() + } + return false +} + +// isGlobal reports whether obj was declared at package scope. +// +// Caveat: blank objects are not declared. +func isGlobal(obj types2.Object) bool { + return obj.Parent() == obj.Pkg().Scope() +} + +// lookupObj returns the object that expr refers to, if any. If expr +// is an explicit instantiation of a generic object, then the instance +// object is returned as well. +func lookupObj(p *pkgWriter, expr syntax.Expr) (obj types2.Object, inst types2.Instance) { + if index, ok := expr.(*syntax.IndexExpr); ok { + args := syntax.UnpackListExpr(index.Index) + if len(args) == 1 { + tv := p.typeAndValue(args[0]) + if tv.IsValue() { + return // normal index expression + } + } + + expr = index.X + } + + // Strip package qualifier, if present. + if sel, ok := expr.(*syntax.SelectorExpr); ok { + if !isPkgQual(p.info, sel) { + return // normal selector expression + } + expr = sel.Sel + } + + if name, ok := expr.(*syntax.Name); ok { + obj = p.info.Uses[name] + inst = p.info.Instances[name] + } + return +} + +// isPkgQual reports whether the given selector expression is a +// package-qualified identifier. +func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool { + if name, ok := sel.X.(*syntax.Name); ok { + _, isPkgName := info.Uses[name].(*types2.PkgName) + return isPkgName + } + return false +} + +// isNil reports whether expr is a (possibly parenthesized) reference +// to the predeclared nil value. +func isNil(p *pkgWriter, expr syntax.Expr) bool { + tv := p.typeAndValue(expr) + return tv.IsNil() +} + +// isBuiltin reports whether expr is a (possibly parenthesized) +// referenced to the specified built-in function. +func (pw *pkgWriter) isBuiltin(expr syntax.Expr, builtin string) bool { + if name, ok := syntax.Unparen(expr).(*syntax.Name); ok && name.Value == builtin { + return pw.typeAndValue(name).IsBuiltin() + } + return false +} + +// recvBase returns the base type for the given receiver parameter. +func recvBase(recv *types2.Var) *types2.Named { + typ := types2.Unalias(recv.Type()) + if ptr, ok := typ.(*types2.Pointer); ok { + typ = ptr.Elem() + } + return typ.(*types2.Named) +} + +// namesAsExpr returns a list of names as a syntax.Expr. +func namesAsExpr(names []*syntax.Name) syntax.Expr { + if len(names) == 1 { + return names[0] + } + + exprs := make([]syntax.Expr, len(names)) + for i, name := range names { + exprs[i] = name + } + return &syntax.ListExpr{ElemList: exprs} +} + +// fieldIndex returns the index of the struct field named by key. +func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int { + field := info.Uses[key].(*types2.Var) + + for i := 0; i < str.NumFields(); i++ { + if str.Field(i) == field { + return i + } + } + + panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str)) +} + +// objTypeParams returns the type parameters on the given object. +func objTypeParams(obj types2.Object) *types2.TypeParamList { + switch obj := obj.(type) { + case *types2.Func: + sig := obj.Type().(*types2.Signature) + if sig.Recv() != nil { + return sig.RecvTypeParams() + } + return sig.TypeParams() + case *types2.TypeName: + if !obj.IsAlias() { + return obj.Type().(*types2.Named).TypeParams() + } + } + return nil +} + +// splitNamed decomposes a use of a defined type into its original +// type definition and the type arguments used to instantiate it. +func splitNamed(typ *types2.Named) (*types2.TypeName, *types2.TypeList) { + base.Assertf(typ.TypeParams().Len() == typ.TypeArgs().Len(), "use of uninstantiated type: %v", typ) + + orig := typ.Origin() + base.Assertf(orig.TypeArgs() == nil, "origin %v of %v has type arguments", orig, typ) + base.Assertf(typ.Obj() == orig.Obj(), "%v has object %v, but %v has object %v", typ, typ.Obj(), orig, orig.Obj()) + + return typ.Obj(), typ.TypeArgs() +} + +func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag { + if p == nil { + return 0 + } + return p.(*pragmas).Flag +} + +func asWasmImport(p syntax.Pragma) *WasmImport { + if p == nil { + return nil + } + return p.(*pragmas).WasmImport +} + +// isPtrTo reports whether from is the type *to. +func isPtrTo(from, to types2.Type) bool { + ptr, ok := types2.Unalias(from).(*types2.Pointer) + return ok && types2.Identical(ptr.Elem(), to) +} + +// hasFallthrough reports whether stmts ends in a fallthrough +// statement. +func hasFallthrough(stmts []syntax.Stmt) bool { + last, ok := lastNonEmptyStmt(stmts).(*syntax.BranchStmt) + return ok && last.Tok == syntax.Fallthrough +} + +// lastNonEmptyStmt returns the last non-empty statement in list, if +// any. +func lastNonEmptyStmt(stmts []syntax.Stmt) syntax.Stmt { + for i := len(stmts) - 1; i >= 0; i-- { + stmt := stmts[i] + if _, ok := stmt.(*syntax.EmptyStmt); !ok { + return stmt + } + } + return nil +} + +// terminates reports whether stmt terminates normal control flow +// (i.e., does not merely advance to the following statement). +func (pw *pkgWriter) terminates(stmt syntax.Stmt) bool { + switch stmt := stmt.(type) { + case *syntax.BranchStmt: + if stmt.Tok == syntax.Goto { + return true + } + case *syntax.ReturnStmt: + return true + case *syntax.ExprStmt: + if call, ok := syntax.Unparen(stmt.X).(*syntax.CallExpr); ok { + if pw.isBuiltin(call.Fun, "panic") { + return true + } + } + + // The handling of BlockStmt here is approximate, but it serves to + // allow dead-code elimination for: + // + // if true { + // return x + // } + // unreachable + case *syntax.IfStmt: + cond := pw.staticBool(&stmt.Cond) + return (cond < 0 || pw.terminates(stmt.Then)) && (cond > 0 || pw.terminates(stmt.Else)) + case *syntax.BlockStmt: + return pw.terminates(lastNonEmptyStmt(stmt.List)) + } + + return false +} |