diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/writer.go')
-rw-r--r-- | src/cmd/compile/internal/noder/writer.go | 851 |
1 files changed, 710 insertions, 141 deletions
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index b440ad3a1e..5f8767bf83 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1,5 +1,3 @@ -// 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. @@ -16,6 +14,45 @@ import ( "cmd/compile/internal/types2" ) +// This file implements the Unified IR package writer and defines the +// Unified IR export data format. +// +// Low-level coding details (e.g., byte-encoding of individual +// primitive values, or handling element bitstreams and +// cross-references) are handled by internal/pkgbits, so here we only +// concern ourselves with higher-level worries like mapping Go +// language constructs into elements. + +// There are two central types in the writing process: the "writer" +// type handles writing out individual elements, while the "pkgWriter" +// type keeps track of which elements have already been created. +// +// For each sort of "thing" (e.g., position, package, object, type) +// that can be written into the export data, there are generally +// several methods that work together: +// +// - writer.thing handles writing out a *use* of a thing, which often +// means writing a relocation to that thing's encoded index. +// +// - pkgWriter.thingIdx handles reserving an index for a thing, and +// writing out any elements needed for the thing. +// +// - writer.doThing handles writing out the *definition* of a thing, +// which in general is a mix of low-level coding primitives (e.g., +// ints and strings) or uses of other things. +// +// A design goal of Unified IR is to have a single, canonical writer +// implementation, but multiple reader implementations each tailored +// to their respective needs. For example, within cmd/compile's own +// backend, inlining is implemented largely by just re-running the +// function body reading code. + +// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo, +// and better document the file format boundary between public and +// private data. + +// A pkgWriter constructs Unified IR export data from the results of +// running the types2 type checker on a Go compilation unit. type pkgWriter struct { pkgbits.PkgEncoder @@ -23,18 +60,29 @@ type pkgWriter struct { curpkg *types2.Package info *types2.Info + // Indices for previously written syntax and types2 things. + posBasesIdx map[*syntax.PosBase]pkgbits.Index pkgsIdx map[*types2.Package]pkgbits.Index typsIdx map[types2.Type]pkgbits.Index - globalsIdx map[types2.Object]pkgbits.Index + objsIdx map[types2.Object]pkgbits.Index + + // Maps from types2.Objects back to their syntax.Decl. funDecls map[*types2.Func]*syntax.FuncDecl typDecls map[*types2.TypeName]typeDeclGen - linknames map[types2.Object]string + // linknames maps package-scope objects to their linker symbol name, + // if specified by a //go:linkname directive. + linknames map[types2.Object]string + + // cgoPragmas accumulates any //go:cgo_* pragmas that need to be + // passed through to cmd/link. cgoPragmas [][]string } +// newPkgWriter returns an initialized pkgWriter for the specified +// package. func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { return &pkgWriter{ PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), @@ -43,9 +91,9 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { curpkg: pkg, info: info, - pkgsIdx: make(map[*types2.Package]pkgbits.Index), - globalsIdx: make(map[types2.Object]pkgbits.Index), - typsIdx: make(map[types2.Type]pkgbits.Index), + pkgsIdx: make(map[*types2.Package]pkgbits.Index), + objsIdx: make(map[types2.Object]pkgbits.Index), + typsIdx: make(map[types2.Type]pkgbits.Index), posBasesIdx: make(map[*syntax.PosBase]pkgbits.Index), @@ -56,34 +104,60 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { } } +// errorf reports a user error about thing p. func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) { base.ErrorfAt(pw.m.pos(p), msg, args...) } +// fatalf reports an internal compiler error about thing p. func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) { base.FatalfAt(pw.m.pos(p), msg, args...) } +// unexpected reports a fatal error about a thing of unexpected +// dynamic type. func (pw *pkgWriter) unexpected(what string, p poser) { pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) } +// typeOf returns the Type of the given value expression. +func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type { + tv, ok := pw.info.Types[expr] + if !ok { + pw.fatalf(expr, "missing Types entry: %v", syntax.String(expr)) + } + if !tv.IsValue() { + pw.fatalf(expr, "expected value: %v", syntax.String(expr)) + } + return tv.Type +} + +// A writer provides APIs for writing out an individual element. type writer struct { p *pkgWriter pkgbits.Encoder + // sig holds the signature for the current function body, if any. + sig *types2.Signature + // TODO(mdempsky): We should be able to prune localsIdx whenever a // scope closes, and then maybe we can just use the same map for // storing the TypeParams too (as their TypeName instead). - // variables declared within this function + // localsIdx tracks any local variables declared within this + // function body. It's unused for writing out non-body things. localsIdx map[*types2.Var]int - closureVars []posObj - closureVarsIdx map[*types2.Var]int + // closureVars tracks any free variables that are referenced by this + // function body. It's unused for writing out non-body things. + closureVars []posVar + closureVarsIdx map[*types2.Var]int // index of previously seen free variables + + dict *writerDict - dict *writerDict + // derived tracks whether the type being written out references any + // type parameters. It's unused for writing non-type things. derived bool } @@ -107,6 +181,10 @@ type writerDict struct { // itabs lists itabs that are needed for dynamic type assertions // (including type switches). itabs []itabInfo + + // methodsExprs lists method expressions with derived-type receiver + // parameters. + methodExprs []methodExprInfo } // A derivedInfo represents a reference to an encoded generic Go type. @@ -128,16 +206,38 @@ type typeInfo struct { derived bool } +// An objInfo represents a reference to an encoded, instantiated (if +// applicable) Go object. type objInfo struct { idx pkgbits.Index // index for the generic function declaration explicits []typeInfo // info for the type arguments } +// An itabInfo represents a reference to an encoded itab entry (i.e., +// a non-empty interface type along with a concrete type that +// implements that interface). type itabInfo struct { typIdx pkgbits.Index // always a derived type index iface typeInfo // always a non-empty interface type } +// A methodExprInfo represents a reference to an encoded method +// expression, whose receiver parameter is a derived type. +type methodExprInfo struct { + recvIdx pkgbits.Index // always a derived type index + methodInfo selectorInfo +} + +// A selectorInfo represents a reference to an encoded field or method +// name (i.e., objects that can only be accessed using selector +// expressions). +type selectorInfo struct { + pkgIdx pkgbits.Index + nameIdx pkgbits.Index +} + +// anyDerived reports whether any of info's explicit type arguments +// are derived types. func (info objInfo) anyDerived() bool { for _, explicit := range info.explicits { if explicit.derived { @@ -147,6 +247,8 @@ func (info objInfo) anyDerived() bool { return false } +// equals reports whether info and other represent the same Go object +// (i.e., same base object and identical type arguments, if any). func (info objInfo) equals(other objInfo) bool { if info.idx != other.idx { return false @@ -169,6 +271,7 @@ func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) * // @@@ Positions +// pos writes the position of p into the element bitstream. func (w *writer) pos(p poser) { w.Sync(pkgbits.SyncPos) pos := p.Pos() @@ -178,17 +281,19 @@ func (w *writer) pos(p poser) { return } - // TODO(mdempsky): Delta encoding. Also, if there's a b-side, update - // its position base too (but not vice versa!). + // TODO(mdempsky): Delta encoding. w.posBase(pos.Base()) w.Uint(pos.Line()) w.Uint(pos.Col()) } +// posBase writes a reference to the given PosBase into the element +// bitstream. func (w *writer) posBase(b *syntax.PosBase) { w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b)) } +// posBaseIdx returns the index for the given PosBase. func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { if idx, ok := pw.posBasesIdx[b]; ok { return idx @@ -210,11 +315,18 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { // @@@ Packages +// pkg writes a use of the given Package into the element bitstream. func (w *writer) pkg(pkg *types2.Package) { + w.pkgRef(w.p.pkgIdx(pkg)) +} + +func (w *writer) pkgRef(idx pkgbits.Index) { w.Sync(pkgbits.SyncPkg) - w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg)) + w.Reloc(pkgbits.RelocPkg, idx) } +// pkgIdx returns the index for the given package, adding it to the +// package export data if needed. func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { if idx, ok := pw.pkgsIdx[pkg]; ok { return idx @@ -241,7 +353,6 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path) w.String(path) w.String(pkg.Name()) - w.Len(pkg.Height()) w.Len(len(pkg.Imports())) for _, imp := range pkg.Imports() { @@ -254,12 +365,18 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { // @@@ Types -var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) +var ( + anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) + runeTypeName = types2.Universe.Lookup("rune").(*types2.TypeName) +) +// typ writes a use of the given type into the bitstream. func (w *writer) typ(typ types2.Type) { w.typInfo(w.p.typIdx(typ, w.dict)) } +// typInfo writes a use of the given type (specified as a typeInfo +// instead) into the bitstream. func (w *writer) typInfo(info typeInfo) { w.Sync(pkgbits.SyncType) if w.Bool(info.derived) { @@ -468,6 +585,11 @@ func (w *writer) param(param *types2.Var) { // @@@ Objects +// obj writes a use of the given object into the bitstream. +// +// If obj is a generic object, then explicits are the explicit type +// arguments used to instantiate it (i.e., used to substitute the +// object's own declared type parameters). func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { explicitInfos := make([]typeInfo, explicits.Len()) for i := range explicitInfos { @@ -515,8 +637,13 @@ func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { } } +// objIdx returns the index for the given Object, adding it to the +// export data as needed. func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { - if idx, ok := pw.globalsIdx[obj]; ok { + // TODO(mdempsky): Validate that obj is a global object (or a local + // defined type, which we hoist to global scope anyway). + + if idx, ok := pw.objsIdx[obj]; ok { return idx } @@ -530,12 +657,35 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { dict.implicits = decl.implicits } + // We encode objects into 4 elements across different sections, all + // sharing the same index: + // + // - RelocName has just the object's qualified name (i.e., + // Object.Pkg and Object.Name) and the CodeObj indicating what + // specific type of Object it is (Var, Func, etc). + // + // - RelocObj has the remaining public details about the object, + // relevant to go/types importers. + // + // - RelocObjExt has additional private details about the object, + // which are only relevant to cmd/compile itself. This is + // separated from RelocObj so that go/types importers are + // unaffected by internal compiler changes. + // + // - RelocObjDict has public details about the object's type + // parameters and derived type's used by the object. This is + // separated to facilitate the eventual introduction of + // shape-based stenciling. + // + // TODO(mdempsky): Re-evaluate whether RelocName still makes sense + // to keep separate from RelocObj. + w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1) wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1) wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1) wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1) - pw.globalsIdx[obj] = w.Idx // break cycles + pw.objsIdx[obj] = w.Idx // break cycles assert(wext.Idx == w.Idx) assert(wname.Idx == w.Idx) assert(wdict.Idx == w.Idx) @@ -557,6 +707,8 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { return w.Idx } +// doObj writes the RelocObj definition for obj to w, and the +// RelocObjExt definition to wext. func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj { if obj.Pkg() != w.p.curpkg { return pkgbits.ObjStub @@ -619,6 +771,8 @@ func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj { } // typExpr writes the type represented by the given expression. +// +// TODO(mdempsky): Document how this differs from exprType. func (w *writer) typExpr(expr syntax.Expr) { tv, ok := w.p.info.Types[expr] assert(ok) @@ -667,6 +821,12 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) { w.typInfo(itab.iface) } + w.Len(len(dict.methodExprs)) + for _, methodExpr := range dict.methodExprs { + w.Len(int(methodExpr.recvIdx)) + w.selectorInfo(methodExpr.methodInfo) + } + assert(len(dict.derived) == nderived) assert(len(dict.funcs) == nfuncs) } @@ -724,8 +884,8 @@ func (w *writer) qualifiedIdent(obj types2.Object) { // 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). +// objects that can only be accessed by non-qualified name, within the +// context of a particular function). func (w *writer) localIdent(obj types2.Object) { assert(!isGlobal(obj)) w.Sync(pkgbits.SyncLocalIdent) @@ -736,9 +896,19 @@ func (w *writer) localIdent(obj types2.Object) { // selector writes the name of a field or method (i.e., objects that // can only be accessed using selector expressions). func (w *writer) selector(obj types2.Object) { + w.selectorInfo(w.p.selectorIdx(obj)) +} + +func (w *writer) selectorInfo(info selectorInfo) { w.Sync(pkgbits.SyncSelector) - w.pkg(obj.Pkg()) - w.String(obj.Name()) + w.pkgRef(info.pkgIdx) + w.StringRef(info.nameIdx) +} + +func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo { + pkgIdx := pw.pkgIdx(obj.Pkg()) + nameIdx := pw.StringIdx(obj.Name()) + return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx} } // @@@ Compiler extensions @@ -787,7 +957,7 @@ func (w *writer) funcExt(obj *types2.Func) { } sig, block := obj.Type().(*types2.Signature), decl.Body - body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict) + body, closureVars := w.p.bodyIdx(sig, block, w.dict) assert(len(closureVars) == 0) w.Sync(pkgbits.SyncFuncExt) @@ -829,8 +999,11 @@ func (w *writer) pragmaFlag(p ir.PragmaFlag) { // @@@ Function bodies -func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posObj) { +// bodyIdx returns the index for the given function body (specified by +// block), adding it to the export data +func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) { w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody) + w.sig = sig w.dict = dict w.funcargs(sig) @@ -862,10 +1035,11 @@ func (w *writer) funcarg(param *types2.Var, result bool) { } } +// addLocal records the declaration of a new local variable. func (w *writer) addLocal(obj *types2.Var) { w.Sync(pkgbits.SyncAddLocal) idx := len(w.localsIdx) - if pkgbits.EnableSync { + if w.p.SyncMarkers() { w.Int(idx) } if w.localsIdx == nil { @@ -874,6 +1048,8 @@ func (w *writer) addLocal(obj *types2.Var) { w.localsIdx[obj] = idx } +// useLocal writes a reference to the given local or free variable +// into the bitstream. func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { w.Sync(pkgbits.SyncUseObjLocal) @@ -888,7 +1064,7 @@ func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { w.closureVarsIdx = make(map[*types2.Var]int) } idx = len(w.closureVars) - w.closureVars = append(w.closureVars, posObj{pos, obj}) + w.closureVars = append(w.closureVars, posVar{pos, obj}) w.closureVarsIdx[obj] = idx } w.Len(idx) @@ -911,6 +1087,7 @@ func (w *writer) closeAnotherScope() { // @@@ Statements +// stmt writes the given statement into the function body bitstream. func (w *writer) stmt(stmt syntax.Stmt) { var stmts []syntax.Stmt if stmt != nil { @@ -949,13 +1126,15 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.op(binOps[stmt.Op]) w.expr(stmt.Lhs) w.pos(stmt) - w.expr(stmt.Rhs) + + var typ types2.Type + if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr { + typ = w.p.typeOf(stmt.Lhs) + } + w.implicitConvExpr(stmt, typ, stmt.Rhs) default: - w.Code(stmtAssign) - w.pos(stmt) - w.exprList(stmt.Rhs) - w.assignList(stmt.Lhs) + w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) } case *syntax.BlockStmt: @@ -1000,17 +1179,24 @@ func (w *writer) stmt1(stmt syntax.Stmt) { case *syntax.ReturnStmt: w.Code(stmtReturn) w.pos(stmt) - w.exprList(stmt.Results) + + resultTypes := w.sig.Results() + dstType := func(i int) types2.Type { + return resultTypes.At(i).Type() + } + w.multiExpr(stmt, dstType, unpackListExpr(stmt.Results)) case *syntax.SelectStmt: w.Code(stmtSelect) w.selectStmt(stmt) case *syntax.SendStmt: + chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan) + w.Code(stmtSend) w.pos(stmt) w.expr(stmt.Chan) - w.expr(stmt.Value) + w.implicitConvExpr(stmt, chanType.Elem(), stmt.Value) case *syntax.SwitchStmt: w.Code(stmtSwitch) @@ -1023,25 +1209,36 @@ func (w *writer) assignList(expr syntax.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.assign(expr) + } +} + +func (w *writer) assign(expr syntax.Expr) { + expr = unparen(expr) + + if name, ok := expr.(*syntax.Name); ok { + if name.Value == "_" { + w.Code(assignBlank) + return } - w.Bool(false) - w.expr(expr) + if obj, ok := w.p.info.Defs[name]; ok { + obj := obj.(*types2.Var) + + w.Code(assignDef) + w.pos(obj) + w.localIdent(obj) + w.typ(obj.Type()) + + // TODO(mdempsky): Minimize locals index size by deferring + // this until the variables actually come into scope. + w.addLocal(obj) + return + } } + + w.Code(assignExpr) + w.expr(expr) } func (w *writer) declStmt(decl syntax.Decl) { @@ -1052,13 +1249,48 @@ func (w *writer) declStmt(decl syntax.Decl) { case *syntax.ConstDecl, *syntax.TypeDecl: case *syntax.VarDecl: - w.Code(stmtAssign) - w.pos(decl) - w.exprList(decl.Values) - w.assignList(namesAsExpr(decl.NameList)) + w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values) } } +// assignStmt writes out an assignment for "lhs = rhs". +func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { + lhs := unpackListExpr(lhs0) + rhs := unpackListExpr(rhs0) + + w.Code(stmtAssign) + w.pos(pos) + + // As if w.assignList(lhs0). + w.Len(len(lhs)) + for _, expr := range lhs { + w.assign(expr) + } + + dstType := func(i int) types2.Type { + dst := lhs[i] + + // Finding dstType is somewhat involved, because for VarDecl + // statements, the Names are only added to the info.{Defs,Uses} + // maps, not to info.Types. + if name, ok := unparen(dst).(*syntax.Name); ok { + if name.Value == "_" { + return nil // ok: no implicit conversion + } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { + return def.Type() + } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { + return use.Type() + } else { + w.p.fatalf(dst, "cannot find type of destination object: %v", dst) + } + } + + return w.p.typeOf(dst) + } + + w.multiExpr(pos, dstType, rhs) +} + func (w *writer) blockStmt(stmt *syntax.BlockStmt) { w.Sync(pkgbits.SyncBlockStmt) w.openScope(stmt.Pos()) @@ -1072,12 +1304,45 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { w.pos(rang) - w.expr(rang.X) w.assignList(rang.Lhs) + w.expr(rang.X) + + xtyp := w.p.typeOf(rang.X) + if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap { + w.rtype(xtyp) + } + { + lhs := unpackListExpr(rang.Lhs) + assign := func(i int, src types2.Type) { + if i >= len(lhs) { + return + } + dst := unparen(lhs[i]) + if name, ok := dst.(*syntax.Name); ok && name.Value == "_" { + return + } + + var dstType types2.Type + if rang.Def { + // For `:=` assignments, the LHS names only appear in Defs, + // not Types (as used by typeOf). + dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type() + } else { + dstType = w.p.typeOf(dst) + } + + w.convRTTI(src, dstType) + } + + keyType, valueType := w.p.rangeTypes(rang.X) + assign(0, keyType) + assign(1, valueType) + } + } else { w.pos(stmt) w.stmt(stmt.Init) - w.expr(stmt.Cond) + w.optExpr(stmt.Cond) w.stmt(stmt.Post) } @@ -1085,6 +1350,30 @@ func (w *writer) forStmt(stmt *syntax.ForStmt) { w.closeAnotherScope() } +// rangeTypes returns the types of values produced by ranging over +// expr. +func (pw *pkgWriter) rangeTypes(expr syntax.Expr) (key, value types2.Type) { + typ := pw.typeOf(expr) + switch typ := types2.CoreType(typ).(type) { + case *types2.Pointer: // must be pointer to array + return types2.Typ[types2.Int], types2.CoreType(typ.Elem()).(*types2.Array).Elem() + case *types2.Array: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Slice: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Basic: + if typ.Info()&types2.IsString != 0 { + return types2.Typ[types2.Int], runeTypeName.Type() + } + case *types2.Map: + return typ.Key(), typ.Elem() + case *types2.Chan: + return typ.Elem(), nil + } + pw.fatalf(expr, "unexpected range type: %v", typ) + panic("unreachable") +} + func (w *writer) ifStmt(stmt *syntax.IfStmt) { w.Sync(pkgbits.SyncIfStmt) w.openScope(stmt.Pos()) @@ -1123,11 +1412,9 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.pos(stmt) w.stmt(stmt.Init) - var iface types2.Type + var iface, tagType types2.Type if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { - tv, ok := w.p.info.Types[guard.X] - assert(ok && tv.IsValue()) - iface = tv.Type + iface = w.p.typeOf(guard.X) w.pos(guard) if tag := guard.Lhs; w.Bool(tag != nil) { @@ -1136,7 +1423,32 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { } w.expr(guard.X) } else { - w.expr(stmt.Tag) + tag := stmt.Tag + + if tag != nil { + tagType = w.p.typeOf(tag) + } else { + tagType = types2.Typ[types2.Bool] + } + + // Walk is going to emit comparisons between the tag value and + // each case expression, and we want these comparisons to always + // have the same type. If there are any case values that can't be + // converted to the tag value's type, then convert everything to + // `any` instead. + Outer: + for _, clause := range stmt.Body { + for _, cas := range unpackListExpr(clause.Cases) { + if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { + tagType = types2.NewInterfaceType(nil, nil) + break Outer + } + } + } + + if w.Bool(tag != nil) { + w.implicitConvExpr(tag, tagType, tag) + } } w.Len(len(stmt.Body)) @@ -1148,14 +1460,25 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.pos(clause) + cases := unpackListExpr(clause.Cases) if iface != nil { - cases := unpackListExpr(clause.Cases) w.Len(len(cases)) for _, cas := range cases { - w.exprType(iface, cas, true) + if w.Bool(isNil(w.p.info, cas)) { + continue + } + w.exprType(iface, cas) } } else { - w.exprList(clause.Cases) + // As if w.exprList(clause.Cases), + // but with implicit conversions to tagType. + + w.Sync(pkgbits.SyncExprList) + w.Sync(pkgbits.SyncExprs) + w.Len(len(cases)) + for _, cas := range cases { + w.implicitConvExpr(cas, tagType, cas) + } } if obj, ok := w.p.info.Implicits[clause]; ok { @@ -1200,30 +1523,26 @@ func (w *writer) optLabel(label *syntax.Name) { // @@@ Expressions +// expr writes the given expression into the function body bitstream. func (w *writer) expr(expr syntax.Expr) { + base.Assertf(expr != nil, "missing expression") + expr = unparen(expr) // skip parens; unneeded after typecheck obj, inst := lookupObj(w.p.info, expr) targs := inst.TypeArgs if tv, ok := w.p.info.Types[expr]; ok { - // TODO(mdempsky): Be more judicious about which types are marked as "needed". - if inst.Type != nil { - w.needType(inst.Type) - } else { - w.needType(tv.Type) - } - if tv.IsType() { - w.Code(exprType) - w.exprType(nil, expr, false) - return + w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr)) } if tv.Value != nil { w.Code(exprConst) w.pos(expr) - w.typ(tv.Type) + typ := idealType(tv) + assert(typ != nil) + w.typ(typ) w.Value(tv.Value) // TODO(mdempsky): These details are only important for backend @@ -1232,11 +1551,18 @@ func (w *writer) expr(expr syntax.Expr) { w.String(syntax.String(expr)) return } + + if _, isNil := obj.(*types2.Nil); isNil { + w.Code(exprNil) + w.pos(expr) + w.typ(tv.Type) + return + } } if obj != nil { if isGlobal(obj) { - w.Code(exprName) + w.Code(exprGlobal) w.obj(obj, targs) return } @@ -1254,13 +1580,6 @@ func (w *writer) expr(expr syntax.Expr) { 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) @@ -1274,35 +1593,60 @@ func (w *writer) expr(expr syntax.Expr) { assert(ok) w.Code(exprSelector) - w.expr(expr.X) + if w.Bool(sel.Kind() == types2.MethodExpr) { + tv, ok := w.p.info.Types[expr.X] + assert(ok) + assert(tv.IsType()) + + typInfo := w.p.typIdx(tv.Type, w.dict) + if w.Bool(typInfo.derived) { + methodInfo := w.p.selectorIdx(sel.Obj()) + idx := w.dict.methodExprIdx(typInfo, methodInfo) + w.Len(idx) + break + } + + w.typInfo(typInfo) + } else { + 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.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation + + xtyp := w.p.typeOf(expr.X) + + var keyType types2.Type + if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok { + keyType = mapType.Key() + } w.Code(exprIndex) w.expr(expr.X) w.pos(expr) - w.expr(expr.Index) + w.implicitConvExpr(expr, keyType, expr.Index) + if keyType != nil { + w.rtype(xtyp) + } case *syntax.SliceExpr: w.Code(exprSlice) w.expr(expr.X) w.pos(expr) for _, n := range &expr.Index { - w.expr(n) + w.optExpr(n) } case *syntax.AssertExpr: - tv, ok := w.p.info.Types[expr.X] - assert(ok && tv.IsValue()) + iface := w.p.typeOf(expr.X) w.Code(exprAssert) w.expr(expr.X) w.pos(expr) - w.exprType(tv.Type, expr.Type, false) + w.exprType(iface, expr.Type) + w.rtype(iface) case *syntax.Operation: if expr.Y == nil { @@ -1313,11 +1657,28 @@ func (w *writer) expr(expr syntax.Expr) { break } + var commonType types2.Type + switch expr.Op { + case syntax.Shl, syntax.Shr: + // ok: operands are allowed to have different types + default: + xtyp := w.p.typeOf(expr.X) + ytyp := w.p.typeOf(expr.Y) + switch { + case types2.AssignableTo(xtyp, ytyp): + commonType = ytyp + case types2.AssignableTo(ytyp, xtyp): + commonType = xtyp + default: + w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp) + } + } + w.Code(exprBinaryOp) w.op(binOps[expr.Op]) - w.expr(expr.X) + w.implicitConvExpr(expr, commonType, expr.X) w.pos(expr) - w.expr(expr.Y) + w.implicitConvExpr(expr, commonType, expr.Y) case *syntax.CallExpr: tv, ok := w.p.info.Types[expr.Fun] @@ -1327,12 +1688,68 @@ func (w *writer) expr(expr syntax.Expr) { assert(!expr.HasDots) w.Code(exprConvert) + w.Bool(false) // explicit w.typ(tv.Type) w.pos(expr) + w.convRTTI(w.p.typeOf(expr.ArgList[0]), tv.Type) w.expr(expr.ArgList[0]) break } + var rtype types2.Type + if tv.IsBuiltin() { + switch obj, _ := lookupObj(w.p.info, expr.Fun); obj.Name() { + case "make": + assert(len(expr.ArgList) >= 1) + assert(!expr.HasDots) + + w.Code(exprMake) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + w.exprs(expr.ArgList[1:]) + + typ := w.p.typeOf(expr) + switch coreType := types2.CoreType(typ).(type) { + default: + w.p.fatalf(expr, "unexpected core type: %v", coreType) + case *types2.Chan: + w.rtype(typ) + case *types2.Map: + w.rtype(typ) + case *types2.Slice: + w.rtype(sliceElem(typ)) + } + + return + + case "new": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprNew) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + return + + case "append": + rtype = sliceElem(w.p.typeOf(expr)) + case "copy": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())" + typ = tuple.At(0).Type() + } + rtype = sliceElem(typ) + case "delete": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())" + typ = tuple.At(0).Type() + } + rtype = typ + case "Slice": + rtype = sliceElem(w.p.typeOf(expr)) + } + } + writeFunExpr := func() { if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok { if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { @@ -1348,59 +1765,157 @@ func (w *writer) expr(expr syntax.Expr) { w.Bool(false) // not a method call (i.e., normal function call) } + sigType := types2.CoreType(tv.Type).(*types2.Signature) + paramTypes := sigType.Params() + w.Code(exprCall) writeFunExpr() w.pos(expr) - w.exprs(expr.ArgList) + + paramType := func(i int) types2.Type { + if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 { + return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() + } + return paramTypes.At(i).Type() + } + + w.multiExpr(expr, paramType, expr.ArgList) w.Bool(expr.HasDots) + if rtype != nil { + w.rtype(rtype) + } + } +} + +func sliceElem(typ types2.Type) types2.Type { + return types2.CoreType(typ).(*types2.Slice).Elem() +} + +func (w *writer) optExpr(expr syntax.Expr) { + if w.Bool(expr != nil) { + w.expr(expr) + } +} + +// multiExpr writes a sequence of expressions, where the i'th value is +// implicitly converted to dstType(i). It also handles when exprs is a +// single, multi-valued expression (e.g., the multi-valued argument in +// an f(g()) call, or the RHS operand in a comma-ok assignment). +func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) { + w.Sync(pkgbits.SyncMultiExpr) + + if len(exprs) == 1 { + expr := exprs[0] + if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok { + assert(tuple.Len() > 1) + w.Bool(true) // N:1 assignment + w.pos(pos) + w.expr(expr) + + w.Len(tuple.Len()) + for i := 0; i < tuple.Len(); i++ { + src := tuple.At(i).Type() + // TODO(mdempsky): Investigate not writing src here. I think + // the reader should be able to infer it from expr anyway. + w.typ(src) + if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) { + if src == nil || dst == nil { + w.p.fatalf(pos, "src is %v, dst is %v", src, dst) + } + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.typ(dst) + w.convRTTI(src, dst) + } + } + return + } + } + + w.Bool(false) // N:N assignment + w.Len(len(exprs)) + for i, expr := range exprs { + w.implicitConvExpr(pos, dstType(i), expr) + } +} + +// implicitConvExpr is like expr, but if dst is non-nil and different from +// expr's type, then an implicit conversion operation is inserted at +// pos. +func (w *writer) implicitConvExpr(pos poser, dst types2.Type, expr syntax.Expr) { + src := w.p.typeOf(expr) + if dst != nil && !types2.Identical(src, dst) { + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.Code(exprConvert) + w.Bool(true) // implicit + w.typ(dst) + w.pos(pos) + w.convRTTI(src, dst) + // fallthrough } + w.expr(expr) } func (w *writer) compLit(lit *syntax.CompositeLit) { - tv, ok := w.p.info.Types[lit] - assert(ok) + typ := w.p.typeOf(lit) w.Sync(pkgbits.SyncCompLit) w.pos(lit) - w.typ(tv.Type) + w.typ(typ) - typ := tv.Type if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { typ = ptr.Elem() } - str, isStruct := types2.CoreType(typ).(*types2.Struct) + var keyType, elemType types2.Type + var structType *types2.Struct + switch typ0 := typ; typ := types2.CoreType(typ).(type) { + default: + w.p.fatalf(lit, "unexpected composite literal type: %v", typ) + case *types2.Array: + elemType = typ.Elem() + case *types2.Map: + w.rtype(typ0) + keyType, elemType = typ.Key(), typ.Elem() + case *types2.Slice: + elemType = typ.Elem() + case *types2.Struct: + structType = typ + } w.Len(len(lit.ElemList)) for i, elem := range lit.ElemList { - if isStruct { + elemType := elemType + if structType != nil { if kv, ok := elem.(*syntax.KeyValueExpr); ok { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.Len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name))) + i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name)) elem = kv.Value } else { w.pos(elem) - w.Len(i) } + elemType = structType.Field(i).Type() + w.Len(i) } else { if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { // use position of expr.Key rather than of elem (which has position of ':') w.pos(kv.Key) - w.expr(kv.Key) + w.implicitConvExpr(kv.Key, keyType, kv.Key) elem = kv.Value } } w.pos(elem) - w.expr(elem) + w.implicitConvExpr(elem, elemType, elem) } } func (w *writer) funcLit(expr *syntax.FuncLit) { - tv, ok := w.p.info.Types[expr] - assert(ok) - sig := tv.Type.(*types2.Signature) + sig := w.p.typeOf(expr).(*types2.Signature) - body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict) + body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict) w.Sync(pkgbits.SyncFuncLit) w.pos(expr) @@ -1409,15 +1924,15 @@ func (w *writer) funcLit(expr *syntax.FuncLit) { w.Len(len(closureVars)) for _, cv := range closureVars { w.pos(cv.pos) - w.useLocal(cv.pos, cv.obj) + w.useLocal(cv.pos, cv.var_) } w.Reloc(pkgbits.RelocBody, body) } -type posObj struct { - pos syntax.Pos - obj *types2.Var +type posVar struct { + pos syntax.Pos + var_ *types2.Var } func (w *writer) exprList(expr syntax.Expr) { @@ -1426,10 +1941,6 @@ func (w *writer) exprList(expr syntax.Expr) { } func (w *writer) exprs(exprs []syntax.Expr) { - if len(exprs) == 0 { - assert(exprs == nil) - } - w.Sync(pkgbits.SyncExprs) w.Len(len(exprs)) for _, expr := range exprs { @@ -1437,21 +1948,41 @@ func (w *writer) exprs(exprs []syntax.Expr) { } } -func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { - base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) - - tv, ok := w.p.info.Types[typ] - assert(ok) +// rtype writes information so that the reader can construct an +// expression of type *runtime._type representing typ. +func (w *writer) rtype(typ types2.Type) { + w.Sync(pkgbits.SyncRType) + w.typNeeded(typ) +} - w.Sync(pkgbits.SyncExprType) +// typNeeded writes a reference to typ, and records that its +// *runtime._type is needed. +func (w *writer) typNeeded(typ types2.Type) { + info := w.p.typIdx(typ, w.dict) + w.typInfo(info) - if nilOK && w.Bool(tv.IsNil()) { - return + if info.derived { + w.dict.derived[info.idx].needed = true } +} + +// convRTTI writes information so that the reader can construct +// expressions for converting from src to dst. +func (w *writer) convRTTI(src, dst types2.Type) { + w.Sync(pkgbits.SyncConvRTTI) + w.typNeeded(src) + w.typNeeded(dst) +} +func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { + base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) + + tv, ok := w.p.info.Types[typ] + assert(ok) assert(tv.IsType()) info := w.p.typIdx(tv.Type, w.dict) + w.Sync(pkgbits.SyncExprType) w.pos(typ) if w.Bool(info.derived && iface != nil && !iface.Underlying().(*types2.Interface).Empty()) { @@ -1472,8 +2003,29 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { } w.typInfo(info) + if info.derived { + w.dict.derived[info.idx].needed = true + } } +func (dict *writerDict) methodExprIdx(recvInfo typeInfo, methodInfo selectorInfo) int { + assert(recvInfo.derived) + newInfo := methodExprInfo{recvIdx: recvInfo.idx, methodInfo: methodInfo} + + for idx, oldInfo := range dict.methodExprs { + if oldInfo == newInfo { + return idx + } + } + + idx := len(dict.methodExprs) + dict.methodExprs = append(dict.methodExprs, newInfo) + return idx +} + +// isInterface reports whether typ is known to be an interface type. +// If typ is a type parameter, then isInterface reports an internal +// compiler error instead. func isInterface(typ types2.Type) bool { if _, ok := typ.(*types2.TypeParam); ok { // typ is a type parameter and may be instantiated as either a @@ -1486,6 +2038,7 @@ func isInterface(typ types2.Type) bool { return ok } +// op writes an Op into the bitstream. func (w *writer) op(op ir.Op) { // TODO(mdempsky): Remove in favor of explicit codes? Would make // export data more stable against internal refactorings, but low @@ -1495,20 +2048,6 @@ func (w *writer) op(op ir.Op) { 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 @@ -1526,6 +2065,12 @@ type fileImports struct { importedEmbed, importedUnsafe bool } +// declCollector is a visitor type that collects compiler-needed +// information about declarations that types2 doesn't track. +// +// Notably, it maps declared types and functions back to their +// declaration statement, keeps track of implicit type parameters, and +// assigns unique type "generation" numbers to local defined types. type declCollector struct { pw *pkgWriter typegen *int @@ -1649,10 +2194,7 @@ func (pw *pkgWriter) collectDecls(noders []*noder) { } default: - // TODO(mdempsky): Enable after #42938 is fixed. - if false { - pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") - } + pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") } } } @@ -1750,6 +2292,12 @@ func (w *writer) pkgDecl(decl syntax.Decl) { w.Code(declVar) w.pos(decl) w.pkgObjs(decl.NameList...) + + // TODO(mdempsky): It would make sense to use multiExpr here, but + // that results in IR that confuses pkginit/initorder.go. So we + // continue using exprList, and let typecheck handle inserting any + // implicit conversions. That's okay though, because package-scope + // assignments never require dictionaries. w.exprList(decl.Values) var embeds []pragmaEmbed @@ -1836,6 +2384,27 @@ func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool { return false } +// isMultiValueExpr reports whether expr is a function call expression +// that yields multiple values. +func isMultiValueExpr(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + assert(tv.IsValue()) + if tuple, ok := tv.Type.(*types2.Tuple); ok { + assert(tuple.Len() > 1) + return true + } + return false +} + +// isNil reports whether expr is a (possibly parenthesized) reference +// to the predeclared nil value. +func isNil(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + return tv.IsNil() +} + // recvBase returns the base type for the given receiver parameter. func recvBase(recv *types2.Var) *types2.Named { typ := recv.Type() |