1// Copyright 2021 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package noder67import (8 "fmt"9 "go/constant"10 "go/token"11 "go/version"12 "internal/buildcfg"13 "internal/pkgbits"14 "os"15 "slices"16 "strings"1718 "cmd/compile/internal/base"19 "cmd/compile/internal/ir"20 "cmd/compile/internal/syntax"21 "cmd/compile/internal/types"22 "cmd/compile/internal/types2"23)2425// This file implements the Unified IR package writer and defines the26// Unified IR export data format.27//28// Low-level coding details (e.g., byte-encoding of individual29// primitive values, or handling element bitstreams and30// cross-references) are handled by internal/pkgbits, so here we only31// concern ourselves with higher-level worries like mapping Go32// language constructs into elements.3334// There are two central types in the writing process: the "writer"35// type handles writing out individual elements, while the "pkgWriter"36// type keeps track of which elements have already been created.37//38// For each sort of "thing" (e.g., position, package, object, type)39// that can be written into the export data, there are generally40// several methods that work together:41//42// - writer.thing handles writing out a *use* of a thing, which often43// means writing a relocation to that thing's encoded index.44//45// - pkgWriter.thingIdx handles reserving an index for a thing, and46// writing out any elements needed for the thing.47//48// - writer.doThing handles writing out the *definition* of a thing,49// which in general is a mix of low-level coding primitives (e.g.,50// ints and strings) or uses of other things.51//52// A design goal of Unified IR is to have a single, canonical writer53// implementation, but multiple reader implementations each tailored54// to their respective needs. For example, within cmd/compile's own55// backend, inlining is implemented largely by just re-running the56// function body reading code.5758// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo,59// and better document the file format boundary between public and60// private data.6162type index = pkgbits.Index6364func assert(p bool) { base.Assert(p) }6566// A pkgWriter constructs Unified IR export data from the results of67// running the types2 type checker on a Go compilation unit.68type pkgWriter struct {69 pkgbits.PkgEncoder7071 m posMap72 curpkg *types2.Package73 info *types2.Info74 rangeFuncBodyClosures map[*syntax.FuncLit]bool // non-public information, e.g., which functions are closures range function bodies?7576 // Indices for previously written syntax and types2 things.7778 posBasesIdx map[*syntax.PosBase]index79 pkgsIdx map[*types2.Package]index80 typsIdx map[types2.Type]index81 objsIdx map[types2.Object]index8283 // Maps from types2.Objects back to their syntax.Decl.8485 funDecls map[*types2.Func]*syntax.FuncDecl86 typDecls map[*types2.TypeName]typeDeclGen8788 // linknames maps package-scope objects to their linker symbol name,89 // if specified by a //go:linkname or //go:linknamestd directive.90 linknames map[types2.Object]struct {91 remote string92 std bool93 }9495 // cgoPragmas accumulates any //go:cgo_* pragmas that need to be96 // passed through to cmd/link.97 cgoPragmas [][]string98}99100// newPkgWriter returns an initialized pkgWriter for the specified101// package.102func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {103 return &pkgWriter{104 PkgEncoder: pkgbits.NewPkgEncoder(uirVersion, base.Debug.SyncFrames),105106 m: m,107 curpkg: pkg,108 info: info,109 rangeFuncBodyClosures: otherInfo,110111 pkgsIdx: make(map[*types2.Package]index),112 objsIdx: make(map[types2.Object]index),113 typsIdx: make(map[types2.Type]index),114115 posBasesIdx: make(map[*syntax.PosBase]index),116117 funDecls: make(map[*types2.Func]*syntax.FuncDecl),118 typDecls: make(map[*types2.TypeName]typeDeclGen),119120 linknames: make(map[types2.Object]struct {121 remote string122 std bool123 }),124 }125}126127// errorf reports a user error about thing p.128func (pw *pkgWriter) errorf(p poser, msg string, args ...any) {129 base.ErrorfAt(pw.m.pos(p), 0, msg, args...)130}131132// fatalf reports an internal compiler error about thing p.133func (pw *pkgWriter) fatalf(p poser, msg string, args ...any) {134 base.FatalfAt(pw.m.pos(p), msg, args...)135}136137// unexpected reports a fatal error about a thing of unexpected138// dynamic type.139func (pw *pkgWriter) unexpected(what string, p poser) {140 pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)141}142143func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {144 tv, ok := pw.maybeTypeAndValue(x)145 if !ok {146 pw.fatalf(x, "missing Types entry: %v", syntax.String(x))147 }148 return tv149}150151func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {152 tv := x.GetTypeInfo()153154 // If x is a generic function whose type arguments are inferred155 // from assignment context, then we need to find its inferred type156 // in Info.Instances instead.157 if name, ok := x.(*syntax.Name); ok {158 if inst, ok := pw.info.Instances[name]; ok {159 tv.Type = inst.Type160 }161 }162163 return tv, tv.Type != nil164}165166// typeOf returns the Type of the given value expression.167func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type {168 tv := pw.typeAndValue(expr)169 if !tv.IsValue() {170 pw.fatalf(expr, "expected value: %v", syntax.String(expr))171 }172 return tv.Type173}174175// A writer provides APIs for writing out an individual element.176type writer struct {177 p *pkgWriter178179 *pkgbits.Encoder180181 // sig holds the signature for the current function body, if any.182 sig *types2.Signature183184 // TODO(mdempsky): We should be able to prune localsIdx whenever a185 // scope closes, and then maybe we can just use the same map for186 // storing the TypeParams too (as their TypeName instead).187188 // localsIdx tracks any local variables declared within this189 // function body. It's unused for writing out non-body things.190 localsIdx map[*types2.Var]int191192 // closureVars tracks any free variables that are referenced by this193 // function body. It's unused for writing out non-body things.194 closureVars []posVar195 closureVarsIdx map[*types2.Var]int // index of previously seen free variables196197 dict *writerDict198199 // derived tracks whether the type being written out references any200 // type parameters. It's unused for writing non-type things.201 derived bool202}203204// A writerDict tracks types and objects that are used by a declaration.205type writerDict struct {206 // implicits contains type parameters from enclosing declarations.207 implicits []*types2.TypeParam208 // receivers contains receiver type parameters of the declaration.209 receivers []*types2.TypeParam210211 // derived is a slice of type indices for computing derived types212 // (i.e., types that depend on the declaration's type parameters).213 derived []derivedInfo214215 // derivedIdx maps a Type to its corresponding index within the216 // derived slice, if present.217 derivedIdx map[types2.Type]index218219 // These slices correspond to entries in the runtime dictionary.220 typeParamMethodExprs []writerMethodExprInfo221 subdicts []objInfo222 rtypes []typeInfo223 itabs []itabInfo224}225226type itabInfo struct {227 typ typeInfo228 iface typeInfo229}230231// typeParamIndex returns the index of the given type parameter within232// the dictionary. This may differ from typ.Index() when there are233// implicit or receiver type parameters.234func (dict *writerDict) typeParamIndex(typ *types2.TypeParam) int {235 for idx, implicit := range dict.implicits {236 if implicit == typ {237 return idx238 }239 }240241 for idx, receiver := range dict.receivers {242 if receiver == typ {243 return len(dict.implicits) + idx244 }245 }246247 return len(dict.implicits) + len(dict.receivers) + typ.Index()248}249250// A derivedInfo represents a reference to an encoded generic Go type.251type derivedInfo struct {252 idx index253}254255// A typeInfo represents a reference to an encoded Go type.256//257// If derived is true, then the typeInfo represents a generic Go type258// that contains type parameters. In this case, idx is an index into259// the readerDict.derived{,Types} arrays.260//261// Otherwise, the typeInfo represents a non-generic Go type, and idx262// is an index into the reader.typs array instead.263type typeInfo struct {264 idx index265 derived bool266}267268// An objInfo represents a reference to an encoded, instantiated (if269// applicable) Go object.270type objInfo struct {271 idx index // index for the generic function declaration272 explicits []typeInfo // info for the type arguments273}274275// A selectorInfo represents a reference to an encoded field or method276// name (i.e., objects that can only be accessed using selector277// expressions).278type selectorInfo struct {279 pkgIdx index280 nameIdx index281}282283// anyDerived reports whether any of info's explicit type arguments284// are derived types.285func (info objInfo) anyDerived() bool {286 for _, explicit := range info.explicits {287 if explicit.derived {288 return true289 }290 }291 return false292}293294// equals reports whether info and other represent the same Go object295// (i.e., same base object and identical type arguments, if any).296func (info objInfo) equals(other objInfo) bool {297 if info.idx != other.idx {298 return false299 }300 assert(len(info.explicits) == len(other.explicits))301 for i, targ := range info.explicits {302 if targ != other.explicits[i] {303 return false304 }305 }306 return true307}308309type writerMethodExprInfo struct {310 typeParamIdx int311 methodInfo selectorInfo312}313314// typeParamMethodExprIdx returns the index where the given encoded315// method expression function pointer appears within this dictionary's316// type parameters method expressions section, adding it if necessary.317func (dict *writerDict) typeParamMethodExprIdx(typeParamIdx int, methodInfo selectorInfo) int {318 newInfo := writerMethodExprInfo{typeParamIdx, methodInfo}319320 for idx, oldInfo := range dict.typeParamMethodExprs {321 if oldInfo == newInfo {322 return idx323 }324 }325326 idx := len(dict.typeParamMethodExprs)327 dict.typeParamMethodExprs = append(dict.typeParamMethodExprs, newInfo)328 return idx329}330331// subdictIdx returns the index where the given encoded object's332// runtime dictionary appears within this dictionary's subdictionary333// section, adding it if necessary.334func (dict *writerDict) subdictIdx(newInfo objInfo) int {335 for idx, oldInfo := range dict.subdicts {336 if oldInfo.equals(newInfo) {337 return idx338 }339 }340341 idx := len(dict.subdicts)342 dict.subdicts = append(dict.subdicts, newInfo)343 return idx344}345346// rtypeIdx returns the index where the given encoded type's347// *runtime._type value appears within this dictionary's rtypes348// section, adding it if necessary.349func (dict *writerDict) rtypeIdx(newInfo typeInfo) int {350 for idx, oldInfo := range dict.rtypes {351 if oldInfo == newInfo {352 return idx353 }354 }355356 idx := len(dict.rtypes)357 dict.rtypes = append(dict.rtypes, newInfo)358 return idx359}360361// itabIdx returns the index where the given encoded type pair's362// *runtime.itab value appears within this dictionary's itabs section,363// adding it if necessary.364func (dict *writerDict) itabIdx(typInfo, ifaceInfo typeInfo) int {365 newInfo := itabInfo{typInfo, ifaceInfo}366367 for idx, oldInfo := range dict.itabs {368 if oldInfo == newInfo {369 return idx370 }371 }372373 idx := len(dict.itabs)374 dict.itabs = append(dict.itabs, newInfo)375 return idx376}377378func (pw *pkgWriter) newWriter(k pkgbits.SectionKind, marker pkgbits.SyncMarker) *writer {379 return &writer{380 Encoder: pw.NewEncoder(k, marker),381 p: pw,382 }383}384385// @@@ Positions386387// pos writes the position of p into the element bitstream.388func (w *writer) pos(p poser) {389 w.Sync(pkgbits.SyncPos)390 pos := p.Pos()391392 // TODO(mdempsky): Track down the remaining cases here and fix them.393 if !w.Bool(pos.IsKnown()) {394 return395 }396397 // TODO(mdempsky): Delta encoding.398 w.posBase(pos.Base())399 w.Uint(pos.Line())400 w.Uint(pos.Col())401}402403// posBase writes a reference to the given PosBase into the element404// bitstream.405func (w *writer) posBase(b *syntax.PosBase) {406 w.Reloc(pkgbits.SectionPosBase, w.p.posBaseIdx(b))407}408409// posBaseIdx returns the index for the given PosBase.410func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) index {411 if idx, ok := pw.posBasesIdx[b]; ok {412 return idx413 }414415 w := pw.newWriter(pkgbits.SectionPosBase, pkgbits.SyncPosBase)416 w.p.posBasesIdx[b] = w.Idx417418 w.String(trimFilename(b))419420 if !w.Bool(b.IsFileBase()) {421 w.pos(b)422 w.Uint(b.Line())423 w.Uint(b.Col())424 }425426 return w.Flush()427}428429// @@@ Packages430431// pkg writes a use of the given Package into the element bitstream.432func (w *writer) pkg(pkg *types2.Package) {433 w.pkgRef(w.p.pkgIdx(pkg))434}435436func (w *writer) pkgRef(idx index) {437 w.Sync(pkgbits.SyncPkg)438 w.Reloc(pkgbits.SectionPkg, idx)439}440441// pkgIdx returns the index for the given package, adding it to the442// package export data if needed.443func (pw *pkgWriter) pkgIdx(pkg *types2.Package) index {444 if idx, ok := pw.pkgsIdx[pkg]; ok {445 return idx446 }447448 w := pw.newWriter(pkgbits.SectionPkg, pkgbits.SyncPkgDef)449 pw.pkgsIdx[pkg] = w.Idx450451 // The universe and package unsafe need to be handled specially by452 // importers anyway, so we serialize them using just their package453 // path. This ensures that readers don't confuse them for454 // user-defined packages.455 switch pkg {456 case nil: // universe457 w.String("builtin") // same package path used by godoc458 case types2.Unsafe:459 w.String("unsafe")460 default:461 // TODO(mdempsky): Write out pkg.Path() for curpkg too.462 var path string463 if pkg != w.p.curpkg {464 path = pkg.Path()465 }466 base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path)467 w.String(path)468 w.String(pkg.Name())469470 w.Len(len(pkg.Imports()))471 for _, imp := range pkg.Imports() {472 w.pkg(imp)473 }474 }475476 return w.Flush()477}478479// @@@ Types480481var (482 anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)483 comparableTypeName = types2.Universe.Lookup("comparable").(*types2.TypeName)484 runeTypeName = types2.Universe.Lookup("rune").(*types2.TypeName)485)486487// typ writes a use of the given type into the bitstream.488func (w *writer) typ(typ types2.Type) {489 w.typInfo(w.p.typIdx(typ, w.dict))490}491492// typInfo writes a use of the given type (specified as a typeInfo493// instead) into the bitstream.494func (w *writer) typInfo(info typeInfo) {495 w.Sync(pkgbits.SyncType)496 if w.Bool(info.derived) {497 w.Len(int(info.idx))498 w.derived = true499 } else {500 w.Reloc(pkgbits.SectionType, info.idx)501 }502}503504// typIdx returns the index where the export data description of type505// can be read back in. If no such index exists yet, it's created.506//507// typIdx also reports whether typ is a derived type; that is, whether508// its identity depends on type parameters.509func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {510 // Strip non-global aliases, because they only appear in inline511 // bodies anyway. Otherwise, they can cause types.Sym collisions512 // (e.g., "main.C" for both of the local type aliases in513 // test/fixedbugs/issue50190.go).514 for {515 if alias, ok := typ.(*types2.Alias); ok && !isGlobal(alias.Obj()) {516 typ = alias.Rhs()517 } else {518 break519 }520 }521522 if idx, ok := pw.typsIdx[typ]; ok {523 return typeInfo{idx: idx, derived: false}524 }525 if dict != nil {526 if idx, ok := dict.derivedIdx[typ]; ok {527 return typeInfo{idx: idx, derived: true}528 }529 }530531 w := pw.newWriter(pkgbits.SectionType, pkgbits.SyncTypeIdx)532 w.dict = dict533534 switch typ := typ.(type) {535 default:536 base.Fatalf("unexpected type: %v (%T)", typ, typ)537538 case *types2.Basic:539 switch kind := typ.Kind(); {540 case kind == types2.Invalid:541 base.Fatalf("unexpected types2.Invalid")542543 case types2.Typ[kind] == typ:544 w.Code(pkgbits.TypeBasic)545 w.Len(int(kind))546547 default:548 // Handle "byte" and "rune" as references to their TypeNames.549 obj := types2.Universe.Lookup(typ.Name()).(*types2.TypeName)550 assert(obj.Type() == typ)551552 w.Code(pkgbits.TypeNamed)553 w.namedType(obj, nil)554 }555556 case *types2.Named:557 w.Code(pkgbits.TypeNamed)558 w.namedType(splitNamed(typ))559560 case *types2.Alias:561 w.Code(pkgbits.TypeNamed)562 w.namedType(splitAlias(typ))563564 case *types2.TypeParam:565 w.derived = true566 w.Code(pkgbits.TypeTypeParam)567 w.Len(w.dict.typeParamIndex(typ))568569 case *types2.Array:570 w.Code(pkgbits.TypeArray)571 w.Uint64(uint64(typ.Len()))572 w.typ(typ.Elem())573574 case *types2.Chan:575 w.Code(pkgbits.TypeChan)576 w.Len(int(typ.Dir()))577 w.typ(typ.Elem())578579 case *types2.Map:580 w.Code(pkgbits.TypeMap)581 w.typ(typ.Key())582 w.typ(typ.Elem())583584 case *types2.Pointer:585 w.Code(pkgbits.TypePointer)586 w.typ(typ.Elem())587588 case *types2.Signature:589 base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)590 w.Code(pkgbits.TypeSignature)591 w.signature(typ)592593 case *types2.Slice:594 w.Code(pkgbits.TypeSlice)595 w.typ(typ.Elem())596597 case *types2.Struct:598 w.Code(pkgbits.TypeStruct)599 w.structType(typ)600601 case *types2.Interface:602 // Handle "any" as reference to its TypeName.603 // The underlying "any" interface is canonical, so this logic handles both604 // GODEBUG=gotypesalias=1 (when any is represented as a types2.Alias), and605 // gotypesalias=0.606 if types2.Unalias(typ) == types2.Unalias(anyTypeName.Type()) {607 w.Code(pkgbits.TypeNamed)608 w.obj(anyTypeName, nil)609 break610 }611612 w.Code(pkgbits.TypeInterface)613 w.interfaceType(typ)614615 case *types2.Union:616 w.Code(pkgbits.TypeUnion)617 w.unionType(typ)618 }619620 if w.derived {621 idx := index(len(dict.derived))622 dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})623 dict.derivedIdx[typ] = idx624 return typeInfo{idx: idx, derived: true}625 }626627 pw.typsIdx[typ] = w.Idx628 return typeInfo{idx: w.Flush(), derived: false}629}630631// namedType writes a use of the given named type into the bitstream.632func (w *writer) namedType(obj *types2.TypeName, targs []types2.Type) {633 // Named types that are declared within a generic function (and634 // thus have implicit type parameters) are always derived types.635 if w.p.hasImplicitTypeParams(obj) {636 w.derived = true637 }638639 w.obj(obj, targs)640}641642func (w *writer) structType(typ *types2.Struct) {643 w.Len(typ.NumFields())644 for i := 0; i < typ.NumFields(); i++ {645 f := typ.Field(i)646 w.pos(f)647 w.selector(f)648 w.typ(f.Type())649 w.String(typ.Tag(i))650 w.Bool(f.Embedded())651 }652}653654func (w *writer) unionType(typ *types2.Union) {655 w.Len(typ.Len())656 for i := 0; i < typ.Len(); i++ {657 t := typ.Term(i)658 w.Bool(t.Tilde())659 w.typ(t.Type())660 }661}662663func (w *writer) interfaceType(typ *types2.Interface) {664 // If typ has no embedded types but it's not a basic interface, then665 // the natural description we write out below will fail to666 // reconstruct it.667 if typ.NumEmbeddeds() == 0 && !typ.IsMethodSet() {668 // Currently, this can only happen for the underlying Interface of669 // "comparable", which is needed to handle type declarations like670 // "type C comparable".671 assert(typ == comparableTypeName.Type().(*types2.Named).Underlying())672673 // Export as "interface{ comparable }".674 w.Len(0) // NumExplicitMethods675 w.Len(1) // NumEmbeddeds676 w.Bool(false) // IsImplicit677 w.typ(comparableTypeName.Type()) // EmbeddedType(0)678 return679 }680681 w.Len(typ.NumExplicitMethods())682 w.Len(typ.NumEmbeddeds())683684 if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 {685 w.Bool(typ.IsImplicit())686 } else {687 // Implicit interfaces always have 0 explicit methods and 1688 // embedded type, so we skip writing out the implicit flag689 // otherwise as a space optimization.690 assert(!typ.IsImplicit())691 }692693 for i := 0; i < typ.NumExplicitMethods(); i++ {694 m := typ.ExplicitMethod(i)695 sig := m.Type().(*types2.Signature)696 assert(sig.TypeParams() == nil)697698 w.pos(m)699 w.selector(m)700 w.signature(sig)701 }702703 for i := 0; i < typ.NumEmbeddeds(); i++ {704 w.typ(typ.EmbeddedType(i))705 }706}707708func (w *writer) signature(sig *types2.Signature) {709 w.Sync(pkgbits.SyncSignature)710 w.params(sig.Params())711 w.params(sig.Results())712 w.Bool(sig.Variadic())713}714715func (w *writer) params(typ *types2.Tuple) {716 w.Sync(pkgbits.SyncParams)717 w.Len(typ.Len())718 for i := 0; i < typ.Len(); i++ {719 w.param(typ.At(i))720 }721}722723func (w *writer) param(param *types2.Var) {724 w.Sync(pkgbits.SyncParam)725 w.pos(param)726 w.localIdent(param)727 w.typ(param.Type())728}729730// @@@ Objects731732// obj writes a use of the given object into the bitstream.733//734// If obj is a generic object, then explicits are the explicit type735// arguments used to instantiate it (i.e., used to substitute the736// object's own declared type parameters).737func (w *writer) obj(obj types2.Object, explicits []types2.Type) {738 w.objInfo(w.p.objInstIdx(obj, explicits, w.dict))739}740741// objInfo writes a use of the given encoded object into the742// bitstream.743func (w *writer) objInfo(info objInfo) {744 w.Sync(pkgbits.SyncObject)745 if w.Version().Has(pkgbits.DerivedFuncInstance) {746 w.Bool(false)747 }748 w.Reloc(pkgbits.SectionObj, info.idx)749750 w.Len(len(info.explicits))751 for _, info := range info.explicits {752 w.typInfo(info)753 }754}755756// objInstIdx returns the indices for an object and a corresponding757// list of type arguments used to instantiate it, adding them to the758// export data as needed.759func (pw *pkgWriter) objInstIdx(obj types2.Object, explicits []types2.Type, dict *writerDict) objInfo {760 explicitInfos := make([]typeInfo, len(explicits))761 for i := range explicitInfos {762 explicitInfos[i] = pw.typIdx(explicits[i], dict)763 }764 return objInfo{idx: pw.objIdx(obj), explicits: explicitInfos}765}766767// objIdx returns the index for the given Object, adding it to the768// export data as needed.769func (pw *pkgWriter) objIdx(obj types2.Object) index {770 // TODO(mdempsky): Validate that obj is a global object (or a local771 // defined type, which we hoist to global scope anyway).772773 if idx, ok := pw.objsIdx[obj]; ok {774 return idx775 }776777 dict := &writerDict{778 derivedIdx: make(map[types2.Type]index),779 }780781 if isDefinedType(obj) && obj.Pkg() == pw.curpkg {782 decl, ok := pw.typDecls[obj.(*types2.TypeName)]783 if !ok {784 base.Fatalf("%v not in pw.typDecls", obj.(*types2.TypeName))785 }786 dict.implicits = decl.implicits787 }788789 if isGenericMethod(obj.Type()) {790 dict.receivers = asTypeParamSlice(obj.Type().(*types2.Signature).RecvTypeParams())791 }792793 // We encode objects into 4 elements across different sections, all794 // sharing the same index:795 //796 // - RelocName has just the object's qualified name (i.e.,797 // Object.Pkg and Object.Name) and the CodeObj indicating what798 // specific type of Object it is (Var, Func, etc).799 //800 // - RelocObj has the remaining public details about the object,801 // relevant to go/types importers.802 //803 // - RelocObjExt has additional private details about the object,804 // which are only relevant to cmd/compile itself. This is805 // separated from RelocObj so that go/types importers are806 // unaffected by internal compiler changes.807 //808 // - RelocObjDict has public details about the object's type809 // parameters and derived type's used by the object. This is810 // separated to facilitate the eventual introduction of811 // shape-based stenciling.812 //813 // TODO(mdempsky): Re-evaluate whether RelocName still makes sense814 // to keep separate from RelocObj.815816 w := pw.newWriter(pkgbits.SectionObj, pkgbits.SyncObject1)817 wext := pw.newWriter(pkgbits.SectionObjExt, pkgbits.SyncObject1)818 wname := pw.newWriter(pkgbits.SectionName, pkgbits.SyncObject1)819 wdict := pw.newWriter(pkgbits.SectionObjDict, pkgbits.SyncObject1)820821 pw.objsIdx[obj] = w.Idx // break cycles822 assert(wext.Idx == w.Idx)823 assert(wname.Idx == w.Idx)824 assert(wdict.Idx == w.Idx)825826 w.dict = dict827 wext.dict = dict828829 code := w.doObj(wext, obj)830 w.Flush()831 wext.Flush()832833 wname.qualifiedIdent(obj)834 wname.Code(code)835 wname.Flush()836837 wdict.objDict(obj, w.dict)838 wdict.Flush()839840 return w.Idx841}842843// doObj writes the RelocObj definition for obj to w, and the844// RelocObjExt definition to wext.845func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj {846 if obj.Pkg() != w.p.curpkg {847 return pkgbits.ObjStub848 }849850 switch obj := obj.(type) {851 default:852 w.p.unexpected("object", obj)853 panic("unreachable")854855 case *types2.Const:856 w.pos(obj)857 w.typ(obj.Type())858 w.Value(obj.Val())859 return pkgbits.ObjConst860861 case *types2.Func:862 if base.Flag.LowerH > 0 {863 // Unified IR panics are the worst; this is a huge help in debugging them.864 defer func() {865 if p := recover(); p != nil {866 fmt.Printf("Intercepted unified IR writer panic for function %s, repanicking", obj.FullName())867 panic(p)868 }869 }()870 }871 decl, ok := w.p.funDecls[obj]872 assert(ok)873 sig := obj.Type().(*types2.Signature)874875 w.pos(obj)876 if isGenericMethod(sig) {877 w.Bool(true) // generic method878879 w.selector(obj)880 w.typeParamNames(sig.RecvTypeParams())881 w.param(sig.Recv())882 } else {883 if w.Version().Has(pkgbits.GenericMethods) {884 w.Bool(false) // function885 }886 }887 w.typeParamNames(sig.TypeParams())888 w.signature(sig)889 w.pos(decl)890 wext.funcExt(obj)891 return pkgbits.ObjFunc892893 case *types2.TypeName:894 if obj.IsAlias() {895 w.pos(obj)896 rhs := obj.Type()897 var tparams *types2.TypeParamList898 if alias, ok := rhs.(*types2.Alias); ok { // materialized alias899 assert(alias.TypeArgs() == nil)900 tparams = alias.TypeParams()901 rhs = alias.Rhs()902 }903 if w.Version().Has(pkgbits.AliasTypeParamNames) {904 w.typeParamNames(tparams)905 }906 assert(w.Version().Has(pkgbits.AliasTypeParamNames) || tparams.Len() == 0)907 w.typ(rhs)908 return pkgbits.ObjAlias909 }910911 named := obj.Type().(*types2.Named)912 assert(named.TypeArgs() == nil)913914 w.pos(obj)915 w.typeParamNames(named.TypeParams())916 wext.typeExt(obj)917 w.typ(named.Underlying())918919 // separate generic and non-generic methods920 var methods, gmethods []*types2.Func921 for i := range named.NumMethods() {922 m := named.Method(i)923 if isGenericMethod(m.Type()) {924 gmethods = append(gmethods, m)925 } else {926 methods = append(methods, m)927 }928 }929 // encode non-generic methods inline930 w.Len(len(methods))931 for _, m := range methods {932 w.method(wext, m)933 }934 if len(gmethods) > 0 {935 assert(w.Version().Has(pkgbits.GenericMethods))936 }937 // encode a pointer to each generic method938 if w.Version().Has(pkgbits.GenericMethods) {939 w.Len(len(gmethods))940 for _, m := range gmethods {941 w.Reloc(pkgbits.SectionObj, w.p.objIdx(m))942 }943 }944945 return pkgbits.ObjType946947 case *types2.Var:948 w.pos(obj)949 w.typ(obj.Type())950 wext.varExt(obj)951 return pkgbits.ObjVar952 }953}954955// objDict writes the dictionary needed for reading the given object.956func (w *writer) objDict(obj types2.Object, dict *writerDict) {957 // TODO(mdempsky): Split objDict into multiple entries? reader.go958 // doesn't care about the type parameter bounds, and reader2.go959 // doesn't care about referenced functions.960961 w.dict = dict // TODO(mdempsky): This is a bit sketchy.962 w.Len(len(dict.implicits))963964 rtparams := objRecvTypeParams(obj)965 tparams := objTypeParams(obj)966967 if w.Version().Has(pkgbits.GenericMethods) {968 w.Len(len(rtparams))969 } else {970 assert(len(rtparams) == 0)971 }972 w.Len(len(tparams))973974 for _, rtparam := range rtparams {975 w.typ(rtparam.Constraint())976 }977 for _, tparam := range tparams {978 w.typ(tparam.Constraint())979 }980981 nderived := len(dict.derived)982 w.Len(nderived)983 for _, typ := range dict.derived {984 w.Reloc(pkgbits.SectionType, typ.idx)985 if w.Version().Has(pkgbits.DerivedInfoNeeded) {986 w.Bool(false)987 }988 }989990 // Write runtime dictionary information.991 //992 // N.B., the go/types importer reads up to the section, but doesn't993 // read any further, so it's safe to change. (See TODO above.)994995 // For each type parameter, write out whether the constraint is a996 // basic interface. This is used to determine how aggressively we997 // can shape corresponding type arguments.998 //999 // This is somewhat redundant with writing out the full type1000 // parameter constraints above, but the compiler currently skips1001 // over those. Also, we don't care about the *declared* constraints,1002 // but how the type parameters are actually *used*. E.g., if a type1003 // parameter is constrained to `int | uint` but then never used in1004 // arithmetic/conversions/etc, we could shape those together.1005 for _, implicit := range dict.implicits {1006 w.Bool(implicit.Underlying().(*types2.Interface).IsMethodSet())1007 }1008 for _, rtparam := range rtparams {1009 w.Bool(rtparam.Underlying().(*types2.Interface).IsMethodSet())1010 }1011 for _, tparam := range tparams {1012 w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet())1013 }10141015 w.Len(len(dict.typeParamMethodExprs))1016 for _, info := range dict.typeParamMethodExprs {1017 w.Len(info.typeParamIdx)1018 w.selectorInfo(info.methodInfo)1019 }10201021 w.Len(len(dict.subdicts))1022 for _, info := range dict.subdicts {1023 w.objInfo(info)1024 }10251026 w.Len(len(dict.rtypes))1027 for _, info := range dict.rtypes {1028 w.typInfo(info)1029 }10301031 w.Len(len(dict.itabs))1032 for _, info := range dict.itabs {1033 w.typInfo(info.typ)1034 w.typInfo(info.iface)1035 }10361037 assert(len(dict.derived) == nderived)1038}10391040func (w *writer) typeParamNames(tparams *types2.TypeParamList) {1041 w.Sync(pkgbits.SyncTypeParamNames)10421043 ntparams := tparams.Len()1044 for i := 0; i < ntparams; i++ {1045 tparam := tparams.At(i).Obj()1046 w.pos(tparam)1047 w.localIdent(tparam)1048 }1049}10501051func (w *writer) method(wext *writer, meth *types2.Func) {1052 decl, ok := w.p.funDecls[meth]1053 assert(ok)1054 sig := meth.Type().(*types2.Signature)10551056 w.Sync(pkgbits.SyncMethod)1057 w.pos(meth)1058 w.selector(meth)1059 w.typeParamNames(sig.RecvTypeParams())1060 w.param(sig.Recv())1061 w.signature(sig)10621063 w.pos(decl) // XXX: Hack to workaround linker limitations.1064 wext.funcExt(meth)1065}10661067// qualifiedIdent writes out the name of an object typically declared at package1068// scope. It's also used to refer to generic methods and locally defined types.1069func (w *writer) qualifiedIdent(obj types2.Object) {1070 w.Sync(pkgbits.SyncSym)10711072 name := obj.Name()1073 if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {1074 decl, ok := w.p.typDecls[obj.(*types2.TypeName)]1075 assert(ok)1076 if decl.gen != 0 {1077 // For local defined types, we embed a scope-disambiguation1078 // number directly into their name. types.SplitVargenSuffix then1079 // knows to look for this.1080 //1081 // TODO(mdempsky): Find a better solution; this is terrible.1082 name = fmt.Sprintf("%s·%v", name, decl.gen)1083 }1084 }10851086 // Generic methods are promoted to objects and thus need qualified identifiers.1087 // They must be contextualized by their defining type.1088 if isGenericMethod(obj.Type()) {1089 recv := obj.Type().(*types2.Signature).Recv().Type()1090 fstr := "%s.%s"1091 if _, ok := recv.(*types2.Pointer); ok {1092 fstr = "(*%s).%s"1093 }1094 name = fmt.Sprintf(fstr, types2.Unalias(deref2(recv)).(*types2.Named).Obj().Name(), name)1095 }10961097 w.pkg(obj.Pkg())1098 w.String(name)1099}11001101// TODO(mdempsky): We should be able to omit pkg from both localIdent1102// and selector, because they should always be known from context.1103// However, past frustrations with this optimization in iexport make1104// me a little nervous to try it again.11051106// localIdent writes the name of a locally declared object (i.e.,1107// objects that can only be accessed by non-qualified name, within the1108// context of a particular function).1109func (w *writer) localIdent(obj types2.Object) {1110 assert(!isGlobal(obj))1111 w.Sync(pkgbits.SyncLocalIdent)1112 w.pkg(obj.Pkg())1113 w.String(obj.Name())1114}11151116// selector writes the name of a field or method (i.e., objects that1117// can only be accessed using selector expressions).1118func (w *writer) selector(obj types2.Object) {1119 w.selectorInfo(w.p.selectorIdx(obj))1120}11211122func (w *writer) selectorInfo(info selectorInfo) {1123 w.Sync(pkgbits.SyncSelector)1124 w.pkgRef(info.pkgIdx)1125 w.StringRef(info.nameIdx)1126}11271128func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo {1129 pkgIdx := pw.pkgIdx(obj.Pkg())1130 nameIdx := pw.StringIdx(obj.Name())1131 return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx}1132}11331134// @@@ Compiler extensions11351136func (w *writer) funcExt(obj *types2.Func) {1137 decl, ok := w.p.funDecls[obj]1138 assert(ok)11391140 // TODO(mdempsky): Extend these pragma validation flags to account1141 // for generics. E.g., linkname probably doesn't make sense at1142 // least.11431144 pragma := asPragmaFlag(decl.Pragma)1145 if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {1146 w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")1147 }1148 wi := asWasmImport(decl.Pragma)1149 we := asWasmExport(decl.Pragma)11501151 if decl.Body != nil {1152 if pragma&ir.Noescape != 0 {1153 w.p.errorf(decl, "can only use //go:noescape with external func implementations")1154 }1155 if wi != nil {1156 w.p.errorf(decl, "can only use //go:wasmimport with external func implementations")1157 }1158 if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 {1159 // Stack growth can't handle uintptr arguments that may1160 // be pointers (as we don't know which are pointers1161 // when creating the stack map). Thus uintptrkeepalive1162 // functions (and all transitive callees) must be1163 // nosplit.1164 //1165 // N.B. uintptrescapes implies uintptrkeepalive but it1166 // is OK since the arguments must escape to the heap.1167 //1168 // TODO(prattmic): Add recursive nosplit check of callees.1169 // TODO(prattmic): Functions with no body (i.e.,1170 // assembly) must also be nosplit, but we can't check1171 // that here.1172 w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")1173 }1174 } else {1175 if base.Flag.Complete || decl.Name.Value == "init" {1176 // Linknamed functions are allowed to have no body. Hopefully1177 // the linkname target has a body. See issue 23311.1178 // Wasmimport functions are also allowed to have no body.1179 if _, ok := w.p.linknames[obj]; !ok && wi == nil {1180 w.p.errorf(decl, "missing function body")1181 }1182 }1183 }11841185 sig, block := obj.Type().(*types2.Signature), decl.Body1186 body, closureVars := w.p.bodyIdx(sig, block, w.dict)1187 if len(closureVars) > 0 {1188 fmt.Fprintln(os.Stderr, "CLOSURE", closureVars)1189 }1190 assert(len(closureVars) == 0)11911192 w.Sync(pkgbits.SyncFuncExt)1193 w.pragmaFlag(pragma)1194 w.linkname(obj)11951196 if buildcfg.GOARCH == "wasm" {1197 if wi != nil {1198 w.String(wi.Module)1199 w.String(wi.Name)1200 } else {1201 w.String("")1202 w.String("")1203 }1204 if we != nil {1205 w.String(we.Name)1206 } else {1207 w.String("")1208 }1209 }12101211 w.Bool(false) // stub extension1212 w.Reloc(pkgbits.SectionBody, body)1213 w.Sync(pkgbits.SyncEOF)1214}12151216func (w *writer) typeExt(obj *types2.TypeName) {1217 decl, ok := w.p.typDecls[obj]1218 assert(ok)12191220 w.Sync(pkgbits.SyncTypeExt)12211222 w.pragmaFlag(asPragmaFlag(decl.Pragma))12231224 // No LSym.SymIdx info yet.1225 w.Int64(-1)1226 w.Int64(-1)1227}12281229func (w *writer) varExt(obj *types2.Var) {1230 w.Sync(pkgbits.SyncVarExt)1231 w.linkname(obj)1232}12331234func (w *writer) linkname(obj types2.Object) {1235 w.Sync(pkgbits.SyncLinkname)1236 w.Int64(-1)1237 info := w.p.linknames[obj]1238 w.String(info.remote)1239 w.Bool(info.std)1240}12411242func (w *writer) pragmaFlag(p ir.PragmaFlag) {1243 w.Sync(pkgbits.SyncPragma)1244 w.Int(int(p))1245}12461247// @@@ Function bodies12481249// bodyIdx returns the index for the given function body (specified by1250// block), adding it to the export data1251func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx index, closureVars []posVar) {1252 w := pw.newWriter(pkgbits.SectionBody, pkgbits.SyncFuncBody)1253 w.sig = sig1254 w.dict = dict12551256 w.declareParams(sig)1257 if w.Bool(block != nil) {1258 w.stmts(block.List)1259 w.pos(block.Rbrace)1260 }12611262 return w.Flush(), w.closureVars1263}12641265func (w *writer) declareParams(sig *types2.Signature) {1266 addLocals := func(params *types2.Tuple) {1267 for i := 0; i < params.Len(); i++ {1268 w.addLocal(params.At(i))1269 }1270 }12711272 if recv := sig.Recv(); recv != nil {1273 w.addLocal(recv)1274 }1275 addLocals(sig.Params())1276 addLocals(sig.Results())1277}12781279// addLocal records the declaration of a new local variable.1280func (w *writer) addLocal(obj *types2.Var) {1281 idx := len(w.localsIdx)12821283 w.Sync(pkgbits.SyncAddLocal)1284 if w.p.SyncMarkers() {1285 w.Int(idx)1286 }1287 w.varDictIndex(obj)12881289 if w.localsIdx == nil {1290 w.localsIdx = make(map[*types2.Var]int)1291 }1292 w.localsIdx[obj] = idx1293}12941295// useLocal writes a reference to the given local or free variable1296// into the bitstream.1297func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {1298 w.Sync(pkgbits.SyncUseObjLocal)12991300 if idx, ok := w.localsIdx[obj]; w.Bool(ok) {1301 w.Len(idx)1302 return1303 }13041305 idx, ok := w.closureVarsIdx[obj]1306 if !ok {1307 if w.closureVarsIdx == nil {1308 w.closureVarsIdx = make(map[*types2.Var]int)1309 }1310 idx = len(w.closureVars)1311 w.closureVars = append(w.closureVars, posVar{pos, obj})1312 w.closureVarsIdx[obj] = idx1313 }1314 w.Len(idx)1315}13161317func (w *writer) openScope(pos syntax.Pos) {1318 w.Sync(pkgbits.SyncOpenScope)1319 w.pos(pos)1320}13211322func (w *writer) closeScope(pos syntax.Pos) {1323 w.Sync(pkgbits.SyncCloseScope)1324 w.pos(pos)1325 w.closeAnotherScope()1326}13271328func (w *writer) closeAnotherScope() {1329 w.Sync(pkgbits.SyncCloseAnotherScope)1330}13311332// @@@ Statements13331334// stmt writes the given statement into the function body bitstream.1335func (w *writer) stmt(stmt syntax.Stmt) {1336 var stmts []syntax.Stmt1337 if stmt != nil {1338 stmts = []syntax.Stmt{stmt}1339 }1340 w.stmts(stmts)1341}13421343func (w *writer) stmts(stmts []syntax.Stmt) {1344 dead := false1345 w.Sync(pkgbits.SyncStmts)1346 var lastLabel = -11347 for i, stmt := range stmts {1348 if _, ok := stmt.(*syntax.LabeledStmt); ok {1349 lastLabel = i1350 }1351 }1352 for i, stmt := range stmts {1353 if dead && i > lastLabel {1354 // Any statements after a terminating and last label statement are safe to omit.1355 // Otherwise, code after label statement may refer to dead stmts between terminating1356 // and label statement, see issue #65593.1357 if _, ok := stmt.(*syntax.LabeledStmt); !ok {1358 continue1359 }1360 }1361 w.stmt1(stmt)1362 dead = w.p.terminates(stmt)1363 }1364 w.Code(stmtEnd)1365 w.Sync(pkgbits.SyncStmtsEnd)1366}13671368func (w *writer) stmt1(stmt syntax.Stmt) {1369 switch stmt := stmt.(type) {1370 default:1371 w.p.unexpected("statement", stmt)13721373 case nil, *syntax.EmptyStmt:1374 return13751376 case *syntax.AssignStmt:1377 switch {1378 case stmt.Rhs == nil:1379 w.Code(stmtIncDec)1380 w.op(binOps[stmt.Op])1381 w.expr(stmt.Lhs)1382 w.pos(stmt)13831384 case stmt.Op != 0 && stmt.Op != syntax.Def:1385 w.Code(stmtAssignOp)1386 w.op(binOps[stmt.Op])1387 w.expr(stmt.Lhs)1388 w.pos(stmt)13891390 var typ types2.Type1391 if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr {1392 typ = w.p.typeOf(stmt.Lhs)1393 }1394 w.implicitConvExpr(typ, stmt.Rhs)13951396 default:1397 w.assignStmt(stmt, stmt.Lhs, stmt.Rhs)1398 }13991400 case *syntax.BlockStmt:1401 w.Code(stmtBlock)1402 w.blockStmt(stmt)14031404 case *syntax.BranchStmt:1405 w.Code(stmtBranch)1406 w.pos(stmt)1407 var op ir.Op1408 switch stmt.Tok {1409 case syntax.Break:1410 op = ir.OBREAK1411 case syntax.Continue:1412 op = ir.OCONTINUE1413 case syntax.Fallthrough:1414 op = ir.OFALL1415 case syntax.Goto:1416 op = ir.OGOTO1417 }1418 w.op(op)1419 w.optLabel(stmt.Label)14201421 case *syntax.CallStmt:1422 w.Code(stmtCall)1423 w.pos(stmt)1424 var op ir.Op1425 switch stmt.Tok {1426 case syntax.Defer:1427 op = ir.ODEFER1428 case syntax.Go:1429 op = ir.OGO1430 }1431 w.op(op)1432 w.expr(stmt.Call)1433 if stmt.Tok == syntax.Defer {1434 w.optExpr(stmt.DeferAt)1435 }14361437 case *syntax.DeclStmt:1438 for _, decl := range stmt.DeclList {1439 w.declStmt(decl)1440 }14411442 case *syntax.ExprStmt:1443 w.Code(stmtExpr)1444 w.expr(stmt.X)14451446 case *syntax.ForStmt:1447 w.Code(stmtFor)1448 w.forStmt(stmt)14491450 case *syntax.IfStmt:1451 w.Code(stmtIf)1452 w.ifStmt(stmt)14531454 case *syntax.LabeledStmt:1455 w.Code(stmtLabel)1456 w.pos(stmt)1457 w.label(stmt.Label)1458 w.stmt1(stmt.Stmt)14591460 case *syntax.ReturnStmt:1461 w.Code(stmtReturn)1462 w.pos(stmt)14631464 resultTypes := w.sig.Results()1465 dstType := func(i int) types2.Type {1466 return resultTypes.At(i).Type()1467 }1468 w.multiExpr(stmt, dstType, syntax.UnpackListExpr(stmt.Results))14691470 case *syntax.SelectStmt:1471 w.Code(stmtSelect)1472 w.selectStmt(stmt)14731474 case *syntax.SendStmt:1475 chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan)14761477 w.Code(stmtSend)1478 w.pos(stmt)1479 w.expr(stmt.Chan)1480 w.implicitConvExpr(chanType.Elem(), stmt.Value)14811482 case *syntax.SwitchStmt:1483 w.Code(stmtSwitch)1484 w.switchStmt(stmt)1485 }1486}14871488func (w *writer) assignList(expr syntax.Expr) {1489 exprs := syntax.UnpackListExpr(expr)1490 w.Len(len(exprs))14911492 for _, expr := range exprs {1493 w.assign(expr)1494 }1495}14961497func (w *writer) assign(expr syntax.Expr) {1498 expr = syntax.Unparen(expr)14991500 if name, ok := expr.(*syntax.Name); ok {1501 if name.Value == "_" {1502 w.Code(assignBlank)1503 return1504 }15051506 if obj, ok := w.p.info.Defs[name]; ok {1507 obj := obj.(*types2.Var)15081509 w.Code(assignDef)1510 w.pos(obj)1511 w.localIdent(obj)1512 w.typ(obj.Type())15131514 // TODO(mdempsky): Minimize locals index size by deferring1515 // this until the variables actually come into scope.1516 w.addLocal(obj)1517 return1518 }1519 }15201521 w.Code(assignExpr)1522 w.expr(expr)1523}15241525func (w *writer) declStmt(decl syntax.Decl) {1526 switch decl := decl.(type) {1527 default:1528 w.p.unexpected("declaration", decl)15291530 case *syntax.ConstDecl, *syntax.TypeDecl:15311532 case *syntax.VarDecl:1533 w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values)1534 }1535}15361537// assignStmt writes out an assignment for "lhs = rhs".1538func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) {1539 lhs := syntax.UnpackListExpr(lhs0)1540 rhs := syntax.UnpackListExpr(rhs0)15411542 w.Code(stmtAssign)1543 w.pos(pos)15441545 // As if w.assignList(lhs0).1546 w.Len(len(lhs))1547 for _, expr := range lhs {1548 w.assign(expr)1549 }15501551 dstType := func(i int) types2.Type {1552 dst := lhs[i]15531554 // Finding dstType is somewhat involved, because for VarDecl1555 // statements, the Names are only added to the info.{Defs,Uses}1556 // maps, not to info.Types.1557 if name, ok := syntax.Unparen(dst).(*syntax.Name); ok {1558 if name.Value == "_" {1559 return nil // ok: no implicit conversion1560 } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok {1561 return def.Type()1562 } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok {1563 return use.Type()1564 } else {1565 w.p.fatalf(dst, "cannot find type of destination object: %v", dst)1566 }1567 }15681569 return w.p.typeOf(dst)1570 }15711572 w.multiExpr(pos, dstType, rhs)1573}15741575func (w *writer) blockStmt(stmt *syntax.BlockStmt) {1576 w.Sync(pkgbits.SyncBlockStmt)1577 w.openScope(stmt.Pos())1578 w.stmts(stmt.List)1579 w.closeScope(stmt.Rbrace)1580}15811582func (w *writer) forStmt(stmt *syntax.ForStmt) {1583 w.Sync(pkgbits.SyncForStmt)1584 w.openScope(stmt.Pos())15851586 if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {1587 w.pos(rang)1588 w.assignList(rang.Lhs)1589 w.expr(rang.X)15901591 xtyp := w.p.typeOf(rang.X)1592 if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap {1593 w.rtype(xtyp)1594 }1595 {1596 lhs := syntax.UnpackListExpr(rang.Lhs)1597 assign := func(i int, src types2.Type) {1598 if i >= len(lhs) {1599 return1600 }1601 dst := syntax.Unparen(lhs[i])1602 if name, ok := dst.(*syntax.Name); ok && name.Value == "_" {1603 return1604 }16051606 var dstType types2.Type1607 if rang.Def {1608 // For `:=` assignments, the LHS names only appear in Defs,1609 // not Types (as used by typeOf).1610 dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type()1611 } else {1612 dstType = w.p.typeOf(dst)1613 }16141615 w.convRTTI(src, dstType)1616 }16171618 keyType, valueType := types2.RangeKeyVal(w.p.typeOf(rang.X))1619 assign(0, keyType)1620 assign(1, valueType)1621 }16221623 } else {1624 if stmt.Cond != nil && w.p.staticBool(&stmt.Cond) < 0 { // always false1625 stmt.Post = nil1626 stmt.Body.List = nil1627 }16281629 w.pos(stmt)1630 w.stmt(stmt.Init)1631 w.optExpr(stmt.Cond)1632 w.stmt(stmt.Post)1633 }16341635 w.blockStmt(stmt.Body)1636 w.Bool(w.distinctVars(stmt))1637 w.closeAnotherScope()1638}16391640func (w *writer) distinctVars(stmt *syntax.ForStmt) bool {1641 lv := base.Debug.LoopVar1642 fileVersion := w.p.info.FileVersions[stmt.Pos().FileBase()]1643 is122 := fileVersion == "" || version.Compare(fileVersion, "go1.22") >= 016441645 // Turning off loopvar for 1.22 is only possible with loopvarhash=qn1646 //1647 // Debug.LoopVar values to be preserved for 1.21 compatibility are 1 and 2,1648 // which are also set (=1) by GOEXPERIMENT=loopvar. The knobs for turning on1649 // the new, unshared, loopvar behavior apply to versions less than 1.21 because1650 // (1) 1.21 also did that and (2) this is believed to be the likely use case;1651 // anyone checking to see if it affects their code will just run the GOEXPERIMENT1652 // but will not also update all their go.mod files to 1.21.1653 //1654 // -gcflags=-d=loopvar=3 enables logging for 1.22 but does not turn loopvar on for <= 1.21.16551656 return is122 || lv > 0 && lv != 31657}16581659func (w *writer) ifStmt(stmt *syntax.IfStmt) {1660 cond := w.p.staticBool(&stmt.Cond)16611662 w.Sync(pkgbits.SyncIfStmt)1663 w.openScope(stmt.Pos())1664 w.pos(stmt)1665 w.stmt(stmt.Init)1666 w.expr(stmt.Cond)1667 w.Int(cond)1668 if cond >= 0 {1669 w.blockStmt(stmt.Then)1670 } else {1671 w.pos(stmt.Then.Rbrace)1672 }1673 if cond <= 0 {1674 w.stmt(stmt.Else)1675 }1676 w.closeAnotherScope()1677}16781679func (w *writer) selectStmt(stmt *syntax.SelectStmt) {1680 w.Sync(pkgbits.SyncSelectStmt)16811682 w.pos(stmt)1683 w.Len(len(stmt.Body))1684 for i, clause := range stmt.Body {1685 if i > 0 {1686 w.closeScope(clause.Pos())1687 }1688 w.openScope(clause.Pos())16891690 w.pos(clause)1691 w.stmt(clause.Comm)1692 w.stmts(clause.Body)1693 }1694 if len(stmt.Body) > 0 {1695 w.closeScope(stmt.Rbrace)1696 }1697}16981699func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {1700 w.Sync(pkgbits.SyncSwitchStmt)17011702 w.openScope(stmt.Pos())1703 w.pos(stmt)1704 w.stmt(stmt.Init)17051706 var iface, tagType types2.Type1707 var tagTypeIsChan bool1708 if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {1709 iface = w.p.typeOf(guard.X)17101711 w.pos(guard)1712 if tag := guard.Lhs; w.Bool(tag != nil) {1713 w.pos(tag)17141715 // Like w.localIdent, but we don't have a types2.Object.1716 w.Sync(pkgbits.SyncLocalIdent)1717 w.pkg(w.p.curpkg)1718 w.String(tag.Value)1719 }1720 w.expr(guard.X)1721 } else {1722 tag := stmt.Tag17231724 var tagValue constant.Value1725 if tag != nil {1726 tv := w.p.typeAndValue(tag)1727 tagType = tv.Type1728 tagValue = tv.Value1729 _, tagTypeIsChan = tagType.Underlying().(*types2.Chan)1730 } else {1731 tagType = types2.Typ[types2.Bool]1732 tagValue = constant.MakeBool(true)1733 }17341735 if tagValue != nil {1736 // If the switch tag has a constant value, look for a case1737 // clause that we always branch to.1738 func() {1739 var target *syntax.CaseClause1740 Outer:1741 for _, clause := range stmt.Body {1742 if clause.Cases == nil {1743 target = clause1744 }1745 for _, cas := range syntax.UnpackListExpr(clause.Cases) {1746 tv := w.p.typeAndValue(cas)1747 if tv.Value == nil {1748 return // non-constant case; give up1749 }1750 if constant.Compare(tagValue, token.EQL, tv.Value) {1751 target = clause1752 break Outer1753 }1754 }1755 }1756 // We've found the target clause, if any.17571758 if target != nil {1759 if hasFallthrough(target.Body) {1760 return // fallthrough is tricky; give up1761 }17621763 // Rewrite as single "default" case.1764 target.Cases = nil1765 stmt.Body = []*syntax.CaseClause{target}1766 } else {1767 stmt.Body = nil1768 }17691770 // Clear switch tag (i.e., replace with implicit "true").1771 tag = nil1772 stmt.Tag = nil1773 tagType = types2.Typ[types2.Bool]1774 }()1775 }17761777 // Walk is going to emit comparisons between the tag value and1778 // each case expression, and we want these comparisons to always1779 // have the same type. If there are any case values that can't be1780 // converted to the tag value's type, then convert everything to1781 // `any` instead.1782 //1783 // Except that we need to keep comparisons of channel values from1784 // being wrapped in any(). See issue #67190.17851786 if !tagTypeIsChan {1787 Outer:1788 for _, clause := range stmt.Body {1789 for _, cas := range syntax.UnpackListExpr(clause.Cases) {1790 if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) && (types2.IsInterface(casType) || types2.IsInterface(tagType)) {1791 tagType = types2.NewInterfaceType(nil, nil)1792 break Outer1793 }1794 }1795 }1796 }17971798 if w.Bool(tag != nil) {1799 w.implicitConvExpr(tagType, tag)1800 }1801 }18021803 w.Len(len(stmt.Body))1804 for i, clause := range stmt.Body {1805 if i > 0 {1806 w.closeScope(clause.Pos())1807 }1808 w.openScope(clause.Pos())18091810 w.pos(clause)18111812 cases := syntax.UnpackListExpr(clause.Cases)1813 if iface != nil {1814 w.Len(len(cases))1815 for _, cas := range cases {1816 if w.Bool(isNil(w.p, cas)) {1817 continue1818 }1819 w.exprType(iface, cas)1820 }1821 } else {1822 // As if w.exprList(clause.Cases),1823 // but with implicit conversions to tagType.18241825 w.Sync(pkgbits.SyncExprList)1826 w.Sync(pkgbits.SyncExprs)1827 w.Len(len(cases))1828 for _, cas := range cases {1829 typ := tagType1830 if tagTypeIsChan {1831 typ = nil1832 }1833 w.implicitConvExpr(typ, cas)1834 }1835 }18361837 if obj, ok := w.p.info.Implicits[clause]; ok {1838 // TODO(mdempsky): These pos details are quirkish, but also1839 // necessary so the variable's position is correct for DWARF1840 // scope assignment later. It would probably be better for us to1841 // instead just set the variable's DWARF scoping info earlier so1842 // we can give it the correct position information.1843 pos := clause.Pos()1844 if typs := syntax.UnpackListExpr(clause.Cases); len(typs) != 0 {1845 pos = typeExprEndPos(typs[len(typs)-1])1846 }1847 w.pos(pos)18481849 obj := obj.(*types2.Var)1850 w.typ(obj.Type())1851 w.addLocal(obj)1852 }18531854 w.stmts(clause.Body)1855 }1856 if len(stmt.Body) > 0 {1857 w.closeScope(stmt.Rbrace)1858 }18591860 w.closeScope(stmt.Rbrace)1861}18621863func (w *writer) label(label *syntax.Name) {1864 w.Sync(pkgbits.SyncLabel)18651866 // TODO(mdempsky): Replace label strings with dense indices.1867 w.String(label.Value)1868}18691870func (w *writer) optLabel(label *syntax.Name) {1871 w.Sync(pkgbits.SyncOptLabel)1872 if w.Bool(label != nil) {1873 w.label(label)1874 }1875}18761877// @@@ Expressions18781879// expr writes the given expression into the function body bitstream.1880func (w *writer) expr(expr syntax.Expr) {1881 base.Assertf(expr != nil, "missing expression")18821883 expr = syntax.Unparen(expr) // skip parens; unneeded after typecheck18841885 obj, inst := lookupObj(w.p, expr)1886 targs := asTypeSlice(inst.TypeArgs)18871888 if tv, ok := w.p.maybeTypeAndValue(expr); ok {1889 if tv.IsRuntimeHelper() {1890 if pkg := obj.Pkg(); pkg != nil && pkg.Name() == "runtime" {1891 objName := obj.Name()1892 w.Code(exprRuntimeBuiltin)1893 w.String(objName)1894 return1895 }1896 }18971898 if tv.IsType() {1899 w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr))1900 }19011902 if tv.Value != nil {1903 w.Code(exprConst)1904 w.pos(expr)1905 typ := idealType(tv)1906 assert(typ != nil)1907 w.typ(typ)1908 w.Value(tv.Value)1909 return1910 }19111912 if _, isNil := obj.(*types2.Nil); isNil {1913 w.Code(exprZero)1914 w.pos(expr)1915 w.typ(tv.Type)1916 return1917 }19181919 // With shape types (and particular pointer shaping), we may have1920 // an expression of type "go.shape.*uint8", but need to reshape it1921 // to another shape-identical type to allow use in field1922 // selection, indexing, etc.1923 if typ := tv.Type; !tv.IsBuiltin() && !isTuple(typ) && !isUntyped(typ) {1924 w.Code(exprReshape)1925 w.typ(typ)1926 // fallthrough1927 }1928 }19291930 if obj != nil {1931 if len(targs) != 0 {1932 obj := obj.(*types2.Func)19331934 w.Code(exprFuncInst)1935 w.pos(expr)1936 w.funcInst(obj, targs)1937 return1938 }19391940 if isGlobal(obj) {1941 w.Code(exprGlobal)1942 w.obj(obj, nil)1943 return1944 }19451946 obj := obj.(*types2.Var)1947 assert(!obj.IsField())19481949 w.Code(exprLocal)1950 w.useLocal(expr.Pos(), obj)1951 return1952 }19531954 switch expr := expr.(type) {1955 default:1956 w.p.unexpected("expression", expr)19571958 case *syntax.CompositeLit:1959 w.Code(exprCompLit)1960 w.compLit(expr)19611962 case *syntax.FuncLit:1963 w.Code(exprFuncLit)1964 w.funcLit(expr)19651966 case *syntax.SelectorExpr:1967 sel, ok := w.p.info.Selections[expr]1968 assert(ok)19691970 switch sel.Kind() {1971 default:1972 w.p.fatalf(expr, "unexpected selection kind: %v", sel.Kind())19731974 case types2.FieldVal:1975 w.Code(exprFieldVal)1976 w.expr(expr.X)1977 w.pos(expr)1978 w.selector(sel.Obj())19791980 case types2.MethodVal:1981 w.methVal(expr, sel)19821983 case types2.MethodExpr:1984 w.methExpr(expr, sel)1985 }19861987 case *syntax.IndexExpr:1988 // might be explicit instantiation of a generic method1989 if selector, ok := expr.X.(*syntax.SelectorExpr); ok {1990 if sel, ok := w.p.info.Selections[selector]; ok {1991 switch sel.Kind() {1992 default:1993 w.p.fatalf(selector, "unexpected selection kind: %v", sel.Kind())1994 case types2.FieldVal:1995 // not a method1996 case types2.MethodVal:1997 w.methVal(selector, sel)1998 return1999 case types2.MethodExpr:2000 w.methExpr(selector, sel)
Findings
✓ No findings reported for this file.