aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2022-08-04 10:32:51 -0700
committerMatthew Dempsky <mdempsky@google.com>2022-08-04 10:32:51 -0700
commita10afb15e060386615dcc0ecf2bd60ca3abbc04c (patch)
treea89e2d4e921b2b966aee9d86cbdd3d5ff9df01c7 /src/cmd/compile/internal/noder
parent85d87b9c7507628144db51bd1e7e80cc3afed128 (diff)
parentd558507db42d600e5ad82748bda0cb91df57b97d (diff)
downloadgo-a10afb15e060386615dcc0ecf2bd60ca3abbc04c.tar.xz
all: REVERSE MERGE dev.unified (d558507) into master
This commit is a REVERSE MERGE. It merges dev.unified back into its parent branch, master. This marks the end of development on dev.unified. Merge List: + 2022-08-04 d558507db4 [dev.unified] all: merge master (85d87b9) into dev.unified + 2022-08-03 c9f2150cfb [dev.unified] cmd/compile: start using runtime dictionaries + 2022-07-30 994ff78ba0 [dev.unified] go/internal: set underlying types in proper order + 2022-07-28 23554d4744 [dev.unified] all: merge master (462b78f) into dev.unified + 2022-07-28 c8d5ccf82e [dev.unified] go/internal/gcimporter: flatten imports + 2022-07-28 ac0844ec27 [dev.unified] cmd/compile: move "has init" to private metadata + 2022-07-28 f995946094 [dev.unified] cmd/compile: implement simple inline body pruning heuristic + 2022-07-28 f2851c67fd [dev.unified] cmd/compile: allow inlining to fail gracefully + 2022-07-28 831fdf1dff [dev.unified] cmd/compile: extract nil handling from exprType + 2022-07-28 92798176e7 [dev.unified] cmd/compile: write iface conversion RTTI into unified IR + 2022-07-28 9b70178d58 [dev.unified] cmd/compile: write RTTI into unified IR export data + 2022-07-25 fc72b7705d [dev.unified] cmd/compile: add method expressions to dictionaries + 2022-07-25 f48fa643f1 [dev.unified] cmd/compile: remove obsolete RTTI wiring + 2022-07-22 131f981df0 [dev.unified] cmd/compile: make Unified IR always writes concrete type for const exprs + 2022-07-20 ae43bdc3e3 Merge "[dev.unified] all: merge master (8e1e64c) into dev.unified" into dev.unified + 2022-07-19 7a8ba83b72 [dev.unified] cmd/compile/internal/reflectdata: remove hasRType's `required` param + 2022-07-19 64cd6faa13 [dev.unified] cmd/compile/internal/noder: simplify mixed tag/case RTTI wiring + 2022-07-19 a4c5198a3c [dev.unified] cmd/compile/internal/noder: better switch statements + 2022-07-19 318027044a [dev.unified] cmd/compile/internal/noder: explicit nil handling + 2022-07-19 e971b6a9be [dev.unified] test: add switch test case for tricky nil handling + 2022-07-19 878439cfe5 [dev.unified] cmd/compile/internal/noder: preserve RTTI for select statements + 2022-07-19 e376746e54 [dev.unified] cmd/compile/internal/noder: wire RTTI for implicit conversions + 2022-07-19 c846fd8e13 [dev.unified] cmd/compile/internal/noder: implicit conversions for binary exprs + 2022-07-19 ebd34e3e45 [dev.unified] test: relax panic message expectations + 2022-07-19 76a82f09d6 [dev.unified] cmd/compile/internal/noder: prefer *At functions + 2022-07-19 de649a2a98 [dev.unified] all: merge master (8e1e64c) into dev.unified + 2022-07-19 055a5e55fa [dev.unified] test: change Unicode file/package name to use characters not translated by macOS. + 2022-07-18 2cf632cd57 [dev.unified] cmd/compile/internal/reflectdata: prefer ITabAddrAt in ConvIfaceTypeWord + 2022-07-12 9371a65584 internal/pkgbits: change EnableSync into a dynamic knob + 2022-07-01 d667be8831 [dev.unified] cmd/compile/internal/walk: RType fields for range assignments + 2022-06-30 1b838e9556 [dev.unified] all: merge master (993c387) into dev.unified + 2022-06-30 0a503cf43a [dev.unified] cmd/compile: refactor `range` desugaring + 2022-06-30 3635b07d16 [dev.unified] cmd/compile/internal/noder: implicit conversions for multi-valued expressions + 2022-06-30 e7219cc093 [dev.unified] cmd/compile/internal/noder: refactor N:1 expression handling + 2022-06-30 2f3ef73e18 [dev.unified] test: tweak nilcheck test + 2022-06-30 95d7ce9ab1 [dev.unified] test: break escape_iface.go into unified/nounified variants + 2022-06-30 f751319a0b [dev.unified] test: relax live_regabi.go + 2022-06-30 e3cdc981c8 [dev.unified] cmd/compile/internal/walk: fix typo in debug print + 2022-06-29 2280d897d6 [dev.unified] test: add regress test for generic select statements + 2022-06-27 4b78ece3d7 [dev.unified] cmd/compile: drop package height from Unified IR importer + 2022-06-27 398d46d538 [dev.unified] cmd/compile/internal/types2: remove package height + 2022-06-24 e7100adbca [dev.unified] all: merge master (5a1c5b8) into dev.unified + 2022-06-23 09a838ad86 [dev.unified] cmd/compile: rename haveRType and implicitExpr + 2022-06-23 421e9e9db2 [dev.unified] cmd/compile: implicit conversions for return statements + 2022-06-23 a3fea7796a [dev.unified] cmd/compile/internal/noder: implicit conversions for writer.assignStmt + 2022-06-23 82a958a661 [dev.unified] cmd/compile/internal/noder: refactor stmtAssign generation + 2022-06-23 711dacd8cf [dev.unified] cmd/compile/internal/noder: implicit conversion of call arguments + 2022-06-23 46b01ec667 [dev.unified] cmd/compile/internal/noder: remove needType logic + 2022-06-23 a3e474f867 [dev.unified] cmd/compile/internal/noder: implicit conversions for complits + 2022-06-23 5f5422a2dd [dev.unified] cmd/compile/internal/noder: start writing implicit conversions + 2022-06-23 9cb784ac69 [dev.unified] cmd/compile/internal/noder: add pkgWriter.typeOf helper + 2022-06-23 c70e93ff3d [dev.unified] cmd/compile/internal/typecheck: replace unreachable code with assert + 2022-06-23 20e1d5ac8c [dev.unified] cmd/compile: special case f(g()) calls in Unified IR + 2022-06-23 61ae2b734c [dev.unified] cmd/compile: plumb rtype through OSWITCH/OCASE clauses + 2022-06-23 3d432b6c4b [dev.unified] cmd/compile: plumb rtype through for OMAPLIT + 2022-06-23 7368647ac6 [dev.unified] cmd/compile: start setting RType fields for Unified IR + 2022-06-23 5960f4ec10 [dev.unified] cmd/compile: add RType fields + 2022-06-21 5e0258c700 [dev.unified] cmd/compile: avoid reflectType in ssagen + 2022-06-21 93833cd5d8 [dev.unified] cmd/compile: extract rtype code from walk + 2022-06-21 f70775ff22 [dev.unified] cmd/compile: refactor reflectdata.{TypePtr,ITabAddr} + 2022-06-21 fc5dad6646 [dev.unified] cmd/compile/internal/walk: minor prep refactoring + 2022-06-16 1f4e8afafe [dev.unified] all: merge master (635b124) into dev.unified + 2022-06-15 8a9485c023 [dev.unified] test: extract different inline test between unified and non-unified + 2022-06-14 394ea70cc9 [dev.unified] cmd/compile: more Unified IR docs and review + 2022-06-10 f73ad3d24d [dev.unified] test: add regress tests for #53276 and #53328 + 2022-06-09 8ef8b60e18 [dev.unified] cmd/compile/internal/noder: stop handling type expressions as expressions + 2022-06-09 1a6c96bb9b [dev.unified] test: relax issue7921.go diagnostic message + 2022-06-09 c50c6bbc03 [dev.unified] cmd/compile: set base.Pos when process assignDef in Unified IR + 2022-06-09 d6df08693c [dev.unified] cmd/compile: fix unified IR don't report type size too large error + 2022-06-08 e7ef58542c [dev.unified] cmd/compile: restore Unified IR linkname pragma diagnostic + 2022-06-07 9e5c968021 [dev.unified] cmd/compile: visit LHS before RHS/X in assign/for statement + 2022-06-06 46ddf0873e [dev.unified] cmd/compile: export/import implicit attribute for conversion exprs + 2022-06-06 a8780f94c3 [dev.unified] cmd/compile: fix missing method value wrapper in unified IR + 2022-06-06 3a1f1e1575 [dev.unified] cmd/compile: remove package height + 2022-06-06 df7cb59de4 [dev.unified] cmd/compile: only sort symbols by name and package path + 2022-06-06 b39ac80871 [dev.unified] cmd/compile/internal/noder: push exprBlank up into assignment handling + 2022-06-06 55fc07e164 [dev.unified] cmd/compile/internal/noder: add optExpr for optional expressions + 2022-06-06 6c33f1d52e [dev.unified] cmd/compile/internal/noder: rename exprName to exprGlobal + 2022-06-06 4d28fcabb4 [dev.unified] all: update codereview.cfg for dev.unified branch Change-Id: I604d057735e8a365621c91c206f9e46eabb4679b
Diffstat (limited to 'src/cmd/compile/internal/noder')
-rw-r--r--src/cmd/compile/internal/noder/codes.go28
-rw-r--r--src/cmd/compile/internal/noder/expr.go29
-rw-r--r--src/cmd/compile/internal/noder/helpers.go35
-rw-r--r--src/cmd/compile/internal/noder/import.go2
-rw-r--r--src/cmd/compile/internal/noder/irgen.go1
-rw-r--r--src/cmd/compile/internal/noder/linker.go107
-rw-r--r--src/cmd/compile/internal/noder/quirks.go10
-rw-r--r--src/cmd/compile/internal/noder/reader.go804
-rw-r--r--src/cmd/compile/internal/noder/unified.go110
-rw-r--r--src/cmd/compile/internal/noder/writer.go847
10 files changed, 1606 insertions, 367 deletions
diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go
index 8f54a07ca4..1a60ea39bb 100644
--- a/src/cmd/compile/internal/noder/codes.go
+++ b/src/cmd/compile/internal/noder/codes.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.
@@ -8,6 +6,7 @@ package noder
import "internal/pkgbits"
+// A codeStmt distinguishes among statement encodings.
type codeStmt int
func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 }
@@ -31,6 +30,7 @@ const (
stmtSelect
)
+// A codeExpr distinguishes among expression encodings.
type codeExpr int
func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr }
@@ -38,12 +38,9 @@ func (c codeExpr) Value() int { return int(c) }
// TODO(mdempsky): Split expr into addr, for lvalues.
const (
- exprNone codeExpr = iota
- exprConst
- exprType // type expression
- exprLocal // local variable
- exprName // global variable or function
- exprBlank
+ exprConst codeExpr = iota
+ exprLocal // local variable
+ exprGlobal // global variable or function
exprCompLit
exprFuncLit
exprSelector
@@ -54,8 +51,23 @@ const (
exprBinaryOp
exprCall
exprConvert
+ exprNew
+ exprMake
+ exprNil
+)
+
+type codeAssign int
+
+func (c codeAssign) Marker() pkgbits.SyncMarker { return pkgbits.SyncAssign }
+func (c codeAssign) Value() int { return int(c) }
+
+const (
+ assignBlank codeAssign = iota
+ assignDef
+ assignExpr
)
+// A codeDecl distinguishes among declaration encodings.
type codeDecl int
func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl }
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index a1160d42c4..54b07c39f4 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -6,7 +6,6 @@ package noder
import (
"fmt"
- "go/constant"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -53,31 +52,9 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
base.Assert(g.exprStmtOK)
- // The gc backend expects all expressions to have a concrete type, and
- // types2 mostly satisfies this expectation already. But there are a few
- // cases where the Go spec doesn't require converting to concrete type,
- // and so types2 leaves them untyped. So we need to fix those up here.
- typ := tv.Type
- if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
- switch basic.Kind() {
- case types2.UntypedNil:
- // ok; can appear in type switch case clauses
- // TODO(mdempsky): Handle as part of type switches instead?
- case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
- // Untyped rhs of non-constant shift, e.g. x << 1.0.
- // If we have a constant value, it must be an int >= 0.
- if tv.Value != nil {
- s := constant.ToInt(tv.Value)
- assert(s.Kind() == constant.Int && constant.Sign(s) >= 0)
- }
- typ = types2.Typ[types2.Uint]
- case types2.UntypedBool:
- typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
- case types2.UntypedString:
- typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
- default:
- base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic)
- }
+ typ := idealType(tv)
+ if typ == nil {
+ base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", tv.Type)
}
// Constant expression.
diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go
index 33acd6051a..40f80ab528 100644
--- a/src/cmd/compile/internal/noder/helpers.go
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -11,6 +11,7 @@ import (
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
"cmd/internal/src"
)
@@ -39,10 +40,6 @@ func typed(typ *types.Type, n ir.Node) ir.Node {
// Values
-func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
- return typed(typ, ir.NewBasicLit(pos, val))
-}
-
func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node {
orig := ir.NewRawOrigExpr(pos, op, raw)
return ir.NewConstExpr(val, typed(typ, orig))
@@ -224,3 +221,33 @@ func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
}
return ir.NewAssignOpStmt(pos, op, x, bl)
}
+
+func idealType(tv types2.TypeAndValue) types2.Type {
+ // The gc backend expects all expressions to have a concrete type, and
+ // types2 mostly satisfies this expectation already. But there are a few
+ // cases where the Go spec doesn't require converting to concrete type,
+ // and so types2 leaves them untyped. So we need to fix those up here.
+ typ := tv.Type
+ if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
+ switch basic.Kind() {
+ case types2.UntypedNil:
+ // ok; can appear in type switch case clauses
+ // TODO(mdempsky): Handle as part of type switches instead?
+ case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
+ // Untyped rhs of non-constant shift, e.g. x << 1.0.
+ // If we have a constant value, it must be an int >= 0.
+ if tv.Value != nil {
+ s := constant.ToInt(tv.Value)
+ assert(s.Kind() == constant.Int && constant.Sign(s) >= 0)
+ }
+ typ = types2.Typ[types2.Uint]
+ case types2.UntypedBool:
+ typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
+ case types2.UntypedString:
+ typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
+ default:
+ return nil
+ }
+ }
+ return typ
+}
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index 2cef9f75e8..49b8fd142a 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -241,7 +241,7 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag
pr := pkgbits.NewPkgDecoder(pkg1.Path, data)
// Read package descriptors for both types2 and compiler backend.
- readPackage(newPkgReader(pr), pkg1)
+ readPackage(newPkgReader(pr), pkg1, false)
pkg2 = importer.ReadPackage(env, packages, pr)
case 'i':
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index e45a204867..ad937eac62 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -219,7 +219,6 @@ type typeDelayInfo struct {
func (g *irgen) generate(noders []*noder) {
types.LocalPkg.Name = g.self.Name()
- types.LocalPkg.Height = g.self.Height()
typecheck.TypecheckAllowed = true
// Prevent size calculations until we set the underlying type
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go
index a58b9b930c..0f39fdec05 100644
--- a/src/cmd/compile/internal/noder/linker.go
+++ b/src/cmd/compile/internal/noder/linker.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.
@@ -34,13 +32,20 @@ import (
// low-level linking details can be moved there, but the logic for
// handling extension data needs to stay in the compiler.
+// A linker combines a package's stub export data with any referenced
+// elements from imported packages into a single, self-contained
+// export data file.
type linker struct {
pw pkgbits.PkgEncoder
- pkgs map[string]pkgbits.Index
- decls map[*types.Sym]pkgbits.Index
+ pkgs map[string]pkgbits.Index
+ decls map[*types.Sym]pkgbits.Index
+ bodies map[*types.Sym]pkgbits.Index
}
+// relocAll ensures that all elements specified by pr and relocs are
+// copied into the output export data file, and returns the
+// corresponding indices in the output.
func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt {
res := make([]pkgbits.RelocEnt, len(relocs))
for i, rent := range relocs {
@@ -50,6 +55,8 @@ func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.Re
return res
}
+// relocIdx ensures a single element is copied into the output export
+// data file, and returns the corresponding index in the output.
func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index {
assert(pr != nil)
@@ -85,10 +92,19 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index)
return newidx
}
+// relocString copies the specified string from pr into the output
+// export data file, deduplicating it against other strings.
func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
return l.pw.StringIdx(pr.StringIdx(idx))
}
+// relocPkg copies the specified package from pr into the output
+// export data file, rewriting its import path to match how it was
+// imported.
+//
+// TODO(mdempsky): Since CL 391014, we already have the compilation
+// unit's import path, so there should be no need to rewrite packages
+// anymore.
func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
path := pr.PeekPkgPath(idx)
@@ -114,6 +130,9 @@ func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
return w.Flush()
}
+// relocObj copies the specified object from pr into the output export
+// data file, rewriting its compiler-private extension data (e.g.,
+// adding inlining cost and escape analysis results for functions).
func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
path, name, tag := pr.PeekObj(idx)
sym := types.NewPkg(path, "").Lookup(name)
@@ -152,21 +171,12 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
l.relocCommon(pr, &wname, pkgbits.RelocName, idx)
l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx)
- var obj *ir.Name
- if sym.Pkg == types.LocalPkg {
- var ok bool
- obj, ok = sym.Def.(*ir.Name)
-
- // Generic types and functions and declared constraint types won't
- // have definitions.
- // For now, just generically copy their extension data.
- // TODO(mdempsky): Restore assertion.
- if !ok && false {
- base.Fatalf("missing definition for %v", sym)
- }
- }
+ // Generic types and functions won't have definitions, and imported
+ // objects may not either.
+ obj, _ := sym.Def.(*ir.Name)
+ local := sym.Pkg == types.LocalPkg
- if obj != nil {
+ if local && obj != nil {
wext.Sync(pkgbits.SyncObject1)
switch tag {
case pkgbits.ObjFunc:
@@ -181,9 +191,66 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx)
}
+ // Check if we need to export the inline bodies for functions and
+ // methods.
+ if obj != nil {
+ if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC {
+ l.exportBody(obj, local)
+ }
+
+ if obj.Op() == ir.OTYPE {
+ if typ := obj.Type(); !typ.IsInterface() {
+ for _, method := range typ.Methods().Slice() {
+ l.exportBody(method.Nname.(*ir.Name), local)
+ }
+ }
+ }
+ }
+
return w.Idx
}
+// exportBody exports the given function or method's body, if
+// appropriate. local indicates whether it's a local function or
+// method available on a locally declared type. (Due to cross-package
+// type aliases, a method may be imported, but still available on a
+// locally declared type.)
+func (l *linker) exportBody(obj *ir.Name, local bool) {
+ assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC)
+
+ fn := obj.Func
+ if fn.Inl == nil {
+ return // not inlinable anyway
+ }
+
+ // As a simple heuristic, if the function was declared in this
+ // package or we inlined it somewhere in this package, then we'll
+ // (re)export the function body. This isn't perfect, but seems
+ // reasonable in practice. In particular, it has the nice property
+ // that in the worst case, adding a blank import ensures the
+ // function body is available for inlining.
+ //
+ // TODO(mdempsky): Reimplement the reachable method crawling logic
+ // from typecheck/crawler.go.
+ exportBody := local || fn.Inl.Body != nil
+ if !exportBody {
+ return
+ }
+
+ sym := obj.Sym()
+ if _, ok := l.bodies[sym]; ok {
+ // Due to type aliases, we might visit methods multiple times.
+ base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj)
+ return
+ }
+
+ pri, ok := bodyReaderFor(fn)
+ assert(ok)
+ l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)
+}
+
+// relocCommon copies the specified element from pr into w,
+// recursively relocating any referenced elements as well.
func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) {
r := pr.NewDecoderRaw(k, idx)
w.Relocs = l.relocAll(pr, r.Relocs)
@@ -220,10 +287,6 @@ func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) {
if inl := name.Func.Inl; w.Bool(inl != nil) {
w.Len(int(inl.Cost))
w.Bool(inl.CanDelayResults)
-
- pri, ok := bodyReader[name.Func]
- assert(ok)
- w.Reloc(pkgbits.RelocBody, l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx))
}
w.Sync(pkgbits.SyncEOF)
diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go
index c4cb9b9a2c..a22577f965 100644
--- a/src/cmd/compile/internal/noder/quirks.go
+++ b/src/cmd/compile/internal/noder/quirks.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.
@@ -12,12 +10,12 @@ import (
"cmd/compile/internal/syntax"
)
-// This file defines helper functions useful for satisfying toolstash
-// -cmp when compared against the legacy frontend behavior, but can be
-// removed after that's no longer a concern.
-
// typeExprEndPos returns the position that noder would leave base.Pos
// after parsing the given type expression.
+//
+// Deprecated: This function exists to emulate position semantics from
+// Go 1.17, necessary for compatibility with the backend DWARF
+// generation logic that assigns variables to their appropriate scope.
func typeExprEndPos(expr0 syntax.Expr) syntax.Pos {
for {
switch expr := expr0.(type) {
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 296cdd7d54..d02d05bc5d 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.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.
@@ -19,6 +17,7 @@ import (
"cmd/compile/internal/dwarfgen"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
@@ -26,15 +25,25 @@ import (
"cmd/internal/src"
)
+// This file implements cmd/compile backend's reader for the Unified
+// IR export data.
+
+// A pkgReader reads Unified IR export data.
type pkgReader struct {
pkgbits.PkgDecoder
+ // Indices for encoded things; lazily populated as needed.
+ //
+ // Note: Objects (i.e., ir.Names) are lazily instantiated by
+ // populating their types.Sym.Def; see objReader below.
+
posBases []*src.PosBase
pkgs []*types.Pkg
typs []*types.Type
- // offset for rewriting the given index into the output,
- // but bitwise inverted so we can detect if we're missing the entry or not.
+ // offset for rewriting the given (absolute!) index into the output,
+ // but bitwise inverted so we can detect if we're missing the entry
+ // or not.
newindex []pkgbits.Index
}
@@ -50,15 +59,19 @@ func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
}
}
+// A pkgReaderIndex compactly identifies an index (and its
+// corresponding dictionary) within a package's export data.
type pkgReaderIndex struct {
- pr *pkgReader
- idx pkgbits.Index
- dict *readerDict
+ pr *pkgReader
+ idx pkgbits.Index
+ dict *readerDict
+ shapedFn *ir.Func
}
func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader {
r := pri.pr.newReader(k, pri.idx, marker)
r.dict = pri.dict
+ r.shapedFn = pri.shapedFn
return r
}
@@ -69,6 +82,7 @@ func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pk
}
}
+// A writer provides APIs for reading an individual element.
type reader struct {
pkgbits.Decoder
@@ -87,6 +101,12 @@ type reader struct {
funarghack bool
+ // shapedFn is the shape-typed version of curfn, if any.
+ shapedFn *ir.Func
+
+ // dictParam is the .dict param, if any.
+ dictParam *ir.Name
+
// scopeVars is a stack tracking the number of variables declared in
// the current function at the moment each open scope was opened.
scopeVars []int
@@ -108,7 +128,13 @@ type reader struct {
// Label to return to.
retlabel *types.Sym
- inlvars, retvars ir.Nodes
+ // inlvars is the list of variables that the inlinee's arguments are
+ // assigned to, one for each receiver and normal parameter, in order.
+ inlvars ir.Nodes
+
+ // retvars is the list of variables that the inlinee's results are
+ // assigned to, one for each result parameter, in order.
+ retvars ir.Nodes
}
type readerDict struct {
@@ -143,6 +169,8 @@ type readerDict struct {
funcsObj []ir.Node
itabs []itabInfo2
+
+ methodExprs []ir.Node
}
type itabInfo2 struct {
@@ -162,6 +190,7 @@ func setValue(name *ir.Name, val constant.Value) {
// @@@ Positions
+// pos reads a position from the bitstream.
func (r *reader) pos() src.XPos {
return base.Ctxt.PosTable.XPos(r.pos0())
}
@@ -178,10 +207,13 @@ func (r *reader) pos0() src.Pos {
return src.MakePos(posBase, line, col)
}
+// posBase reads a position base from the bitstream.
func (r *reader) posBase() *src.PosBase {
return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
}
+// posBaseIdx returns the specified position base, reading it first if
+// needed.
func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
if b := pr.posBases[idx]; b != nil {
return b
@@ -222,6 +254,7 @@ func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
return b
}
+// TODO(mdempsky): Document this.
func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
if r.inlCall == nil {
return oldBase
@@ -236,36 +269,23 @@ func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
return newBase
}
+// TODO(mdempsky): Document this.
func (r *reader) updatePos(xpos src.XPos) src.XPos {
pos := base.Ctxt.PosTable.Pos(xpos)
pos.SetBase(r.inlPosBase(pos.Base()))
return base.Ctxt.PosTable.XPos(pos)
}
-func (r *reader) origPos(xpos src.XPos) src.XPos {
- if r.inlCall == nil {
- return xpos
- }
-
- pos := base.Ctxt.PosTable.Pos(xpos)
- for old, new := range r.inlPosBases {
- if pos.Base() == new {
- pos.SetBase(old)
- return base.Ctxt.PosTable.XPos(pos)
- }
- }
-
- base.FatalfAt(xpos, "pos base missing from inlPosBases")
- panic("unreachable")
-}
-
// @@@ Packages
+// pkg reads a package reference from the bitstream.
func (r *reader) pkg() *types.Pkg {
r.Sync(pkgbits.SyncPkg)
return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
}
+// pkgIdx returns the specified package from the export data, reading
+// it first if needed.
func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
if pkg := pr.pkgs[idx]; pkg != nil {
return pkg
@@ -276,6 +296,7 @@ func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
return pkg
}
+// doPkg reads a package definition from the bitstream.
func (r *reader) doPkg() *types.Pkg {
path := r.String()
switch path {
@@ -288,7 +309,6 @@ func (r *reader) doPkg() *types.Pkg {
}
name := r.String()
- height := r.Len()
pkg := types.NewPkg(path, "")
@@ -298,12 +318,6 @@ func (r *reader) doPkg() *types.Pkg {
base.Assertf(pkg.Name == name, "package %q has name %q, but want %q", pkg.Path, pkg.Name, name)
}
- if pkg.Height == 0 {
- pkg.Height = height
- } else {
- base.Assertf(pkg.Height == height, "package %q has height %v, but want %v", pkg.Path, pkg.Height, height)
- }
-
return pkg
}
@@ -536,8 +550,12 @@ func (r *reader) param() (*types.Pkg, *types.Field) {
// @@@ Objects
+// objReader maps qualified identifiers (represented as *types.Sym) to
+// a pkgReader and corresponding index that can be used for reading
+// that object's definition.
var objReader = map[*types.Sym]pkgReaderIndex{}
+// obj reads an instantiated object reference from the bitstream.
func (r *reader) obj() ir.Node {
r.Sync(pkgbits.SyncObject)
@@ -573,6 +591,8 @@ func (r *reader) obj() ir.Node {
return r.p.objIdx(idx, implicits, explicits)
}
+// objIdx returns the specified object from the bitstream,
+// instantiated with the given type arguments, if any.
func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type) ir.Node {
rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
_, sym := rname.qualifiedIdent()
@@ -605,6 +625,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
do := func(op ir.Op, hasTParams bool) *ir.Name {
pos := r.pos()
+ setBasePos(pos)
if hasTParams {
r.typeParamNames()
}
@@ -712,6 +733,7 @@ func (r *reader) mangle(sym *types.Sym) *types.Sym {
return sym.Pkg.Lookup(buf.String())
}
+// objDictIdx reads and returns the specified object dictionary.
func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type) *readerDict {
r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
@@ -730,12 +752,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
// For stenciling, we can just skip over the type parameters.
for range dict.targs[dict.implicits:] {
// Skip past bounds without actually evaluating them.
- r.Sync(pkgbits.SyncType)
- if r.Bool() {
- r.Len()
- } else {
- r.Reloc(pkgbits.RelocType)
- }
+ r.typInfo()
}
dict.derived = make([]derivedInfo, r.Len())
@@ -771,6 +788,14 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
dict.itabs[i] = itabInfo2{typ: typ, lsym: lsym}
}
+ dict.methodExprs = make([]ir.Node, r.Len())
+ for i := range dict.methodExprs {
+ recv := pr.typIdx(typeInfo{idx: pkgbits.Index(r.Len()), derived: true}, &dict, true)
+ _, sym := r.selector()
+
+ dict.methodExprs[i] = typecheck.Expr(ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, ir.TypeNode(recv), sym))
+ }
+
return &dict
}
@@ -791,9 +816,7 @@ func (r *reader) method(rext *reader) *types.Field {
_, recv := r.param()
typ := r.signature(pkg, recv)
- fnsym := sym
- fnsym = ir.MethodSym(recv.Type, fnsym)
- name := ir.NewNameAt(pos, fnsym)
+ name := ir.NewNameAt(pos, ir.MethodSym(recv.Type, sym))
setType(name, typ)
name.Func = ir.NewFunc(r.pos())
@@ -882,6 +905,8 @@ func (r *reader) funcExt(name *ir.Name) {
typecheck.Func(fn)
if r.Bool() {
+ assert(name.Defn == nil)
+
fn.ABI = obj.ABI(r.Uint64())
// Escape analysis.
@@ -896,7 +921,6 @@ func (r *reader) funcExt(name *ir.Name) {
Cost: int32(r.Len()),
CanDelayResults: r.Bool(),
}
- r.addBody(name.Func)
}
} else {
r.addBody(name.Func)
@@ -952,24 +976,71 @@ func (r *reader) pragmaFlag() ir.PragmaFlag {
// @@@ Function bodies
-// bodyReader tracks where the serialized IR for a function's body can
-// be found.
+// bodyReader tracks where the serialized IR for a local or imported,
+// generic function's body can be found.
var bodyReader = map[*ir.Func]pkgReaderIndex{}
+// importBodyReader tracks where the serialized IR for an imported,
+// static (i.e., non-generic) function body can be read.
+var importBodyReader = map[*types.Sym]pkgReaderIndex{}
+
+// bodyReaderFor returns the pkgReaderIndex for reading fn's
+// serialized IR, and whether one was found.
+func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) {
+ if fn.Nname.Defn != nil {
+ pri, ok = bodyReader[fn]
+ base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available
+ } else {
+ pri, ok = importBodyReader[fn.Sym()]
+ }
+ return
+}
+
// todoBodies holds the list of function bodies that still need to be
// constructed.
var todoBodies []*ir.Func
+// addBody reads a function body reference from the element bitstream,
+// and associates it with fn.
func (r *reader) addBody(fn *ir.Func) {
- pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict}
- bodyReader[fn] = pri
+ // addBody should only be called for local functions or imported
+ // generic functions; see comment in funcExt.
+ assert(fn.Nname.Defn != nil)
- if fn.Nname.Defn == nil {
- // Don't read in function body for imported functions.
- // See comment in funcExt.
- return
+ idx := r.Reloc(pkgbits.RelocBody)
+
+ var shapedFn *ir.Func
+ if r.hasTypeParams() && fn.OClosure == nil {
+ name := fn.Nname
+ sym := name.Sym()
+
+ shapedSym := sym.Pkg.Lookup(sym.Name + "-shaped")
+
+ // TODO(mdempsky): Once we actually start shaping functions, we'll
+ // need to deduplicate them.
+ shaped := ir.NewDeclNameAt(name.Pos(), ir.ONAME, shapedSym)
+ setType(shaped, shapeSig(fn, r.dict)) // TODO(mdempsky): Use shape types.
+
+ shapedFn = ir.NewFunc(fn.Pos())
+ shaped.Func = shapedFn
+ shapedFn.Nname = shaped
+ shapedFn.SetDupok(true)
+
+ shaped.Class = 0 // so MarkFunc doesn't complain
+ ir.MarkFunc(shaped)
+
+ shaped.Defn = shapedFn
+
+ shapedFn.Pragma = fn.Pragma // TODO(mdempsky): How does stencil.go handle pragmas?
+ typecheck.Func(shapedFn)
+
+ bodyReader[shapedFn] = pkgReaderIndex{r.p, idx, r.dict, nil}
+ todoBodies = append(todoBodies, shapedFn)
}
+ pri := pkgReaderIndex{r.p, idx, r.dict, shapedFn}
+ bodyReader[fn] = pri
+
if r.curfn == nil {
todoBodies = append(todoBodies, fn)
return
@@ -983,9 +1054,14 @@ func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
r.funcBody(fn)
}
+// funcBody reads a function body definition from the element
+// bitstream, and populates fn with it.
func (r *reader) funcBody(fn *ir.Func) {
r.curfn = fn
r.closureVars = fn.ClosureVars
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
ir.WithFunc(fn, func() {
r.funcargs(fn)
@@ -994,6 +1070,11 @@ func (r *reader) funcBody(fn *ir.Func) {
return
}
+ if r.shapedFn != nil {
+ r.callShaped(fn.Pos())
+ return
+ }
+
body := r.stmts()
if body == nil {
body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))}
@@ -1005,6 +1086,139 @@ func (r *reader) funcBody(fn *ir.Func) {
r.marker.WriteTo(fn)
}
+// callShaped emits a tail call to r.shapedFn, passing along the
+// arguments to the current function.
+func (r *reader) callShaped(pos src.XPos) {
+ sig := r.curfn.Nname.Type()
+
+ var args ir.Nodes
+
+ // First argument is a pointer to the -dict global variable.
+ args.Append(r.dictPtr())
+
+ // Collect the arguments to the current function, so we can pass
+ // them along to the shaped function. (This is unfortunately quite
+ // hairy.)
+ for _, params := range &types.RecvsParams {
+ for _, param := range params(sig).FieldSlice() {
+ var arg ir.Node
+ if param.Nname != nil {
+ name := param.Nname.(*ir.Name)
+ if !ir.IsBlank(name) {
+ if r.inlCall != nil {
+ // During inlining, we want the respective inlvar where we
+ // assigned the callee's arguments.
+ arg = r.inlvars[len(args)-1]
+ } else {
+ // Otherwise, we can use the parameter itself directly.
+ base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn)
+ arg = name
+ }
+ }
+ }
+
+ // For anonymous and blank parameters, we don't have an *ir.Name
+ // to use as the argument. However, since we know the shaped
+ // function won't use the value either, we can just pass the
+ // zero value. (Also unfortunately, we don't have an easy
+ // zero-value IR node; so we use a default-initialized temporary
+ // variable.)
+ if arg == nil {
+ tmp := typecheck.TempAt(pos, r.curfn, param.Type)
+ r.curfn.Body.Append(
+ typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)),
+ typecheck.Stmt(ir.NewAssignStmt(pos, tmp, nil)),
+ )
+ arg = tmp
+ }
+
+ args.Append(arg)
+ }
+ }
+
+ // Mark the function as a wrapper so it doesn't show up in stack
+ // traces.
+ r.curfn.SetWrapper(true)
+
+ call := typecheck.Call(pos, r.shapedFn.Nname, args, sig.IsVariadic()).(*ir.CallExpr)
+
+ var stmt ir.Node
+ if sig.NumResults() != 0 {
+ stmt = typecheck.Stmt(ir.NewReturnStmt(pos, []ir.Node{call}))
+ } else {
+ stmt = call
+ }
+ r.curfn.Body.Append(stmt)
+}
+
+// dictPtr returns a pointer to the runtime dictionary variable needed
+// for the current function to call its shaped variant.
+func (r *reader) dictPtr() ir.Node {
+ var fn *ir.Func
+ if r.inlCall != nil {
+ // During inlining, r.curfn is named after the caller (not the
+ // callee), because it's relevant to closure naming, sigh.
+ fn = r.inlFunc
+ } else {
+ fn = r.curfn
+ }
+
+ var baseSym *types.Sym
+ if recv := fn.Nname.Type().Recv(); recv != nil {
+ // All methods of a given instantiated receiver type share the
+ // same dictionary.
+ baseSym = deref(recv.Type).Sym()
+ } else {
+ baseSym = fn.Nname.Sym()
+ }
+
+ sym := baseSym.Pkg.Lookup(baseSym.Name + "-dict")
+
+ if sym.Def == nil {
+ dict := ir.NewNameAt(r.curfn.Pos(), sym)
+ dict.Class = ir.PEXTERN
+
+ lsym := dict.Linksym()
+ ot := 0
+
+ for idx, info := range r.dict.derived {
+ if info.needed {
+ typ := r.p.typIdx(typeInfo{idx: pkgbits.Index(idx), derived: true}, r.dict, false)
+ rtype := reflectdata.TypeLinksym(typ)
+ ot = objw.SymPtr(lsym, ot, rtype, 0)
+ } else {
+ // TODO(mdempsky): Compact unused runtime dictionary space.
+ ot = objw.Uintptr(lsym, ot, 0)
+ }
+ }
+
+ // TODO(mdempsky): Write out more dictionary information.
+
+ objw.Global(lsym, int32(ot), obj.DUPOK|obj.RODATA)
+
+ dict.SetType(r.dict.varType())
+ dict.SetTypecheck(1)
+
+ sym.Def = dict
+ }
+
+ return typecheck.Expr(ir.NewAddrExpr(r.curfn.Pos(), sym.Def.(*ir.Name)))
+}
+
+// numWords returns the number of words that dict's runtime dictionary
+// variable requires.
+func (dict *readerDict) numWords() int64 {
+ var num int
+ num += len(dict.derivedTypes)
+ // TODO(mdempsky): Add space for more dictionary information.
+ return int64(num)
+}
+
+// varType returns the type of dict's runtime dictionary variable.
+func (dict *readerDict) varType() *types.Type {
+ return types.NewArray(types.Types[types.TUINTPTR], dict.numWords())
+}
+
func (r *reader) funcargs(fn *ir.Func) {
sig := fn.Nname.Type()
@@ -1062,16 +1276,20 @@ func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
- r.Sync(pkgbits.SyncAddLocal)
- if pkgbits.EnableSync {
- want := r.Int()
- if have := len(r.locals); have != want {
- base.FatalfAt(name.Pos(), "locals table has desynced")
+ if name.Sym().Name == dictParamName {
+ r.dictParam = name
+ } else {
+ r.Sync(pkgbits.SyncAddLocal)
+ if r.p.SyncMarkers() {
+ want := r.Int()
+ if have := len(r.locals); have != want {
+ base.FatalfAt(name.Pos(), "locals table has desynced")
+ }
}
+ r.locals = append(r.locals, name)
}
name.SetUsed(true)
- r.locals = append(r.locals, name)
// TODO(mdempsky): Move earlier.
if ir.IsBlank(name) {
@@ -1229,11 +1447,8 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
case stmtAssign:
pos := r.pos()
-
- // TODO(mdempsky): After quirks mode is gone, swap these
- // statements so we visit LHS before RHS again.
- rhs := r.exprList()
names, lhs := r.assignList()
+ rhs := r.multiExpr()
if len(rhs) == 0 {
for _, name := range names {
@@ -1301,7 +1516,7 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
case stmtReturn:
pos := r.pos()
- results := r.exprList()
+ results := r.multiExpr()
return ir.NewReturnStmt(pos, results)
case stmtSelect:
@@ -1323,25 +1538,42 @@ func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
var names []*ir.Name
for i := range lhs {
- if r.Bool() {
- pos := r.pos()
- _, sym := r.localIdent()
- typ := r.typ()
-
- name := ir.NewNameAt(pos, sym)
- lhs[i] = name
- names = append(names, name)
- setType(name, typ)
- r.addLocal(name, ir.PAUTO)
- continue
+ expr, def := r.assign()
+ lhs[i] = expr
+ if def {
+ names = append(names, expr.(*ir.Name))
}
-
- lhs[i] = r.expr()
}
return names, lhs
}
+// assign returns an assignee expression. It also reports whether the
+// returned expression is a newly declared variable.
+func (r *reader) assign() (ir.Node, bool) {
+ switch tag := codeAssign(r.Code(pkgbits.SyncAssign)); tag {
+ default:
+ panic("unhandled assignee expression")
+
+ case assignBlank:
+ return typecheck.AssignExpr(ir.BlankNode), false
+
+ case assignDef:
+ pos := r.pos()
+ setBasePos(pos)
+ _, sym := r.localIdent()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, sym)
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ return name, true
+
+ case assignExpr:
+ return r.expr(), false
+ }
+}
+
func (r *reader) blockStmt() []ir.Node {
r.Sync(pkgbits.SyncBlockStmt)
r.openScope()
@@ -1357,16 +1589,10 @@ func (r *reader) forStmt(label *types.Sym) ir.Node {
if r.Bool() {
pos := r.pos()
+ rang := ir.NewRangeStmt(pos, nil, nil, nil, nil)
+ rang.Label = label
- // TODO(mdempsky): After quirks mode is gone, swap these
- // statements so we read LHS before X again.
- x := r.expr()
names, lhs := r.assignList()
-
- body := r.blockStmt()
- r.closeAnotherScope()
-
- rang := ir.NewRangeStmt(pos, nil, nil, x, body)
if len(lhs) >= 1 {
rang.Key = lhs[0]
if len(lhs) >= 2 {
@@ -1374,13 +1600,27 @@ func (r *reader) forStmt(label *types.Sym) ir.Node {
}
}
rang.Def = r.initDefn(rang, names)
- rang.Label = label
+
+ rang.X = r.expr()
+ if rang.X.Type().IsMap() {
+ rang.RType = r.rtype(pos)
+ }
+ if rang.Key != nil && !ir.IsBlank(rang.Key) {
+ rang.KeyTypeWord, rang.KeySrcRType = r.convRTTI(pos)
+ }
+ if rang.Value != nil && !ir.IsBlank(rang.Value) {
+ rang.ValueTypeWord, rang.ValueSrcRType = r.convRTTI(pos)
+ }
+
+ rang.Body = r.blockStmt()
+ r.closeAnotherScope()
+
return rang
}
pos := r.pos()
init := r.stmt()
- cond := r.expr()
+ cond := r.optExpr()
post := r.stmt()
body := r.blockStmt()
r.closeAnotherScope()
@@ -1419,6 +1659,44 @@ func (r *reader) selectStmt(label *types.Sym) ir.Node {
comm := r.stmt()
body := r.stmts()
+ // "case i = <-c: ..." may require an implicit conversion (e.g.,
+ // see fixedbugs/bug312.go). Currently, typecheck throws away the
+ // implicit conversion and relies on it being reinserted later,
+ // but that would lose any explicit RTTI operands too. To preserve
+ // RTTI, we rewrite this as "case tmp := <-c: i = tmp; ...".
+ if as, ok := comm.(*ir.AssignStmt); ok && as.Op() == ir.OAS && !as.Def {
+ if conv, ok := as.Y.(*ir.ConvExpr); ok && conv.Op() == ir.OCONVIFACE {
+ base.AssertfAt(conv.Implicit(), conv.Pos(), "expected implicit conversion: %v", conv)
+
+ recv := conv.X
+ base.AssertfAt(recv.Op() == ir.ORECV, recv.Pos(), "expected receive expression: %v", recv)
+
+ tmp := r.temp(pos, recv.Type())
+
+ // Replace comm with `tmp := <-c`.
+ tmpAs := ir.NewAssignStmt(pos, tmp, recv)
+ tmpAs.Def = true
+ tmpAs.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ comm = tmpAs
+
+ // Change original assignment to `i = tmp`, and prepend to body.
+ conv.X = tmp
+ body = append([]ir.Node{as}, body...)
+ }
+ }
+
+ // multiExpr will have desugared a comma-ok receive expression
+ // into a separate statement. However, the rest of the compiler
+ // expects comm to be the OAS2RECV statement itself, so we need to
+ // shuffle things around to fit that pattern.
+ if as2, ok := comm.(*ir.AssignListStmt); ok && as2.Op() == ir.OAS2 {
+ init := ir.TakeInit(as2.Rhs[0])
+ base.AssertfAt(len(init) == 1 && init[0].Op() == ir.OAS2RECV, as2.Pos(), "unexpected assignment: %+v", as2)
+
+ comm = init[0]
+ body = append([]ir.Node{as2}, body...)
+ }
+
clauses[i] = ir.NewCommStmt(pos, comm, body)
}
if len(clauses) > 0 {
@@ -1450,7 +1728,7 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
iface = x.Type()
tag = ir.NewTypeSwitchGuard(pos, ident, x)
} else {
- tag = r.expr()
+ tag = r.optExpr()
}
clauses := make([]*ir.CaseClause, r.Len())
@@ -1461,20 +1739,43 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
r.openScope()
pos := r.pos()
- var cases []ir.Node
+ var cases, rtypes []ir.Node
if iface != nil {
cases = make([]ir.Node, r.Len())
if len(cases) == 0 {
cases = nil // TODO(mdempsky): Unclear if this matters.
}
for i := range cases {
- cases[i] = r.exprType(true)
+ if r.Bool() { // case nil
+ cases[i] = typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
+ } else {
+ cases[i] = r.exprType()
+ }
}
} else {
cases = r.exprList()
+
+ // For `switch { case any(true): }` (e.g., issue 3980 in
+ // test/switch.go), the backend still creates a mixed bool/any
+ // comparison, and we need to explicitly supply the RTTI for the
+ // comparison.
+ //
+ // TODO(mdempsky): Change writer.go to desugar "switch {" into
+ // "switch true {", which we already handle correctly.
+ if tag == nil {
+ for i, cas := range cases {
+ if cas.Type().IsEmptyInterface() {
+ for len(rtypes) < i {
+ rtypes = append(rtypes, nil)
+ }
+ rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL]))
+ }
+ }
+ }
}
clause := ir.NewCaseStmt(pos, cases, nil)
+ clause.RTypes = rtypes
if ident != nil {
pos := r.pos()
@@ -1551,25 +1852,14 @@ func (r *reader) expr() (res ir.Node) {
default:
panic("unhandled expression")
- case exprNone:
- return nil
-
- case exprBlank:
- // blank only allowed in LHS of assignments
- // TODO(mdempsky): Handle directly in assignList instead?
- return typecheck.AssignExpr(ir.BlankNode)
-
case exprLocal:
return typecheck.Expr(r.useLocal())
- case exprName:
+ case exprGlobal:
// Callee instead of Expr allows builtins
// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
return typecheck.Callee(r.obj())
- case exprType:
- return r.exprType(false)
-
case exprConst:
pos := r.pos()
typ := r.typ()
@@ -1578,6 +1868,11 @@ func (r *reader) expr() (res ir.Node) {
orig := r.String()
return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
+ case exprNil:
+ pos := r.pos()
+ typ := r.typ()
+ return Nil(pos, typ)
+
case exprCompLit:
return r.compLit()
@@ -1585,17 +1880,20 @@ func (r *reader) expr() (res ir.Node) {
return r.funcLit()
case exprSelector:
- x := r.expr()
- pos := r.pos()
- _, sym := r.selector()
+ var x ir.Node
+ if r.Bool() { // MethodExpr
+ if r.Bool() {
+ return r.dict.methodExprs[r.Len()]
+ }
- // Method expression with derived receiver type.
- if x.Op() == ir.ODYNAMICTYPE {
- // TODO(mdempsky): Handle with runtime dictionary lookup.
- n := ir.TypeNode(x.Type())
+ n := ir.TypeNode(r.typ())
n.SetTypecheck(1)
x = n
+ } else { // FieldVal, MethodVal
+ x = r.expr()
}
+ pos := r.pos()
+ _, sym := r.selector()
n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
if n.Op() == ir.OMETHVALUE {
@@ -1615,14 +1913,20 @@ func (r *reader) expr() (res ir.Node) {
x := r.expr()
pos := r.pos()
index := r.expr()
- return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+ n := typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+ switch n.Op() {
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
case exprSlice:
x := r.expr()
pos := r.pos()
var index [3]ir.Node
for i := range index {
- index[i] = r.expr()
+ index[i] = r.optExpr()
}
op := ir.OSLICE
if index[2] != nil {
@@ -1633,10 +1937,13 @@ func (r *reader) expr() (res ir.Node) {
case exprAssert:
x := r.expr()
pos := r.pos()
- typ := r.exprType(false)
+ typ := r.exprType()
+ srcRType := r.rtype(pos)
+ // TODO(mdempsky): Always emit ODYNAMICDOTTYPE for uniformity?
if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType)
+ assert.SrcRType = srcRType
assert.ITab = typ.ITab
return typed(typ.Type(), assert)
}
@@ -1675,13 +1982,43 @@ func (r *reader) expr() (res ir.Node) {
fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym))
}
pos := r.pos()
- args := r.exprs()
+ args := r.multiExpr()
dots := r.Bool()
- return typecheck.Call(pos, fun, args, dots)
+ n := typecheck.Call(pos, fun, args, dots)
+ switch n.Op() {
+ case ir.OAPPEND:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ n.RType = r.rtype(pos)
+ case ir.OUNSAFESLICE:
+ n := n.(*ir.BinaryExpr)
+ n.RType = r.rtype(pos)
+ }
+ return n
+
+ case exprMake:
+ pos := r.pos()
+ typ := r.exprType()
+ extra := r.exprs()
+ n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr)
+ n.RType = r.rtype(pos)
+ return n
+
+ case exprNew:
+ pos := r.pos()
+ typ := r.exprType()
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
case exprConvert:
+ implicit := r.Bool()
typ := r.typ()
pos := r.pos()
+ typeWord, srcRType := r.convRTTI(pos)
x := r.expr()
// TODO(mdempsky): Stop constructing expressions of untyped type.
@@ -1697,8 +2034,72 @@ func (r *reader) expr() (res ir.Node) {
base.ErrorExit() // harsh, but prevents constructing invalid IR
}
- return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
+ n := ir.NewConvExpr(pos, ir.OCONV, typ, x)
+ n.TypeWord, n.SrcRType = typeWord, srcRType
+ if implicit {
+ n.SetImplicit(true)
+ }
+ return typecheck.Expr(n)
+ }
+}
+
+func (r *reader) optExpr() ir.Node {
+ if r.Bool() {
+ return r.expr()
}
+ return nil
+}
+
+func (r *reader) multiExpr() []ir.Node {
+ r.Sync(pkgbits.SyncMultiExpr)
+
+ if r.Bool() { // N:1
+ pos := r.pos()
+ expr := r.expr()
+
+ results := make([]ir.Node, r.Len())
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr})
+ as.Def = true
+ for i := range results {
+ tmp := r.temp(pos, r.typ())
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ as.Lhs.Append(tmp)
+
+ res := ir.Node(tmp)
+ if r.Bool() {
+ n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res)
+ n.TypeWord, n.SrcRType = r.convRTTI(pos)
+ n.SetImplicit(true)
+ res = typecheck.Expr(n)
+ }
+ results[i] = res
+ }
+
+ // TODO(mdempsky): Could use ir.InlinedCallExpr instead?
+ results[0] = ir.InitExpr([]ir.Node{typecheck.Stmt(as)}, results[0])
+ return results
+ }
+
+ // N:N
+ exprs := make([]ir.Node, r.Len())
+ if len(exprs) == 0 {
+ return nil
+ }
+ for i := range exprs {
+ exprs[i] = r.expr()
+ }
+ return exprs
+}
+
+// temp returns a new autotemp of the specified type.
+func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name {
+ // See typecheck.typecheckargs.
+ curfn := r.curfn
+ if curfn == nil {
+ curfn = typecheck.InitTodoFunc
+ }
+
+ return typecheck.TempAt(pos, curfn, typ)
}
func (r *reader) compLit() ir.Node {
@@ -1713,6 +2114,10 @@ func (r *reader) compLit() ir.Node {
if typ.Kind() == types.TFORW {
base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
}
+ var rtype ir.Node
+ if typ.IsMap() {
+ rtype = r.rtype(pos)
+ }
isStruct := typ.Kind() == types.TSTRUCT
elems := make([]ir.Node, r.Len())
@@ -1731,6 +2136,10 @@ func (r *reader) compLit() ir.Node {
}
lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
+ if rtype != nil {
+ lit := lit.(*ir.CompLitExpr)
+ lit.RType = rtype
+ }
if typ0.IsPtr() {
lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
lit.SetType(typ0)
@@ -1775,6 +2184,11 @@ func (r *reader) funcLit() ir.Node {
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
ir.NewClosureVar(r.pos(), fn, r.useLocal())
}
+ if param := r.dictParam; param != nil {
+ // If we have a dictionary parameter, capture it too. For
+ // simplicity, we capture it last and unconditionally.
+ ir.NewClosureVar(param.Pos(), fn, param)
+ }
r.addBody(fn)
@@ -1799,17 +2213,73 @@ func (r *reader) exprs() []ir.Node {
return nodes
}
-func (r *reader) exprType(nilOK bool) ir.Node {
- r.Sync(pkgbits.SyncExprType)
+// dictWord returns an expression to return the specified
+// uintptr-typed word from the dictionary parameter.
+func (r *reader) dictWord(pos src.XPos, idx int64) ir.Node {
+ base.AssertfAt(r.dictParam != nil, pos, "expected dictParam in %v", r.curfn)
+ return typecheck.Expr(ir.NewIndexExpr(pos, r.dictParam, ir.NewBasicLit(pos, constant.MakeInt64(idx))))
+}
- if nilOK && r.Bool() {
- return typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr))
+// rtype reads a type reference from the element bitstream, and
+// returns an expression of type *runtime._type representing that
+// type.
+func (r *reader) rtype(pos src.XPos) ir.Node {
+ r.Sync(pkgbits.SyncRType)
+ return r.rtypeInfo(pos, r.typInfo())
+}
+
+// rtypeInfo returns an expression of type *runtime._type representing
+// the given decoded type reference.
+func (r *reader) rtypeInfo(pos src.XPos, info typeInfo) ir.Node {
+ if !info.derived {
+ typ := r.p.typIdx(info, r.dict, true)
+ return reflectdata.TypePtrAt(pos, typ)
+ }
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TUINT8]), r.dictWord(pos, int64(info.idx))))
+}
+
+// convRTTI returns expressions appropriate for populating an
+// ir.ConvExpr's TypeWord and SrcRType fields, respectively.
+func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) {
+ r.Sync(pkgbits.SyncConvRTTI)
+ srcInfo := r.typInfo()
+ dstInfo := r.typInfo()
+
+ dst := r.p.typIdx(dstInfo, r.dict, true)
+ if !dst.IsInterface() {
+ return
}
+ src := r.p.typIdx(srcInfo, r.dict, true)
+
+ // See reflectdata.ConvIfaceTypeWord.
+ switch {
+ case dst.IsEmptyInterface():
+ if !src.IsInterface() {
+ typeWord = r.rtypeInfo(pos, srcInfo) // direct eface construction
+ }
+ case !src.IsInterface():
+ typeWord = reflectdata.ITabAddrAt(pos, src, dst) // direct iface construction
+ default:
+ typeWord = r.rtypeInfo(pos, dstInfo) // convI2I
+ }
+
+ // See reflectdata.ConvIfaceSrcRType.
+ if !src.IsInterface() {
+ srcRType = r.rtypeInfo(pos, srcInfo)
+ }
+
+ return
+}
+
+func (r *reader) exprType() ir.Node {
+ r.Sync(pkgbits.SyncExprType)
+
pos := r.pos()
+ setBasePos(pos)
lsymPtr := func(lsym *obj.LSym) ir.Node {
- return typecheck.Expr(typecheck.NodAddr(ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8])))
+ return typecheck.Expr(typecheck.NodAddrAt(pos, ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8])))
}
var typ *types.Type
@@ -1836,7 +2306,7 @@ func (r *reader) exprType(nilOK bool) ir.Node {
return n
}
- rtype = lsymPtr(reflectdata.TypeLinksym(typ))
+ rtype = r.rtypeInfo(pos, info)
}
dt := ir.NewDynamicType(pos, rtype)
@@ -1974,13 +2444,20 @@ func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
var inlgen = 0
+// InlineCall implements inline.NewInline by re-reading the function
+// body from its Unified IR export data.
func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
// TODO(mdempsky): Turn callerfn into an explicit parameter.
callerfn := ir.CurFunc
- pri, ok := bodyReader[fn]
+ pri, ok := bodyReaderFor(fn)
if !ok {
- base.FatalfAt(call.Pos(), "missing function body for call to %v", fn)
+ // TODO(mdempsky): Reconsider this diagnostic's wording, if it's
+ // to be included in Go 1.20.
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(call.Pos(), "cannot inline call to %v: missing inline body", fn)
+ }
+ return nil
}
if fn.Inl.Body == nil {
@@ -2008,6 +2485,9 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
for i, cv := range r.inlFunc.ClosureVars {
r.closureVars[i] = cv.Outer
}
+ if len(r.closureVars) != 0 && r.hasTypeParams() {
+ r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
+ }
r.funcargs(fn)
@@ -2070,8 +2550,12 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
nparams := len(r.curfn.Dcl)
ir.WithFunc(r.curfn, func() {
- r.curfn.Body = r.stmts()
- r.curfn.Endlineno = r.pos()
+ if r.shapedFn != nil {
+ r.callShaped(call.Pos())
+ } else {
+ r.curfn.Body = r.stmts()
+ r.curfn.Endlineno = r.pos()
+ }
// TODO(mdempsky): This shouldn't be necessary. Inlining might
// read in new function/method declarations, which could
@@ -2260,18 +2744,21 @@ func (r *reader) needWrapper(typ *types.Type) {
}
}
+// importedDef reports whether r is reading from an imported and
+// non-generic element.
+//
+// If a type was found in an imported package, then we can assume that
+// package (or one of its transitive dependencies) already generated
+// method wrappers for it.
+//
+// Exception: If we're instantiating an imported generic type or
+// function, we might be instantiating it with type arguments not
+// previously seen before.
+//
+// TODO(mdempsky): Distinguish when a generic function or type was
+// instantiated in an imported package so that we can add types to
+// haveWrapperTypes instead.
func (r *reader) importedDef() bool {
- // If a type was found in an imported package, then we can assume
- // that package (or one of its transitive dependencies) already
- // generated method wrappers for it.
- //
- // Exception: If we're instantiating an imported generic type or
- // function, we might be instantiating it with type arguments not
- // previously seen before.
- //
- // TODO(mdempsky): Distinguish when a generic function or type was
- // instantiated in an imported package so that we can add types to
- // haveWrapperTypes instead.
return r.p != localPkgReader && !r.hasTypeParams()
}
@@ -2459,6 +2946,15 @@ func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
// so we're responsible for applying inlining ourselves here.
inline.InlineCalls(fn)
+ // The body of wrapper function after inlining may reveal new ir.OMETHVALUE node,
+ // we don't know whether wrapper function has been generated for it or not, so
+ // generate one immediately here.
+ ir.VisitList(fn.Body, func(n ir.Node) {
+ if n, ok := n.(*ir.SelectorExpr); ok && n.Op() == ir.OMETHVALUE {
+ wrapMethodValue(n.X.Type(), n.Selection, target, true)
+ }
+ })
+
target.Decls = append(target.Decls, fn)
}
@@ -2516,3 +3012,45 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
ret.Results = []ir.Node{call}
fn.Body.Append(ret)
}
+
+func setBasePos(pos src.XPos) {
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = pos
+}
+
+// dictParamName is the name of the synthetic dictionary parameter
+// added to shaped functions.
+const dictParamName = ".dict"
+
+// shapeSig returns a copy of fn's signature, except adding a
+// dictionary parameter and promoting the receiver parameter (if any)
+// to a normal parameter.
+//
+// The parameter types.Fields are all copied too, so their Nname
+// fields can be initialized for use by the shape function.
+func shapeSig(fn *ir.Func, dict *readerDict) *types.Type {
+ sig := fn.Nname.Type()
+ recv := sig.Recv()
+ nrecvs := 0
+ if recv != nil {
+ nrecvs++
+ }
+
+ params := make([]*types.Field, 1+nrecvs+sig.Params().Fields().Len())
+ params[0] = types.NewField(fn.Pos(), fn.Sym().Pkg.Lookup(dictParamName), types.NewPtr(dict.varType()))
+ if recv != nil {
+ params[1] = types.NewField(recv.Pos, recv.Sym, recv.Type)
+ }
+ for i, param := range sig.Params().Fields().Slice() {
+ d := types.NewField(param.Pos, param.Sym, param.Type)
+ d.SetIsDDD(param.IsDDD())
+ params[1+nrecvs+i] = d
+ }
+
+ results := make([]*types.Field, sig.Results().Fields().Len())
+ for i, result := range sig.Results().Fields().Slice() {
+ results[i] = types.NewField(result.Pos, result.Sym, result.Type)
+ }
+
+ return types.NewSignature(types.LocalPkg, nil, nil, params, results)
+}
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 46acdab79e..1ded367383 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.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.
@@ -82,13 +80,12 @@ func unified(noders []*noder) {
base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version)
types.ParseLangFlag()
- types.LocalPkg.Height = 0 // reset so pkgReader.pkgIdx doesn't complain
target := typecheck.Target
typecheck.TypecheckAllowed = true
localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
- readPackage(localPkgReader, types.LocalPkg)
+ readPackage(localPkgReader, types.LocalPkg, true)
r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
r.pkgInit(types.LocalPkg, target)
@@ -164,7 +161,7 @@ func writePkgStub(noders []*noder) string {
{
w := publicRootWriter
w.pkg(pkg)
- w.Bool(false) // has init; XXX
+ w.Bool(false) // TODO(mdempsky): Remove; was "has init"
scope := pkg.Scope()
names := scope.Names()
@@ -227,42 +224,76 @@ func freePackage(pkg *types2.Package) {
base.Fatalf("package never finalized")
}
-func readPackage(pr *pkgReader, importpkg *types.Pkg) {
- r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+// readPackage reads package export data from pr to populate
+// importpkg.
+//
+// localStub indicates whether pr is reading the stub export data for
+// the local package, as opposed to relocated export data for an
+// import.
+func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
+ {
+ r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+
+ pkg := r.pkg()
+ base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg)
+
+ r.Bool() // TODO(mdempsky): Remove; was "has init"
- pkg := r.pkg()
- base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg)
+ for i, n := 0, r.Len(); i < n; i++ {
+ r.Sync(pkgbits.SyncObject)
+ assert(!r.Bool())
+ idx := r.Reloc(pkgbits.RelocObj)
+ assert(r.Len() == 0)
- if r.Bool() {
- sym := pkg.Lookup(".inittask")
- task := ir.NewNameAt(src.NoXPos, sym)
- task.Class = ir.PEXTERN
- sym.Def = task
+ path, name, code := r.p.PeekObj(idx)
+ if code != pkgbits.ObjStub {
+ objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil}
+ }
+ }
+
+ r.Sync(pkgbits.SyncEOF)
}
- for i, n := 0, r.Len(); i < n; i++ {
- r.Sync(pkgbits.SyncObject)
- assert(!r.Bool())
- idx := r.Reloc(pkgbits.RelocObj)
- assert(r.Len() == 0)
+ if !localStub {
+ r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
- path, name, code := r.p.PeekObj(idx)
- if code != pkgbits.ObjStub {
- objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil}
+ if r.Bool() {
+ sym := importpkg.Lookup(".inittask")
+ task := ir.NewNameAt(src.NoXPos, sym)
+ task.Class = ir.PEXTERN
+ sym.Def = task
+ }
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ path := r.String()
+ name := r.String()
+ idx := r.Reloc(pkgbits.RelocBody)
+
+ sym := types.NewPkg(path, "").Lookup(name)
+ if _, ok := importBodyReader[sym]; !ok {
+ importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil}
+ }
}
+
+ r.Sync(pkgbits.SyncEOF)
}
}
+// writeUnifiedExport writes to `out` the finalized, self-contained
+// Unified IR export data file for the current compilation unit.
func writeUnifiedExport(out io.Writer) {
l := linker{
pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
- pkgs: make(map[string]pkgbits.Index),
- decls: make(map[*types.Sym]pkgbits.Index),
+ pkgs: make(map[string]pkgbits.Index),
+ decls: make(map[*types.Sym]pkgbits.Index),
+ bodies: make(map[*types.Sym]pkgbits.Index),
}
publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
+ privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate)
assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
+ assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
var selfPkgIdx pkgbits.Index
@@ -273,7 +304,7 @@ func writeUnifiedExport(out io.Writer) {
r.Sync(pkgbits.SyncPkg)
selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg))
- r.Bool() // has init
+ r.Bool() // TODO(mdempsky): Remove; was "has init"
for i, n := 0, r.Len(); i < n; i++ {
r.Sync(pkgbits.SyncObject)
@@ -304,8 +335,7 @@ func writeUnifiedExport(out io.Writer) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, selfPkgIdx)
-
- w.Bool(typecheck.Lookup(".inittask").Def != nil)
+ w.Bool(false) // TODO(mdempsky): Remove; was "has init"
w.Len(len(idxs))
for _, idx := range idxs {
@@ -319,5 +349,31 @@ func writeUnifiedExport(out io.Writer) {
w.Flush()
}
+ {
+ type symIdx struct {
+ sym *types.Sym
+ idx pkgbits.Index
+ }
+ var bodies []symIdx
+ for sym, idx := range l.bodies {
+ bodies = append(bodies, symIdx{sym, idx})
+ }
+ sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx })
+
+ w := privateRootWriter
+
+ w.Bool(typecheck.Lookup(".inittask").Def != nil)
+
+ w.Len(len(bodies))
+ for _, body := range bodies {
+ w.String(body.sym.Pkg.Path)
+ w.String(body.sym.Name)
+ w.Reloc(pkgbits.RelocBody, body.idx)
+ }
+
+ w.Sync(pkgbits.SyncEOF)
+ w.Flush()
+ }
+
base.Ctxt.Fingerprint = l.pw.DumpTo(out)
}
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.assign(expr)
+ }
+}
- w.Bool(true)
- w.pos(obj)
- w.localIdent(obj)
- w.typ(obj.Type())
+func (w *writer) assign(expr syntax.Expr) {
+ expr = unparen(expr)
- // TODO(mdempsky): Minimize locals index size by deferring
- // this until the variables actually come into scope.
- w.addLocal(obj)
- continue
- }
+ 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,11 +1249,46 @@ 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) {
@@ -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()