// UNREVIEWED // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package noder import ( "fmt" "go/constant" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" ) type pkgWriter struct { pkgEncoder m posMap curpkg *types2.Package info *types2.Info posBasesIdx map[*syntax.PosBase]int pkgsIdx map[*types2.Package]int typsIdx map[types2.Type]int globalsIdx map[types2.Object]int funDecls map[*types2.Func]*syntax.FuncDecl typDecls map[*types2.TypeName]typeDeclGen linknames map[types2.Object]string cgoPragmas [][]string dups dupTypes } func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { return &pkgWriter{ pkgEncoder: newPkgEncoder(), m: m, curpkg: pkg, info: info, pkgsIdx: make(map[*types2.Package]int), globalsIdx: make(map[types2.Object]int), typsIdx: make(map[types2.Type]int), posBasesIdx: make(map[*syntax.PosBase]int), funDecls: make(map[*types2.Func]*syntax.FuncDecl), typDecls: make(map[*types2.TypeName]typeDeclGen), linknames: make(map[types2.Object]string), } } func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) { base.ErrorfAt(pw.m.pos(p), msg, args...) } func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) { base.FatalfAt(pw.m.pos(p), msg, args...) } func (pw *pkgWriter) unexpected(what string, p poser) { pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) } type writer struct { p *pkgWriter encoder // For writing out object descriptions, ext points to the extension // writer for where we can write the compiler's private extension // details for the object. // // TODO(mdempsky): This is a little hacky, but works easiest with // the way things are currently. ext *writer // 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). // variables declared within this function localsIdx map[*types2.Var]int closureVars []posObj closureVarsIdx map[*types2.Var]int dict *writerDict 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]int // funcs lists references to generic functions that were // instantiated with derived types (i.e., that require // sub-dictionaries when called at run time). funcs []objInfo } type derivedInfo struct { idx int needed bool } type typeInfo struct { idx int derived bool } type objInfo struct { idx int // index for the generic function declaration explicits []typeInfo // info for the type arguments } func (info objInfo) anyDerived() bool { for _, explicit := range info.explicits { if explicit.derived { return true } } return false } 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 } func (pw *pkgWriter) newWriter(k reloc, marker syncMarker) *writer { return &writer{ encoder: pw.newEncoder(k, marker), p: pw, } } // @@@ Positions func (w *writer) pos(p poser) { w.sync(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. Also, if there's a b-side, update // its position base too (but not vice versa!). w.posBase(pos.Base()) w.uint(pos.Line()) w.uint(pos.Col()) } func (w *writer) posBase(b *syntax.PosBase) { w.reloc(relocPosBase, w.p.posBaseIdx(b)) } func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) int { if idx, ok := pw.posBasesIdx[b]; ok { return idx } w := pw.newWriter(relocPosBase, 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 func (w *writer) pkg(pkg *types2.Package) { w.sync(syncPkg) w.reloc(relocPkg, w.p.pkgIdx(pkg)) } func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int { if idx, ok := pw.pkgsIdx[pkg]; ok { return idx } w := pw.newWriter(relocPkg, syncPkgDef) pw.pkgsIdx[pkg] = w.idx if pkg == nil { w.string("builtin") } else { var path string if pkg != w.p.curpkg { path = pkg.Path() } w.string(path) w.string(pkg.Name()) w.len(pkg.Height()) w.len(len(pkg.Imports())) for _, imp := range pkg.Imports() { w.pkg(imp) } } return w.flush() } // @@@ Types func (w *writer) typ(typ types2.Type) { w.typInfo(w.p.typIdx(typ, w.dict)) } func (w *writer) typInfo(info typeInfo) { w.sync(syncType) if w.bool(info.derived) { w.len(info.idx) w.derived = true } else { w.reloc(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 quirksMode() { typ = pw.dups.orig(typ) } 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(relocType, syncTypeIdx) w.dict = dict switch typ := typ.(type) { default: base.Fatalf("unexpected type: %v (%T)", typ, typ) case *types2.Basic: switch kind := typ.Kind(); { case kind == types2.Invalid: base.Fatalf("unexpected types2.Invalid") case types2.Typ[kind] == typ: w.code(typeBasic) w.len(int(kind)) default: // Handle "byte" and "rune" as references to their TypeName. obj := types2.Universe.Lookup(typ.Name()) assert(obj.Type() == typ) w.code(typeNamed) w.obj(obj, nil) } case *types2.Named: // Type aliases can refer to uninstantiated generic types, so we // might see len(TParams) != 0 && len(TArgs) == 0 here. // TODO(mdempsky): Revisit after #46477 is resolved. assert(typ.TParams().Len() == typ.TArgs().Len() || typ.TArgs().Len() == 0) // TODO(mdempsky): Why do we need to loop here? orig := typ for orig.TArgs() != nil { orig = orig.Orig() } w.code(typeNamed) w.obj(orig.Obj(), typ.TArgs()) case *types2.TypeParam: index := func() int { for idx, name := range w.dict.implicits { if name.Type().(*types2.TypeParam) == typ { return idx } } return len(w.dict.implicits) + typ.Index() }() w.derived = true w.code(typeTypeParam) w.len(index) case *types2.Array: w.code(typeArray) w.uint64(uint64(typ.Len())) w.typ(typ.Elem()) case *types2.Chan: w.code(typeChan) w.len(int(typ.Dir())) w.typ(typ.Elem()) case *types2.Map: w.code(typeMap) w.typ(typ.Key()) w.typ(typ.Elem()) case *types2.Pointer: w.code(typePointer) w.typ(typ.Elem()) case *types2.Signature: assert(typ.TParams() == nil) w.code(typeSignature) w.signature(typ) case *types2.Slice: w.code(typeSlice) w.typ(typ.Elem()) case *types2.Struct: w.code(typeStruct) w.structType(typ) case *types2.Interface: w.code(typeInterface) w.interfaceType(typ) case *types2.Union: w.code(typeUnion) w.unionType(typ) } if w.derived { idx := 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) { w.len(typ.NumExplicitMethods()) w.len(typ.NumEmbeddeds()) for i := 0; i < typ.NumExplicitMethods(); i++ { m := typ.ExplicitMethod(i) sig := m.Type().(*types2.Signature) assert(sig.TParams() == 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(syncSignature) w.params(sig.Params()) w.params(sig.Results()) w.bool(sig.Variadic()) } func (w *writer) params(typ *types2.Tuple) { w.sync(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(syncParam) w.pos(param) w.localIdent(param) w.typ(param.Type()) } // @@@ Objects func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { explicitInfos := make([]typeInfo, explicits.Len()) for i := range explicitInfos { explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict) } info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos} if _, ok := obj.(*types2.Func); ok && info.anyDerived() { idx := -1 for i, prev := range w.dict.funcs { if prev.equals(info) { idx = i } } if idx < 0 { idx = len(w.dict.funcs) w.dict.funcs = append(w.dict.funcs, info) } // TODO(mdempsky): Push up into expr; this shouldn't appear // outside of expression context. w.sync(syncObject) w.bool(true) w.len(idx) return } // TODO(mdempsky): Push up into typIdx; this shouldn't be needed // except while writing out types. if isDefinedType(obj) && obj.Pkg() == w.p.curpkg { decl, ok := w.p.typDecls[obj.(*types2.TypeName)] assert(ok) if len(decl.implicits) != 0 { w.derived = true } } w.sync(syncObject) w.bool(false) w.reloc(relocObj, info.idx) w.len(len(info.explicits)) for _, info := range info.explicits { w.typInfo(info) } } func (pw *pkgWriter) objIdx(obj types2.Object) int { if idx, ok := pw.globalsIdx[obj]; ok { return idx } dict := &writerDict{ derivedIdx: make(map[types2.Type]int), } if isDefinedType(obj) && obj.Pkg() == pw.curpkg { decl, ok := pw.typDecls[obj.(*types2.TypeName)] assert(ok) dict.implicits = decl.implicits } w := pw.newWriter(relocObj, syncObject1) w.ext = pw.newWriter(relocObjExt, syncObject1) wname := pw.newWriter(relocName, syncObject1) wdict := pw.newWriter(relocObjDict, syncObject1) pw.globalsIdx[obj] = w.idx // break cycles assert(w.ext.idx == w.idx) assert(wname.idx == w.idx) assert(wdict.idx == w.idx) w.dict = dict w.ext.dict = dict code := w.doObj(obj) w.flush() w.ext.flush() wname.qualifiedIdent(obj) wname.code(code) wname.flush() wdict.objDict(obj, w.dict) wdict.flush() return w.idx } func (w *writer) doObj(obj types2.Object) codeObj { if obj.Pkg() != w.p.curpkg { return objStub } switch obj := obj.(type) { default: w.p.unexpected("object", obj) panic("unreachable") case *types2.Const: w.pos(obj) w.value(obj.Type(), obj.Val()) return objConst case *types2.Func: decl, ok := w.p.funDecls[obj] assert(ok) sig := obj.Type().(*types2.Signature) w.pos(obj) w.typeParamNames(sig.TParams()) w.signature(sig) w.pos(decl) w.ext.funcExt(obj) return objFunc case *types2.TypeName: decl, ok := w.p.typDecls[obj] assert(ok) if obj.IsAlias() { w.pos(obj) w.typ(obj.Type()) return objAlias } named := obj.Type().(*types2.Named) assert(named.TArgs() == nil) w.pos(obj) w.typeParamNames(named.TParams()) w.ext.typeExt(obj) w.typExpr(decl.Type) w.len(named.NumMethods()) for i := 0; i < named.NumMethods(); i++ { w.method(named.Method(i)) } return objType case *types2.Var: w.pos(obj) w.typ(obj.Type()) w.ext.varExt(obj) return objVar } } // typExpr writes the type represented by the given expression. func (w *writer) typExpr(expr syntax.Expr) { tv, ok := w.p.info.Types[expr] assert(ok) assert(tv.IsType()) w.typ(tv.Type) } func (w *writer) value(typ types2.Type, val constant.Value) { w.sync(syncValue) w.typ(typ) w.rawValue(val) } // 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(relocType, typ.idx) w.bool(typ.needed) } nfuncs := len(dict.funcs) w.len(nfuncs) for _, fn := range dict.funcs { w.reloc(relocObj, fn.idx) w.len(len(fn.explicits)) for _, targ := range fn.explicits { w.typInfo(targ) } } assert(len(dict.derived) == nderived) assert(len(dict.funcs) == nfuncs) } func (w *writer) typeParamNames(tparams *types2.TParamList) { w.sync(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(meth *types2.Func) { decl, ok := w.p.funDecls[meth] assert(ok) sig := meth.Type().(*types2.Signature) w.sync(syncMethod) w.pos(meth) w.selector(meth) w.typeParamNames(sig.RParams()) w.param(sig.Recv()) w.signature(sig) w.pos(decl) // XXX: Hack to workaround linker limitations. w.ext.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(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 { // TODO(mdempsky): Find a better solution than embedding middle // dot in the symbol name; 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 name, within the context of a // particular function). func (w *writer) localIdent(obj types2.Object) { assert(!isGlobal(obj)) w.sync(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.sync(syncSelector) w.pkg(obj.Pkg()) w.string(obj.Name()) } // @@@ 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") } if decl.Body != nil { if pragma&ir.Noescape != 0 { w.p.errorf(decl, "can only use //go:noescape with external func implementations") } } 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. if _, ok := w.p.linknames[obj]; !ok { w.p.errorf(decl, "missing function body") } } } sig, block := obj.Type().(*types2.Signature), decl.Body body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict) assert(len(closureVars) == 0) w.sync(syncFuncExt) w.pragmaFlag(pragma) w.linkname(obj) w.bool(false) // stub extension w.reloc(relocBody, body) w.sync(syncEOF) } func (w *writer) typeExt(obj *types2.TypeName) { decl, ok := w.p.typDecls[obj] assert(ok) w.sync(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(syncVarExt) w.linkname(obj) } func (w *writer) linkname(obj types2.Object) { w.sync(syncLinkname) w.int64(-1) w.string(w.p.linknames[obj]) } func (w *writer) pragmaFlag(p ir.PragmaFlag) { w.sync(syncPragma) w.int(int(p)) } // @@@ Function bodies func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx int, closureVars []posObj) { w := pw.newWriter(relocBody, syncFuncBody) w.dict = dict w.funcargs(sig) if w.bool(block != nil) { w.stmts(block.List) w.pos(block.Rbrace) } return w.flush(), w.closureVars } func (w *writer) funcargs(sig *types2.Signature) { do := func(params *types2.Tuple, result bool) { for i := 0; i < params.Len(); i++ { w.funcarg(params.At(i), result) } } if recv := sig.Recv(); recv != nil { w.funcarg(recv, false) } do(sig.Params(), false) do(sig.Results(), true) } func (w *writer) funcarg(param *types2.Var, result bool) { if param.Name() != "" || result { w.addLocal(param) } } func (w *writer) addLocal(obj *types2.Var) { w.sync(syncAddLocal) idx := len(w.localsIdx) if enableSync { w.int(idx) } if w.localsIdx == nil { w.localsIdx = make(map[*types2.Var]int) } w.localsIdx[obj] = idx } func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { w.sync(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, posObj{pos, obj}) w.closureVarsIdx[obj] = idx } w.len(idx) } func (w *writer) openScope(pos syntax.Pos) { w.sync(syncOpenScope) w.pos(pos) } func (w *writer) closeScope(pos syntax.Pos) { w.sync(syncCloseScope) w.pos(pos) w.closeAnotherScope() } func (w *writer) closeAnotherScope() { w.sync(syncCloseAnotherScope) } // @@@ Statements 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) { w.sync(syncStmts) for _, stmt := range stmts { w.stmt1(stmt) } w.code(stmtEnd) w.sync(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) w.expr(stmt.Rhs) default: w.code(stmtAssign) w.pos(stmt) w.exprList(stmt.Rhs) w.assignList(stmt.Lhs) } 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) 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) w.exprList(stmt.Results) case *syntax.SelectStmt: w.code(stmtSelect) w.selectStmt(stmt) case *syntax.SendStmt: w.code(stmtSend) w.pos(stmt) w.expr(stmt.Chan) w.expr(stmt.Value) case *syntax.SwitchStmt: w.code(stmtSwitch) w.switchStmt(stmt) } } func (w *writer) assignList(expr syntax.Expr) { exprs := unpackListExpr(expr) w.len(len(exprs)) for _, expr := range exprs { if name, ok := expr.(*syntax.Name); ok && name.Value != "_" { if obj, ok := w.p.info.Defs[name]; ok { obj := obj.(*types2.Var) w.bool(true) 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) continue } } w.bool(false) w.expr(expr) } } func (w *writer) declStmt(decl syntax.Decl) { switch decl := decl.(type) { default: w.p.unexpected("declaration", decl) case *syntax.ConstDecl: case *syntax.TypeDecl: // Quirk: The legacy inliner doesn't support inlining functions // with type declarations. Unified IR doesn't have any need to // write out type declarations explicitly (they're always looked // up via global index tables instead), so we just write out a // marker so the reader knows to synthesize a fake declaration to // prevent inlining. if quirksMode() { w.code(stmtTypeDeclHack) } case *syntax.VarDecl: values := unpackListExpr(decl.Values) // Quirk: When N variables are declared with N initialization // values, we need to decompose that into N interleaved // declarations+initializations, because it leads to different // (albeit semantically equivalent) code generation. if quirksMode() && len(decl.NameList) == len(values) { for i, name := range decl.NameList { w.code(stmtAssign) w.pos(decl) w.exprList(values[i]) w.assignList(name) } break } w.code(stmtAssign) w.pos(decl) w.exprList(decl.Values) w.assignList(namesAsExpr(decl.NameList)) } } func (w *writer) blockStmt(stmt *syntax.BlockStmt) { w.sync(syncBlockStmt) w.openScope(stmt.Pos()) w.stmts(stmt.List) w.closeScope(stmt.Rbrace) } func (w *writer) forStmt(stmt *syntax.ForStmt) { w.sync(syncForStmt) w.openScope(stmt.Pos()) if rang, ok := stmt.Init.(*syntax.RangeClause); w.bool(ok) { w.pos(rang) w.expr(rang.X) w.assignList(rang.Lhs) } else { w.pos(stmt) w.stmt(stmt.Init) w.expr(stmt.Cond) w.stmt(stmt.Post) } w.blockStmt(stmt.Body) w.closeAnotherScope() } func (w *writer) ifStmt(stmt *syntax.IfStmt) { w.sync(syncIfStmt) w.openScope(stmt.Pos()) w.pos(stmt) w.stmt(stmt.Init) w.expr(stmt.Cond) w.blockStmt(stmt.Then) w.stmt(stmt.Else) w.closeAnotherScope() } func (w *writer) selectStmt(stmt *syntax.SelectStmt) { w.sync(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(syncSwitchStmt) w.openScope(stmt.Pos()) w.pos(stmt) w.stmt(stmt.Init) if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.bool(ok) { w.pos(guard) if tag := guard.Lhs; w.bool(tag != nil) { w.pos(tag) w.string(tag.Value) } w.expr(guard.X) } else { w.expr(stmt.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) w.exprList(clause.Cases) 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 := 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(syncLabel) // TODO(mdempsky): Replace label strings with dense indices. w.string(label.Value) } func (w *writer) optLabel(label *syntax.Name) { w.sync(syncOptLabel) if w.bool(label != nil) { w.label(label) } } // @@@ Expressions func (w *writer) expr(expr syntax.Expr) { expr = unparen(expr) // skip parens; unneeded after typecheck obj, targs := lookupObj(w.p.info, expr) if tv, ok := w.p.info.Types[expr]; ok { // TODO(mdempsky): Be more judicious about which types are marked as "needed". w.needType(tv.Type) if tv.IsType() { w.code(exprType) w.typ(tv.Type) return } if tv.Value != nil { pos := expr.Pos() if quirksMode() { if obj != nil { // Quirk: IR (and thus iexport) doesn't track position // information for uses of declared objects. pos = syntax.Pos{} } else if tv.Value.Kind() == constant.String { // Quirk: noder.sum picks a particular position for certain // string concatenations. pos = sumPos(expr) } } w.code(exprConst) w.pos(pos) w.value(tv.Type, tv.Value) // TODO(mdempsky): These details are only important for backend // diagnostics. Explore writing them out separately. w.op(constExprOp(expr)) w.string(syntax.String(expr)) return } } if obj != nil { if isGlobal(obj) { w.code(exprName) w.obj(obj, targs) return } obj := obj.(*types2.Var) assert(targs.Len() == 0) w.code(exprLocal) w.useLocal(expr.Pos(), obj) return } switch expr := expr.(type) { default: w.p.unexpected("expression", expr) case nil: // absent slice index, for condition, or switch tag w.code(exprNone) case *syntax.Name: assert(expr.Value == "_") w.code(exprBlank) 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) w.code(exprSelector) w.expr(expr.X) w.pos(expr) w.selector(sel.Obj()) case *syntax.IndexExpr: tv, ok := w.p.info.Types[expr.Index] assert(ok && tv.IsValue()) w.code(exprIndex) w.expr(expr.X) w.pos(expr) w.expr(expr.Index) case *syntax.SliceExpr: w.code(exprSlice) w.expr(expr.X) w.pos(expr) for _, n := range &expr.Index { w.expr(n) } case *syntax.AssertExpr: w.code(exprAssert) w.expr(expr.X) w.pos(expr) w.expr(expr.Type) case *syntax.Operation: if expr.Y == nil { w.code(exprUnaryOp) w.op(unOps[expr.Op]) w.pos(expr) w.expr(expr.X) break } w.code(exprBinaryOp) w.op(binOps[expr.Op]) w.expr(expr.X) w.pos(expr) w.expr(expr.Y) case *syntax.CallExpr: tv, ok := w.p.info.Types[expr.Fun] assert(ok) if tv.IsType() { assert(len(expr.ArgList) == 1) assert(!expr.HasDots) w.code(exprConvert) w.typ(tv.Type) w.pos(expr) w.expr(expr.ArgList[0]) break } writeFunExpr := func() { if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok { if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { w.expr(selector.X) w.bool(true) // method call w.pos(selector) w.selector(sel.Obj()) return } } if inf, ok := w.p.info.Inferred[expr]; ok { obj, _ := lookupObj(w.p.info, expr.Fun) assert(obj != nil) // As if w.expr(expr.Fun), but using inf.TArgs instead. w.code(exprName) w.obj(obj, inf.TArgs) } else { w.expr(expr.Fun) } w.bool(false) // not a method call (i.e., normal function call) } w.code(exprCall) writeFunExpr() w.pos(expr) w.exprs(expr.ArgList) w.bool(expr.HasDots) } } func (w *writer) compLit(lit *syntax.CompositeLit) { tv, ok := w.p.info.Types[lit] assert(ok) w.sync(syncCompLit) w.pos(lit) w.typ(tv.Type) typ := tv.Type if ptr, ok := typ.Underlying().(*types2.Pointer); ok { typ = ptr.Elem() } str, isStruct := typ.Underlying().(*types2.Struct) w.len(len(lit.ElemList)) for i, elem := range lit.ElemList { if isStruct { if kv, ok := elem.(*syntax.KeyValueExpr); ok { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) w.len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name))) elem = kv.Value } else { w.pos(elem) 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.expr(kv.Key) elem = kv.Value } } w.pos(elem) w.expr(elem) } } func (w *writer) funcLit(expr *syntax.FuncLit) { tv, ok := w.p.info.Types[expr] assert(ok) sig := tv.Type.(*types2.Signature) body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict) w.sync(syncFuncLit) w.pos(expr) w.pos(expr.Type) // for QuirksMode w.signature(sig) w.len(len(closureVars)) for _, cv := range closureVars { w.pos(cv.pos) if quirksMode() { cv.pos = expr.Body.Rbrace } w.useLocal(cv.pos, cv.obj) } w.reloc(relocBody, body) } type posObj struct { pos syntax.Pos obj *types2.Var } func (w *writer) exprList(expr syntax.Expr) { w.sync(syncExprList) w.exprs(unpackListExpr(expr)) } func (w *writer) exprs(exprs []syntax.Expr) { if len(exprs) == 0 { assert(exprs == nil) } w.sync(syncExprs) w.len(len(exprs)) for _, expr := range exprs { w.expr(expr) } } 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(syncOp) w.len(int(op)) } func (w *writer) needType(typ types2.Type) { // Decompose tuple into component element types. if typ, ok := typ.(*types2.Tuple); ok { for i := 0; i < typ.Len(); i++ { w.needType(typ.At(i).Type()) } return } if info := w.p.typIdx(typ, w.dict); info.derived { w.dict.derived[info.idx].needed = true } } // @@@ 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 } 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 pkgNameOf(pw.info, 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, typePragmas, 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) } } // Workaround for #46208. For variable declarations that // declare multiple variables and have an explicit type // expression, the type expression is evaluated multiple // times. This affects toolstash -cmp, because iexport is // sensitive to *types.Type pointer identity. if quirksMode() && n.Type != nil { tv, ok := pw.info.Types[n.Type] assert(ok) assert(tv.IsType()) for _, name := range n.NameList { obj := pw.info.Defs[name].(*types2.Var) pw.dups.add(obj.Type(), tv.Type) } } 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: // TODO(mdempsky): Enable after #42938 is fixed. if false { 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) { if quirksMode() { posBases := posBasesOf(noders) w.len(len(posBases)) for _, posBase := range posBases { w.posBase(posBase) } objs := importedObjsOf(w.p.curpkg, w.p.info, noders) w.len(len(objs)) for _, obj := range objs { w.qualifiedIdent(obj) } } w.len(len(w.p.cgoPragmas)) for _, cgoPragma := range w.p.cgoPragmas { w.strings(cgoPragma) } w.sync(syncDecls) for _, p := range noders { for _, decl := range p.file.DeclList { w.pkgDecl(decl) } } w.code(declEnd) w.sync(syncEOF) } 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.RParams() != nil || sig.TParams() != 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.IsConstraint() { break } // Skip aliases to uninstantiated generic types. // TODO(mdempsky): Revisit after #46477 is resolved. if name.IsAlias() { named, ok := name.Type().(*types2.Named) if ok && named.TParams().Len() != 0 && named.TArgs().Len() == 0 { break } } w.code(declOther) w.pkgObjs(decl.Name) case *syntax.VarDecl: w.code(declVar) w.pos(decl) w.pkgObjs(decl.NameList...) w.exprList(decl.Values) 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(syncDeclNames) w.len(len(names)) for _, name := range names { obj, ok := w.p.info.Defs[name] assert(ok) w.sync(syncDeclName) w.obj(obj, nil) } } // @@@ Helpers // 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 type // arguments are returned as well. func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, targs *types2.TypeList) { if index, ok := expr.(*syntax.IndexExpr); ok { if inf, ok := info.Inferred[index]; ok { targs = inf.TArgs } else { args := unpackListExpr(index.Index) if len(args) == 1 { tv, ok := info.Types[args[0]] assert(ok) if tv.IsValue() { return // normal index expression } } list := make([]types2.Type, len(args)) for i, arg := range args { tv, ok := info.Types[arg] assert(ok) assert(tv.IsType()) list[i] = tv.Type } targs = types2.NewTypeList(list) } expr = index.X } // Strip package qualifier, if present. if sel, ok := expr.(*syntax.SelectorExpr); ok { if !isPkgQual(info, sel) { return // normal selector expression } expr = sel.Sel } if name, ok := expr.(*syntax.Name); ok { obj, _ = info.Uses[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 } // recvBase returns the base type for the given receiver parameter. func recvBase(recv *types2.Var) *types2.Named { typ := 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.TParamList { switch obj := obj.(type) { case *types2.Func: sig := obj.Type().(*types2.Signature) if sig.Recv() != nil { return sig.RParams() } return sig.TParams() case *types2.TypeName: if !obj.IsAlias() { return obj.Type().(*types2.Named).TParams() } } return nil } func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag { if p == nil { return 0 } return p.(*pragmas).Flag }