aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/writer.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/writer.go')
-rw-r--r--src/cmd/compile/internal/noder/writer.go851
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()