-branch: master
+branch: dev.unified
+parent-branch: master
types.LocalPkg = types.NewPkg(base.Ctxt.Pkgpath, "")
- // We won't know localpkg's height until after import
- // processing. In the mean time, set to MaxPkgHeight to ensure
- // height comparisons at least work until then.
- types.LocalPkg.Height = types.MaxPkgHeight
-
// pseudo-package, for scoping
types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin
pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff)
pkgName := p.stringAt(r.uint64())
- pkgHeight := int(r.uint64())
+ _ = int(r.uint64()) // was package height, but not necessary anymore.
if pkgPath == "" {
pkgPath = path
}
pkg := imports[pkgPath]
if pkg == nil {
- pkg = types2.NewPackageHeight(pkgPath, pkgName, pkgHeight)
+ pkg = types2.NewPackage(pkgPath, pkgName)
imports[pkgPath] = pkg
} else {
if pkg.Name() != pkgName {
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
}
- if pkg.Height() != pkgHeight {
- errorf("conflicting heights %v and %v for package %q", pkg.Height(), pkgHeight, path)
- }
}
p.pkgCache[pkgPathOff] = pkg
}
name := r.String()
- height := r.Len()
-
- pkg := types2.NewPackageHeight(path, name, height)
+ pkg := types2.NewPackage(path, name)
r.p.imports[path] = pkg
// TODO(mdempsky): The list of imported packages is important for
case ir.OMETHEXPR:
v.budget++ // Hack for toolstash -cmp.
+
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+
+ // Unified IR unconditionally rewrites:
+ //
+ // a, b = f()
+ //
+ // into:
+ //
+ // DCL tmp1
+ // DCL tmp2
+ // tmp1, tmp2 = f()
+ // a, b = tmp1, tmp2
+ //
+ // so that it can insert implicit conversions as necessary. To
+ // minimize impact to the existing inlining heuristics (in
+ // particular, to avoid breaking the existing inlinability regress
+ // tests), we need to compensate for this here.
+ if base.Debug.Unified != 0 {
+ if init := n.Rhs[0].Init(); len(init) == 1 {
+ if _, ok := init[0].(*ir.AssignListStmt); ok {
+ // 4 for each value, because each temporary variable now
+ // appears 3 times (DCL, LHS, RHS), plus an extra DCL node.
+ //
+ // 1 for the extra "tmp1, tmp2 = f()" assignment statement.
+ v.budget += 4*int32(len(n.Lhs)) + 1
+ }
+ }
+ }
}
v.budget--
// or Op(X, Y) for builtin functions that do not become calls.
type BinaryExpr struct {
miniExpr
- X Node
- Y Node
+ X Node
+ Y Node
+ RType Node `mknode:"-"` // see reflectdata/helpers.go
}
func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr {
origNode
X Node
Args Nodes
+ RType Node `mknode:"-"` // see reflectdata/helpers.go
KeepAlive []*Name // vars to be kept alive until call returns
IsDDD bool
NoInline bool
miniExpr
origNode
List Nodes // initialized values
+ RType Node `mknode:"-"` // *runtime._type for OMAPLIT map types
Prealloc *Name
// For OSLICELIT, Len is the backing array length.
// For OMAPLIT, Len is the number of entries that we've removed from List and
type ConvExpr struct {
miniExpr
X Node
+
+ // For implementing OCONVIFACE expressions.
+ //
+ // TypeWord is an expression yielding a *runtime._type or
+ // *runtime.itab value to go in the type word of the iface/eface
+ // result. See reflectdata.ConvIfaceTypeWord for further details.
+ //
+ // SrcRType is an expression yielding a *runtime._type value for X,
+ // if it's not pointer-shaped and needs to be heap allocated.
+ TypeWord Node `mknode:"-"`
+ SrcRType Node `mknode:"-"`
+
+ // For -d=checkptr instrumentation of conversions from
+ // unsafe.Pointer to *Elem or *[Len]Elem.
+ //
+ // TODO(mdempsky): We only ever need one of these, but currently we
+ // don't decide which one until walk. Longer term, it probably makes
+ // sense to have a dedicated IR op for `(*[Len]Elem)(ptr)[:n:m]`
+ // expressions.
+ ElemRType Node `mknode:"-"`
+ ElemElemRType Node `mknode:"-"`
}
func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr {
miniExpr
X Node
Index Node
+ RType Node `mknode:"-"` // see reflectdata/helpers.go
Assigned bool
}
// but *not* OMAKE (that's a pre-typechecking CallExpr).
type MakeExpr struct {
miniExpr
- Len Node
- Cap Node
+ RType Node `mknode:"-"` // see reflectdata/helpers.go
+ Len Node
+ Cap Node
}
func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr {
// Runtime type information provided by walkDotType for
// assertions from non-empty interface to concrete type.
- ITab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type
+ ITab Node `mknode:"-"` // *runtime.itab for Type implementing X's type
}
func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr {
miniExpr
X Node
+ // SrcRType is an expression that yields a *runtime._type value
+ // representing X's type. It's used in failed assertion panic
+ // messages.
+ SrcRType Node
+
// RType is an expression that yields a *runtime._type value
// representing the asserted type.
//
miniStmt
Var *Name // declared variable for this case in type switch
List Nodes // list of expressions for switch, early select
+
+ // RTypes is a list of RType expressions, which are copied to the
+ // corresponding OEQ nodes that are emitted when switch statements
+ // are desugared. RTypes[i] must be non-nil if the emitted
+ // comparison for List[i] will be a mixed interface/concrete
+ // comparison; see reflectdata.CompareRType for details.
+ //
+ // Because mixed interface/concrete switch cases are rare, we allow
+ // len(RTypes) < len(List). Missing entries are implicitly nil.
+ RTypes Nodes
+
Body Nodes
}
Label *types.Sym
Def bool
X Node
+ RType Node `mknode:"-"` // see reflectdata/helpers.go
Key Node
Value Node
Body Nodes
-// 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.
import "internal/pkgbits"
+// A codeStmt distinguishes among statement encodings.
type codeStmt int
func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 }
stmtSelect
)
+// A codeExpr distinguishes among expression encodings.
type codeExpr int
func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr }
// 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
exprBinaryOp
exprCall
exprConvert
+ exprNew
+ exprMake
+)
+
+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 }
// 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))
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
-// 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.
// 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
decls 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 {
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)
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)
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)
return w.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)
-// 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.
"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) {
-// 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.
"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
}
}
}
+// A pkgReaderIndex compactly identifies an index (and its
+// corresponding dictionary) within a package's export data.
type pkgReaderIndex struct {
pr *pkgReader
idx pkgbits.Index
}
}
+// A writer provides APIs for reading an individual element.
type reader struct {
pkgbits.Decoder
// @@@ Positions
+// pos reads a position from the bitstream.
func (r *reader) pos() src.XPos {
return base.Ctxt.PosTable.XPos(r.pos0())
}
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
return b
}
+// TODO(mdempsky): Document this.
func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
if r.inlCall == nil {
return oldBase
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
return pkg
}
+// doPkg reads a package definition from the bitstream.
func (r *reader) doPkg() *types.Pkg {
path := r.String()
switch path {
}
name := r.String()
- height := r.Len()
pkg := types.NewPkg(path, "")
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
}
// @@@ 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)
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()
do := func(op ir.Op, hasTParams bool) *ir.Name {
pos := r.pos()
+ setBasePos(pos)
if hasTParams {
r.typeParamNames()
}
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)
// 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
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
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 {
case stmtReturn:
pos := r.pos()
- results := r.exprList()
+ results := r.multiExpr()
return ir.NewReturnStmt(pos, results)
case stmtSelect:
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()
if r.Bool() {
pos := r.pos()
- // TODO(mdempsky): After quirks mode is gone, swap these
- // statements so we read LHS before X again.
- x := r.expr()
names, lhs := r.assignList()
+ x := r.expr()
body := r.blockStmt()
r.closeAnotherScope()
rang := ir.NewRangeStmt(pos, nil, nil, x, body)
+ if x.Type().IsMap() {
+ rang.RType = reflectdata.TypePtrAt(pos, x.Type())
+ }
if len(lhs) >= 1 {
rang.Key = lhs[0]
if len(lhs) >= 2 {
pos := r.pos()
init := r.stmt()
- cond := r.expr()
+ cond := r.optExpr()
post := r.stmt()
body := r.blockStmt()
r.closeAnotherScope()
comm := r.stmt()
body := r.stmts()
+ // 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 {
iface = x.Type()
tag = ir.NewTypeSwitchGuard(pos, ident, x)
} else {
- tag = r.expr()
+ tag = r.optExpr()
}
clauses := make([]*ir.CaseClause, r.Len())
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 {
}
} else {
cases = r.exprList()
+
+ tagType := types.Types[types.TBOOL]
+ if tag != nil {
+ tagType = tag.Type()
+ }
+ for i, cas := range cases {
+ if cas.Op() == ir.ONIL {
+ continue // never needs rtype
+ }
+ if tagType.IsInterface() != cas.Type().IsInterface() {
+ typ := tagType
+ if typ.IsInterface() {
+ typ = cas.Type()
+ }
+ for len(rtypes) < i {
+ rtypes = append(rtypes, nil)
+ }
+ rtypes = append(rtypes, reflectdata.TypePtr(typ))
+ }
+ }
}
clause := ir.NewCaseStmt(pos, cases, nil)
+ clause.RTypes = rtypes
if ident != nil {
pos := r.pos()
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()
return r.funcLit()
case exprSelector:
- x := r.expr()
+ var x ir.Node
+ if r.Bool() { // MethodExpr
+ x = r.exprType(false)
+
+ // Method expression with derived receiver type.
+ if x.Op() == ir.ODYNAMICTYPE {
+ // TODO(mdempsky): Handle with runtime dictionary lookup.
+ n := ir.TypeNode(x.Type())
+ n.SetTypecheck(1)
+ x = n
+ }
+ } else { // FieldVal, MethodVal
+ x = r.expr()
+ }
pos := r.pos()
_, sym := r.selector()
- // Method expression with derived receiver type.
- if x.Op() == ir.ODYNAMICTYPE {
- // TODO(mdempsky): Handle with runtime dictionary lookup.
- n := ir.TypeNode(x.Type())
- n.SetTypecheck(1)
- x = n
- }
-
n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
if n.Op() == ir.OMETHVALUE {
wrapper := methodValueWrapper{
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 = reflectdata.TypePtrAt(pos, x.Type())
+ }
+ 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 {
if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE {
assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType)
+ assert.SrcRType = reflectdata.TypePtrAt(pos, x.Type())
assert.ITab = typ.ITab
return typed(typ.Type(), assert)
}
case ir.OANDAND, ir.OOROR:
return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
}
- return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+ n := typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+ switch n.Op() {
+ case ir.OEQ, ir.ONE:
+ n := n.(*ir.BinaryExpr)
+ if n.X.Type().IsInterface() != n.Y.Type().IsInterface() {
+ typ := n.X.Type()
+ if typ.IsInterface() {
+ typ = n.Y.Type()
+ }
+ n.RType = reflectdata.TypePtrAt(pos, typ)
+ }
+ }
+ return n
case exprCall:
fun := r.expr()
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 = reflectdata.TypePtrAt(pos, n.Type().Elem())
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ n.RType = reflectdata.TypePtrAt(pos, n.X.Type().Elem())
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ n.RType = reflectdata.TypePtrAt(pos, n.Args[0].Type())
+ case ir.OUNSAFESLICE:
+ n := n.(*ir.BinaryExpr)
+ n.RType = reflectdata.TypePtrAt(pos, n.Type().Elem())
+ }
+ return n
+
+ case exprMake:
+ pos := r.pos()
+ typ := r.exprType(false)
+ extra := r.exprs()
+ n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr)
+ switch n.Op() {
+ case ir.OMAKECHAN:
+ n.RType = reflectdata.TypePtrAt(pos, typ.Type())
+ case ir.OMAKEMAP:
+ n.RType = reflectdata.TypePtrAt(pos, typ.Type())
+ case ir.OMAKESLICE:
+ n.RType = reflectdata.TypePtrAt(pos, typ.Type().Elem())
+ }
+ return n
+
+ case exprNew:
+ pos := r.pos()
+ typ := r.exprType(false)
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
case exprConvert:
+ implicit := r.Bool()
typ := r.typ()
pos := r.pos()
x := r.expr()
base.ErrorExit() // harsh, but prevents constructing invalid IR
}
- return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
+ n := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
+ if implicit && n.Op() != ir.OLITERAL {
+ n.(ImplicitNode).SetImplicit(true)
+ }
+ return 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()
+
+ // See typecheck.typecheckargs.
+ curfn := r.curfn
+ if curfn == nil {
+ curfn = typecheck.InitTodoFunc
+ }
+
+ results := make([]ir.Node, r.Len())
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr})
+ as.Def = true
+ for i := range results {
+ tmp := typecheck.TempAt(pos, curfn, r.typ())
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
+ as.Lhs.Append(tmp)
+
+ res := ir.Node(tmp)
+ if r.Bool() {
+ res = typecheck.Expr(Implicit(ir.NewConvExpr(pos, ir.OCONV, r.typ(), res)))
+ }
+ 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
}
func (r *reader) compLit() ir.Node {
}
lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
+ switch lit.Op() {
+ case ir.OMAPLIT:
+ lit := lit.(*ir.CompLitExpr)
+ lit.RType = reflectdata.TypePtrAt(pos, typ)
+ }
if typ0.IsPtr() {
lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
lit.SetType(typ0)
}
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])))
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
}
}
+// 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()
}
// 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)
}
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
+}
-// 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.
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
base.Fatalf("package never finalized")
}
+// readPackage reads package export data from pr to populate
+// importpkg.
func readPackage(pr *pkgReader, importpkg *types.Pkg) {
r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
}
}
+// 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),
-// 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.
"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
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),
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),
}
}
+// 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]
+ assert(ok)
+ assert(tv.IsValue())
+ 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
}
// A derivedInfo represents a reference to an encoded generic Go type.
type derivedInfo struct {
idx pkgbits.Index
- needed bool
+ needed bool // TODO(mdempsky): Remove; will break x/tools importer
}
// A typeInfo represents a reference to an encoded Go type.
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).
+//
+// itabInfo is only used for
type itabInfo struct {
typIdx pkgbits.Index // always a derived type index
iface typeInfo // always a non-empty interface type
}
+// 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 {
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
// @@@ Positions
+// pos writes the position of p into the element bitstream.
func (w *writer) pos(p poser) {
w.Sync(pkgbits.SyncPos)
pos := p.Pos()
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
// @@@ Packages
+// pkg writes a use of the given Package into the element bitstream.
func (w *writer) pkg(pkg *types2.Package) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg))
}
+// 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
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() {
var anyTypeName = types2.Universe.Lookup("any").(*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) {
// @@@ 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 {
}
}
+// 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
}
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)
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
}
// 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)
// 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)
}
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)
// @@@ 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)
}
}
+// addLocal records the declaration of a new local variable.
func (w *writer) addLocal(obj *types2.Var) {
w.Sync(pkgbits.SyncAddLocal)
idx := len(w.localsIdx)
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)
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)
// @@@ 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 {
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:
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)
w.Len(len(exprs))
for _, expr := range exprs {
- if name, ok := expr.(*syntax.Name); ok && name.Value != "_" {
- if obj, ok := w.p.info.Defs[name]; ok {
- obj := obj.(*types2.Var)
-
- w.Bool(true)
- w.pos(obj)
- w.localIdent(obj)
- w.typ(obj.Type())
-
- // TODO(mdempsky): Minimize locals index size by deferring
- // this until the variables actually come into scope.
- w.addLocal(obj)
- continue
- }
+ w.assign(expr)
+ }
+}
+
+func (w *writer) assign(expr syntax.Expr) {
+ expr = unparen(expr)
+
+ if name, ok := expr.(*syntax.Name); ok {
+ if name.Value == "_" {
+ w.Code(assignBlank)
+ return
}
- w.Bool(false)
- w.expr(expr)
+ if obj, ok := w.p.info.Defs[name]; ok {
+ obj := obj.(*types2.Var)
+
+ w.Code(assignDef)
+ w.pos(obj)
+ w.localIdent(obj)
+ w.typ(obj.Type())
+
+ // TODO(mdempsky): Minimize locals index size by deferring
+ // this until the variables actually come into scope.
+ w.addLocal(obj)
+ return
+ }
}
+
+ w.Code(assignExpr)
+ w.expr(expr)
}
func (w *writer) declStmt(decl syntax.Decl) {
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) {
if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
w.pos(rang)
- w.expr(rang.X)
+ // TODO(mdempsky): For !rang.Def, we need to handle implicit
+ // conversions; e.g., see #53328.
+ //
+ // This is tricky, because the assignments aren't introduced until
+ // lowering in walk.
w.assignList(rang.Lhs)
+ w.expr(rang.X)
} else {
w.pos(stmt)
w.stmt(stmt.Init)
- w.expr(stmt.Cond)
+ w.optExpr(stmt.Cond)
w.stmt(stmt.Post)
}
var iface 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) {
}
w.expr(guard.X)
} else {
- w.expr(stmt.Tag)
+ w.optExpr(stmt.Tag)
}
w.Len(len(stmt.Body))
w.exprType(iface, cas, true)
}
} else {
+ // TODO(mdempsky): Implicit conversions to tagType, if appropriate.
w.exprList(clause.Cases)
}
// @@@ 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 {
if obj != nil {
if isGlobal(obj) {
- w.Code(exprName)
+ w.Code(exprGlobal)
w.obj(obj, targs)
return
}
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)
assert(ok)
w.Code(exprSelector)
- w.expr(expr.X)
+ if w.Bool(sel.Kind() == types2.MethodExpr) {
+ w.exprType(nil, expr.X, false)
+ } 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
+
+ var keyType types2.Type
+ if mapType, ok := types2.CoreType(w.p.typeOf(expr.X)).(*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)
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, false)
case *syntax.Operation:
if expr.Y == nil {
break
}
+ // TODO(mdempsky): Implicit conversions to common type.
w.Code(exprBinaryOp)
w.op(binOps[expr.Op])
w.expr(expr.X)
assert(!expr.HasDots)
w.Code(exprConvert)
+ w.Bool(false) // explicit
w.typ(tv.Type)
w.pos(expr)
w.expr(expr.ArgList[0])
break
}
+ if name, ok := unparen(expr.Fun).(*syntax.Name); ok && tv.IsBuiltin() {
+ switch name.Value {
+ case "make":
+ assert(len(expr.ArgList) >= 1)
+ assert(!expr.HasDots)
+
+ w.Code(exprMake)
+ w.pos(expr)
+ w.exprType(nil, expr.ArgList[0], false)
+ w.exprs(expr.ArgList[1:])
+ return
+
+ case "new":
+ assert(len(expr.ArgList) == 1)
+ assert(!expr.HasDots)
+
+ w.Code(exprNew)
+ w.pos(expr)
+ w.exprType(nil, expr.ArgList[0], false)
+ return
+ }
+ }
+
writeFunExpr := func() {
if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok {
if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
w.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)
}
}
+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)
+ }
+ }
+ 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)
+ // 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 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:
+ 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)
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) {
}
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 {
w.typInfo(info)
}
+// 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
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
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
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
}
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")
}
}
}
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
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
+}
+
// recvBase returns the base type for the given receiver parameter.
func recvBase(recv *types2.Var) *types2.Named {
typ := recv.Type()
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reflectdata
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func hasRType(n, rtype ir.Node, fieldName string, required bool) bool {
+ if rtype != nil {
+ return true
+ }
+ if base.Debug.Unified != 0 && required {
+ base.FatalfAt(n.Pos(), "missing %s: %+v", fieldName, n)
+ }
+ return false
+}
+
+// assertOp asserts that n is an op.
+func assertOp(n ir.Node, op ir.Op) {
+ base.AssertfAt(n.Op() == op, n.Pos(), "want %v, have %v", op, n)
+}
+
+// assertOp2 asserts that n is an op1 or op2.
+func assertOp2(n ir.Node, op1, op2 ir.Op) {
+ base.AssertfAt(n.Op() == op1 || n.Op() == op2, n.Pos(), "want %v or %v, have %v", op1, op2, n)
+}
+
+// kindRType asserts that typ has the given kind, and returns an
+// expression that yields the *runtime._type value representing typ.
+func kindRType(pos src.XPos, typ *types.Type, k types.Kind) ir.Node {
+ base.AssertfAt(typ.Kind() == k, pos, "want %v type, have %v", k, typ)
+ return TypePtrAt(pos, typ)
+}
+
+// mapRType asserts that typ is a map type, and returns an expression
+// that yields the *runtime._type value representing typ.
+func mapRType(pos src.XPos, typ *types.Type) ir.Node {
+ return kindRType(pos, typ, types.TMAP)
+}
+
+// chanRType asserts that typ is a map type, and returns an expression
+// that yields the *runtime._type value representing typ.
+func chanRType(pos src.XPos, typ *types.Type) ir.Node {
+ return kindRType(pos, typ, types.TCHAN)
+}
+
+// sliceElemRType asserts that typ is a slice type, and returns an
+// expression that yields the *runtime._type value representing typ's
+// element type.
+func sliceElemRType(pos src.XPos, typ *types.Type) ir.Node {
+ base.AssertfAt(typ.IsSlice(), pos, "want slice type, have %v", typ)
+ return TypePtrAt(pos, typ.Elem())
+}
+
+// concreteRType asserts that typ is not an interface type, and
+// returns an expression that yields the *runtime._type value
+// representing typ.
+func concreteRType(pos src.XPos, typ *types.Type) ir.Node {
+ base.AssertfAt(!typ.IsInterface(), pos, "want non-interface type, have %v", typ)
+ return TypePtrAt(pos, typ)
+}
+
+// AppendElemRType asserts that n is an "append" operation, and
+// returns an expression that yields the *runtime._type value
+// representing the result slice type's element type.
+func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node {
+ assertOp(n, ir.OAPPEND)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return sliceElemRType(pos, n.Type())
+}
+
+// CompareRType asserts that n is a comparison (== or !=) operation
+// between expressions of interface and non-interface type, and
+// returns an expression that yields the *runtime._type value
+// representing the non-interface type.
+func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node {
+ assertOp2(n, ir.OEQ, ir.ONE)
+ base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ typ := n.X.Type()
+ if typ.IsInterface() {
+ typ = n.Y.Type()
+ }
+ return concreteRType(pos, typ)
+}
+
+// ConvIfaceTypeWord asserts that n is conversion to interface type,
+// and returns an expression that yields the *runtime._type or
+// *runtime.itab value necessary for implementing the conversion.
+//
+// - *runtime._type for the destination type, for I2I conversions
+// - *runtime.itab, for T2I conversions
+// - *runtime._type for the source type, for T2E conversions
+func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node {
+ assertOp(n, ir.OCONVIFACE)
+ src, dst := n.X.Type(), n.Type()
+ base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n)
+ // TODO(mdempsky): Need to handle implicit interface conversions.
+ if hasRType(n, n.TypeWord, "TypeWord", false) {
+ return n.TypeWord
+ }
+ if dst.IsEmptyInterface() {
+ return concreteRType(pos, src) // direct eface construction
+ }
+ if !src.IsInterface() {
+ return ITabAddr(src, dst) // direct iface construction
+ }
+ return TypePtrAt(pos, dst) // convI2I
+}
+
+// ConvIfaceSrcRType asserts that n is a conversion from
+// non-interface type to interface type (or OCONVIDATA operation), and
+// returns an expression that yields the *runtime._type for copying
+// the convertee value to the heap.
+func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node {
+ assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA)
+ // TODO(mdempsky): Need to handle implicit interface conversions.
+ if hasRType(n, n.SrcRType, "SrcRType", false) {
+ return n.SrcRType
+ }
+ return concreteRType(pos, n.X.Type())
+}
+
+// CopyElemRType asserts that n is a "copy" operation, and returns an
+// expression that yields the *runtime._type value representing the
+// destination slice type's element type.
+func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node {
+ assertOp(n, ir.OCOPY)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return sliceElemRType(pos, n.X.Type())
+}
+
+// DeleteMapRType asserts that n is a "delete" operation, and returns
+// an expression that yields the *runtime._type value representing the
+// map type.
+func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node {
+ assertOp(n, ir.ODELETE)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return mapRType(pos, n.Args[0].Type())
+}
+
+// IndexMapRType asserts that n is a map index operation, and returns
+// an expression that yields the *runtime._type value representing the
+// map type.
+func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node {
+ assertOp(n, ir.OINDEXMAP)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return mapRType(pos, n.X.Type())
+}
+
+// MakeChanRType asserts that n is a "make" operation for a channel
+// type, and returns an expression that yields the *runtime._type
+// value representing that channel type.
+func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node {
+ assertOp(n, ir.OMAKECHAN)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return chanRType(pos, n.Type())
+}
+
+// MakeMapRType asserts that n is a "make" operation for a map type,
+// and returns an expression that yields the *runtime._type value
+// representing that map type.
+func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node {
+ assertOp(n, ir.OMAKEMAP)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return mapRType(pos, n.Type())
+}
+
+// MakeSliceElemRType asserts that n is a "make" operation for a slice
+// type, and returns an expression that yields the *runtime._type
+// value representing that slice type's element type.
+func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node {
+ assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return sliceElemRType(pos, n.Type())
+}
+
+// RangeMapRType asserts that n is a "range" loop over a map value,
+// and returns an expression that yields the *runtime._type value
+// representing that map type.
+func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node {
+ assertOp(n, ir.ORANGE)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return mapRType(pos, n.X.Type())
+}
+
+// UnsafeSliceElemRType asserts that n is an "unsafe.Slice" operation,
+// and returns an expression that yields the *runtime._type value
+// representing the result slice type's element type.
+func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node {
+ assertOp(n, ir.OUNSAFESLICE)
+ if hasRType(n, n.RType, "RType", true) {
+ return n.RType
+ }
+ return sliceElemRType(pos, n.Type())
+}
return TypeSym(t).Linksym()
}
+// Deprecated: Use TypePtrAt instead.
func TypePtr(t *types.Type) *ir.AddrExpr {
- n := ir.NewLinksymExpr(base.Pos, TypeLinksym(t), types.Types[types.TUINT8])
- return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
+ return TypePtrAt(base.Pos, t)
+}
+
+// TypePtrAt returns an expression that evaluates to the
+// *runtime._type value for t.
+func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr {
+ return typecheck.LinksymAddr(pos, TypeLinksym(t), types.Types[types.TUINT8])
}
// ITabLsym returns the LSym representing the itab for concrete type typ implementing
return lsym
}
-// ITabAddr returns an expression representing a pointer to the itab
-// for concrete type typ implementing interface iface.
+// Deprecated: Use ITabAddrAt instead.
func ITabAddr(typ, iface *types.Type) *ir.AddrExpr {
+ return ITabAddrAt(base.Pos, typ, iface)
+}
+
+// ITabAddrAt returns an expression that evaluates to the
+// *runtime.itab value for concrete type typ implementing interface
+// iface.
+func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
lsym := s.Linksym()
writeITab(lsym, typ, iface, false)
}
- n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
- return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
+ return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
}
// needkeyupdate reports whether map updates with t as a key
// newHeapaddr allocates heap memory for n and sets its heap address.
func (s *state) newHeapaddr(n *ir.Name) {
- s.setHeapaddr(n.Pos(), n, s.newObject(n.Type()))
+ s.setHeapaddr(n.Pos(), n, s.newObject(n.Type(), nil))
}
// setHeapaddr allocates a new PAUTO variable to store ptr (which must be non-nil)
}
// newObject returns an SSA value denoting new(typ).
-func (s *state) newObject(typ *types.Type) *ssa.Value {
+func (s *state) newObject(typ *types.Type, rtype *ssa.Value) *ssa.Value {
if typ.Size() == 0 {
return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb)
}
- return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0]
+ if rtype == nil {
+ rtype = s.reflectType(typ)
+ }
+ return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, rtype)[0]
}
func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) {
if !n.Type().IsPtr() {
s.Fatalf("expected pointer type: %v", n.Type())
}
- elem := n.Type().Elem()
+ elem, rtypeExpr := n.Type().Elem(), n.ElemRType
if count != nil {
if !elem.IsArray() {
s.Fatalf("expected array type: %v", elem)
}
- elem = elem.Elem()
+ elem, rtypeExpr = elem.Elem(), n.ElemElemRType
}
size := elem.Size()
// Casting from larger type to smaller one is ok, so for smallest type, do nothing.
if count.Type.Size() != s.config.PtrSize {
s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize)
}
- s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, s.reflectType(elem), count)
+ var rtype *ssa.Value
+ if rtypeExpr != nil {
+ rtype = s.expr(rtypeExpr)
+ } else {
+ rtype = s.reflectType(elem)
+ }
+ s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, rtype, count)
}
// reflectType returns an SSA value representing a pointer to typ's
// reflection type descriptor.
func (s *state) reflectType(typ *types.Type) *ssa.Value {
+ // TODO(mdempsky): Make this Fatalf under Unified IR; frontend needs
+ // to supply RType expressions.
lsym := reflectdata.TypeLinksym(typ)
return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(types.Types[types.TUINT8]), lsym, s.sb)
}
case ir.ONEW:
n := n.(*ir.UnaryExpr)
- return s.newObject(n.Type().Elem())
+ var rtype *ssa.Value
+ if x, ok := n.X.(*ir.DynamicType); ok && x.Op() == ir.ODYNAMICTYPE {
+ rtype = s.expr(x.RType)
+ }
+ return s.newObject(n.Type().Elem(), rtype)
case ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr)
if n.ITab != nil {
targetItab = s.expr(n.ITab)
}
- return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok)
+ return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, nil, target, targetItab, commaok)
}
func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, resok *ssa.Value) {
iface := s.expr(n.X)
- var target, targetItab *ssa.Value
+ var source, target, targetItab *ssa.Value
+ if n.SrcRType != nil {
+ source = s.expr(n.SrcRType)
+ }
if !n.X.Type().IsEmptyInterface() && !n.Type().IsInterface() {
byteptr := s.f.Config.Types.BytePtr
targetItab = s.expr(n.ITab)
} else {
target = s.expr(n.RType)
}
- return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok)
+ return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, source, target, targetItab, commaok)
}
// dottype1 implements a x.(T) operation. iface is the argument (x), dst is the type we're asserting to (T)
// and src is the type we're asserting from.
+// source is the *runtime._type of src
// target is the *runtime._type of dst.
// If src is a nonempty interface and dst is not an interface, targetItab is an itab representing (dst, src). Otherwise it is nil.
// commaok is true if the caller wants a boolean success value. Otherwise, the generated code panics if the conversion fails.
-func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) {
+func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) {
byteptr := s.f.Config.Types.BytePtr
if dst.IsInterface() {
if dst.IsEmptyInterface() {
if !commaok {
// on failure, panic by calling panicdottype
s.startBlock(bFail)
- taddr := s.reflectType(src)
+ taddr := source
+ if taddr == nil {
+ taddr = s.reflectType(src)
+ }
if src.IsEmptyInterface() {
s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr)
} else {
w.string(exportPath(pkg))
if mainIndex {
w.string(pkg.Name)
- w.uint64(uint64(pkg.Height))
+ w.uint64(0) // was package height, but not necessary anymore.
}
// Sort symbols within a package by name.
w.pos(n.Pos())
w.typ(n.Type())
w.expr(n.X)
+ w.bool(n.Implicit())
case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC:
n := n.(*ir.UnaryExpr)
for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
pkg := p.pkgAt(ird.uint64())
pkgName := p.stringAt(ird.uint64())
- pkgHeight := int(ird.uint64())
+ _ = int(ird.uint64()) // was package height, but not necessary anymore.
if pkg.Name == "" {
pkg.Name = pkgName
- pkg.Height = pkgHeight
types.NumImport[pkgName]++
// TODO(mdempsky): This belongs somewhere else.
if pkg.Name != pkgName {
base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
}
- if pkg.Height != pkgHeight {
- base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
- }
}
for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
return n
case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
- return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
+ n := ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
+ n.SetImplicit(r.bool())
+ return n
case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, ir.OUNSAFEADD, ir.OUNSAFESLICE:
pos := r.pos()
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
+ "cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
)
}
}
+// LinksymAddr returns a new expression that evaluates to the address
+// of lsym. typ specifies the type of the addressed memory.
+func LinksymAddr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *ir.AddrExpr {
+ n := ir.NewLinksymExpr(pos, lsym, typ)
+ return Expr(NodAddrAt(pos, n)).(*ir.AddrExpr)
+}
+
func NodNil() ir.Node {
n := ir.NewNilExpr(base.Pos)
n.SetType(types.Types[types.TNIL])
n = convlit1(n, t, false, context)
if n.Type() == nil {
- return n
+ base.Fatalf("cannot assign %v to %v", n, t)
+ }
+ if n.Type().IsUntyped() {
+ base.Fatalf("%L has untyped type", n)
}
if t.Kind() == types.TBLANK {
return n
}
-
- // Convert ideal bool from comparison to plain bool
- // if the next step is non-bool (like interface{}).
- if n.Type() == types.UntypedBool && !t.IsBoolean() {
- if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL {
- r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n)
- r.SetType(types.Types[types.TBOOL])
- r.SetTypecheck(1)
- r.SetImplicit(true)
- n = r
- }
- }
-
if types.Identical(n.Type(), t) {
return n
}
// pkgMap maps a package path to a package.
var pkgMap = make(map[string]*Pkg)
-// MaxPkgHeight is a height greater than any likely package height.
-const MaxPkgHeight = 1e9
-
type Pkg struct {
Path string // string literal used in import statement, e.g. "runtime/internal/sys"
Name string // package name, e.g. "sys"
Syms map[string]*Sym
Pathsym *obj.LSym
- // Height is the package's height in the import graph. Leaf
- // packages (i.e., packages with no imports) have height 0,
- // and all other packages have height 1 plus the maximum
- // height of their imported packages.
- Height int
-
Direct bool // imported directly
}
// Less reports whether symbol a is ordered before symbol b.
//
// Symbols are ordered exported before non-exported, then by name, and
-// finally (for non-exported symbols) by package height and path.
-//
-// Ordering by package height is necessary to establish a consistent
-// ordering for non-exported names with the same spelling but from
-// different packages. We don't necessarily know the path for the
-// package being compiled, but by definition it will have a height
-// greater than any other packages seen within the compilation unit.
-// For more background, see issue #24693.
+// finally (for non-exported symbols) by package path.
func (a *Sym) Less(b *Sym) bool {
if a == b {
return false
return a.Name < b.Name
}
if !ea {
- if a.Pkg.Height != b.Pkg.Height {
- return a.Pkg.Height < b.Pkg.Height
- }
return a.Pkg.Path < b.Pkg.Path
}
return false
//
// Objects are ordered nil before non-nil, exported before
// non-exported, then by name, and finally (for non-exported
-// functions) by package height and path.
+// functions) by package path.
func (a *object) less(b *object) bool {
if a == b {
return false
return a.name < b.name
}
if !ea {
- if a.pkg.height != b.pkg.height {
- return a.pkg.height < b.pkg.height
- }
return a.pkg.path < b.pkg.path
}
name string
scope *Scope
imports []*Package
- height int
complete bool
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
// NewPackage returns a new Package for the given package path and name.
// The package is not complete and contains no explicit imports.
func NewPackage(path, name string) *Package {
- return NewPackageHeight(path, name, 0)
-}
-
-// NewPackageHeight is like NewPackage, but allows specifying the
-// package's height.
-func NewPackageHeight(path, name string, height int) *Package {
scope := NewScope(Universe, nopos, nopos, fmt.Sprintf("package %q", path))
- return &Package{path: path, name: name, scope: scope, height: height}
+ return &Package{path: path, name: name, scope: scope}
}
// Path returns the package path.
// Name returns the package name.
func (pkg *Package) Name() string { return pkg.name }
-// Height returns the package height.
-func (pkg *Package) Height() int { return pkg.height }
-
// SetName sets the package name.
func (pkg *Package) SetName(name string) { pkg.name = name }
// methods with receiver base type names.
func (check *Checker) collectObjects() {
pkg := check.pkg
- pkg.height = 0
// pkgImports is the set of packages already imported by any package file seen
// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
continue
}
- if imp == Unsafe {
- // typecheck ignores imports of package unsafe for
- // calculating height.
- // TODO(mdempsky): Revisit this. This seems fine, but I
- // don't remember explicitly considering this case.
- } else if h := imp.height + 1; h > pkg.height {
- pkg.height = h
- }
-
// local name overrides imported package name
name := imp.name
if s.LocalPkgName != nil {
// Misc
{Scope{}, 60, 104},
- {Package{}, 40, 80},
+ {Package{}, 36, 72},
{_TypeSet{}, 28, 56},
}
}
as.Y = r
if r.Op() == ir.OAPPEND {
+ r := r.(*ir.CallExpr)
// Left in place for back end.
// Do not add a new write barrier.
// Set up address of type for back end.
- r.(*ir.CallExpr).X = reflectdata.TypePtr(r.Type().Elem())
+ r.X = reflectdata.AppendElemRType(base.Pos, r)
return as
}
// Otherwise, lowered for race detector.
var call *ir.CallExpr
if w := t.Elem().Size(); w <= zeroValSize {
fn := mapfn(mapaccess2[fast], t, false)
- call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key)
+ call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key)
} else {
fn := mapfn("mapaccess2_fat", t, true)
z := reflectdata.ZeroAddr(w)
- call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key, z)
+ call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key, z)
}
// mapaccess2* returns a typed bool, but due to spec changes,
fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
// s = growslice(T, s, n)
- nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), s, nn))}
nodes.Append(nif)
// s = s[:n]
fn = typecheck.SubstArgTypes(fn, l1.Type().Elem(), l2.Type().Elem())
ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes))
ptr2, len2 := backingArrayPtrLen(l2)
- ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.TypePtr(elemtype), ptr1, len1, ptr2, len2)
+ ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.AppendElemRType(base.Pos, n), ptr1, len1, ptr2, len2)
} else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime {
// rely on runtime to instrument:
// copy(s[len(l1):], l2)
fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
// s = growslice(T, s, n)
- nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), s, nn))}
nodes = append(nodes, nif)
// s = s[:n]
fn := typecheck.LookupRuntime("growslice") // growslice(<type>, old []T, mincap int) (ret []T)
fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem())
- nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns,
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.AppendElemRType(base.Pos, n), ns,
ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))}
l = append(l, nif)
ptrL, lenL := backingArrayPtrLen(n.X)
n.Y = cheapExpr(n.Y, init)
ptrR, lenR := backingArrayPtrLen(n.Y)
- return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR)
+ return mkcall1(fn, n.Type(), init, reflectdata.CopyElemRType(base.Pos, n), ptrL, lenL, ptrR, lenR)
}
if runtimecall {
t := map_.Type()
fast := mapfast(t)
key = mapKeyArg(fast, n, key, false)
- return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key)
+ return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.DeleteMapRType(base.Pos, n), map_, key)
}
// walkLenCap walks an OLEN or OCAP node.
argtype = types.Types[types.TINT]
}
- return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype))
+ return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.MakeChanRType(base.Pos, n), typecheck.Conv(size, argtype))
}
// walkMakeMap walks an OMAKEMAP node.
fn := typecheck.LookupRuntime(fnname)
fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem())
- return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h)
+ return mkcall1(fn, n.Type(), init, reflectdata.MakeMapRType(base.Pos, n), typecheck.Conv(hint, argtype), h)
}
// walkMakeSlice walks an OMAKESLICE node.
argtype = types.Types[types.TINT]
}
fn := typecheck.LookupRuntime(fnname)
- ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype))
+ ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype))
ptr.MarkNonNil()
len = typecheck.Conv(len, types.Types[types.TINT])
cap = typecheck.Conv(cap, types.Types[types.TINT])
// Replace make+copy with runtime.makeslicecopy.
// instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
fn := typecheck.LookupRuntime("makeslicecopy")
- ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR]))
+ ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR]))
ptr.MarkNonNil()
sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length)
return walkExpr(typecheck.Expr(sh), init)
if ir.ShouldCheckPtr(ir.CurFunc, 1) {
fnname := "unsafeslicecheckptr"
fn := typecheck.LookupRuntime(fnname)
- init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(sliceType.Elem()), unsafePtr, typecheck.Conv(len, lenType)))
+ init.Append(mkcall1(fn, nil, init, reflectdata.UnsafeSliceElemRType(base.Pos, n), unsafePtr, typecheck.Conv(len, lenType)))
} else {
// Otherwise, open code unsafe.Slice to prevent runtime call overhead.
// Keep this code in sync with runtime.unsafeslice{,64}
// Given mixed interface/concrete comparison,
// rewrite into types-equal && data-equal.
// This is efficient, avoids allocations, and avoids runtime calls.
+ //
+ // TODO(mdempsky): It would be more general and probably overall
+ // simpler to just extend walkCompareInterface to optimize when one
+ // operand is an OCONVIFACE.
if n.X.Type().IsInterface() != n.Y.Type().IsInterface() {
// Preserve side-effects in case of short-circuiting; see #32187.
l := cheapExpr(n.X, init)
// l.tab == type(r)
// For non-empty interface, this is:
// l.tab != nil && l.tab._type == type(r)
+ //
+ // TODO(mdempsky): For non-empty interface comparisons, just
+ // compare against the itab address directly?
var eqtype ir.Node
tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, l)
- rtyp := reflectdata.TypePtr(r.Type())
+ rtyp := reflectdata.CompareRType(base.Pos, n)
if l.Type().IsEmptyInterface() {
tab.SetType(types.NewPtr(types.Types[types.TUINT8]))
tab.SetTypecheck(1)
func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) {
// make the map var
- a := ir.NewCallExpr(base.Pos, ir.OMAKE, nil, nil)
+ args := []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))}
+ a := typecheck.Expr(ir.NewCallExpr(base.Pos, ir.OMAKE, nil, args)).(*ir.MakeExpr)
+ a.RType = n.RType
a.SetEsc(n.Esc())
- a.Args = []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))}
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, m, a))
entries := n.List
kidx := ir.NewIndexExpr(base.Pos, vstatk, i)
kidx.SetBounded(true)
- lhs := ir.NewIndexExpr(base.Pos, m, kidx)
+
+ // typechecker rewrites OINDEX to OINDEXMAP
+ lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, kidx)).(*ir.IndexExpr)
+ base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs)
+ lhs.RType = n.RType
zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem()))
incr := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
var body ir.Node = ir.NewAssignStmt(base.Pos, lhs, rhs)
- body = typecheck.Stmt(body) // typechecker rewrites OINDEX to OINDEXMAP
+ body = typecheck.Stmt(body)
body = orderStmtInPlace(body, map[string][]*ir.Name{})
loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil)
appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpelem, elem))
ir.SetPos(tmpelem)
- var a ir.Node = ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, tmpkey), tmpelem)
- a = typecheck.Stmt(a) // typechecker rewrites OINDEX to OINDEXMAP
+
+ // typechecker rewrites OINDEX to OINDEXMAP
+ lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, tmpkey)).(*ir.IndexExpr)
+ base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs)
+ lhs.RType = n.RType
+
+ var a ir.Node = ir.NewAssignStmt(base.Pos, lhs, tmpelem)
+ a = typecheck.Stmt(a)
a = orderStmtInPlace(a, map[string][]*ir.Name{})
appendWalkStmt(init, a)
}
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/internal/src"
"cmd/internal/sys"
)
}
if !fromType.IsInterface() {
- var typeWord ir.Node
- if toType.IsEmptyInterface() {
- typeWord = reflectdata.TypePtr(fromType)
- } else {
- typeWord = reflectdata.ITabAddr(fromType, toType)
- }
- l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone))
+ typeWord := reflectdata.ConvIfaceTypeWord(base.Pos, n)
+ l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n, init))
l.SetType(toType)
l.SetTypecheck(n.Typecheck())
return l
fn := typecheck.LookupRuntime("convI2I")
types.CalcSize(fn.Type())
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
- call.Args = []ir.Node{reflectdata.TypePtr(toType), itab}
+ call.Args = []ir.Node{reflectdata.ConvIfaceTypeWord(base.Pos, n), itab}
typeWord = walkExpr(typecheck.Expr(call), init)
}
return e
}
-// Returns the data word (the second word) used to represent n in an interface.
-// n must not be of interface type.
-// esc describes whether the result escapes.
-func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
+// Returns the data word (the second word) used to represent conv.X in
+// an interface.
+func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ pos, n := conv.Pos(), conv.X
fromType := n.Type()
// If it's a pointer, it is its own representation.
case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
// n is a readonly global; use it directly.
value = n
- case !escapes && fromType.Size() <= 1024:
+ case conv.Esc() == ir.EscNone && fromType.Size() <= 1024:
// n does not escape. Use a stack temporary initialized to n.
value = typecheck.Temp(fromType)
init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
n = copyExpr(n, fromType, init)
}
fn = typecheck.SubstArgTypes(fn, fromType)
- args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)}
+ args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)}
} else {
// Use a specialized conversion routine that takes the type being
// converted by value, not by pointer.
// walkConvIData walks an OCONVIDATA node.
func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
n.X = walkExpr(n.X, init)
- return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)
+ return dataWord(n, init)
}
// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
t := map_.Type()
fast := mapfast(t)
key := mapKeyArg(fast, n, n.Index, n.Assigned)
- args := []ir.Node{reflectdata.TypePtr(t), map_, key}
+ args := []ir.Node{reflectdata.IndexMapRType(base.Pos, n), map_, key}
var mapFn ir.Node
switch {
do(0, recv.X.Type().Elem())
do(1, types.Types[types.TBOOL])
if len(init) != 0 {
- ir.DumpList("ninit", r.Init())
+ ir.DumpList("ninit", init)
base.Fatalf("ninit on select recv")
}
orderBlock(ncas.PtrInit(), o.free)
// Emit eval+insert of dynamic entries, one at a time.
for _, r := range dynamics {
- as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
- typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP
+ lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr)
+ base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs)
+ lhs.RType = n.RType
+
+ as := ir.NewAssignStmt(base.Pos, lhs, r.Value)
+ typecheck.Stmt(as)
o.stmt(as)
}
// the returned node.
func walkRange(nrange *ir.RangeStmt) ir.Node {
if isMapClear(nrange) {
- m := nrange.X
- lno := ir.SetPos(m)
- n := mapClear(m)
- base.Pos = lno
- return n
+ return mapClear(nrange)
}
nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil)
// for v1 := range ha { body }
if v2 == nil {
- body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)}
+ body = []ir.Node{rangeAssign(nrange, hv1)}
break
}
// v1, v2 = hv1, ha[hv1]
tmp := ir.NewIndexExpr(base.Pos, ha, hv1)
tmp.SetBounded(true)
- // Use OAS2 to correctly handle assignments
- // of the form "v1, a[v1] := range".
- a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, tmp})
- body = []ir.Node{a}
+ body = []ir.Node{rangeAssign2(nrange, hv1, tmp)}
break
}
tmp.SetBounded(true)
init = append(init, ir.NewAssignStmt(base.Pos, hp, typecheck.NodAddr(tmp)))
- // Use OAS2 to correctly handle assignments
- // of the form "v1, a[v1] := range".
- a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, ir.NewStarExpr(base.Pos, hp)})
+ a := rangeAssign2(nrange, hv1, ir.NewStarExpr(base.Pos, hp))
body = append(body, a)
// Advance pointer as part of the late increment.
fn := typecheck.LookupRuntime("mapiterinit")
fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), th)
- init = append(init, mkcallstmt1(fn, reflectdata.TypePtr(t), ha, typecheck.NodAddr(hit)))
+ init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit)))
nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil())
fn = typecheck.LookupRuntime("mapiternext")
if v1 == nil {
body = nil
} else if v2 == nil {
- body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, key)}
+ body = []ir.Node{rangeAssign(nrange, key)}
} else {
elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym))
- a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{key, elem})
- body = []ir.Node{a}
+ body = []ir.Node{rangeAssign2(nrange, key, elem)}
}
case types.TCHAN:
if v1 == nil {
body = nil
} else {
- body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)}
+ body = []ir.Node{rangeAssign(nrange, hv1)}
}
// Zero hv1. This prevents hv1 from being the sole, inaccessible
// reference to an otherwise GC-able value during the next channel receive.
if v1 != nil {
if v2 != nil {
// v1, v2 = hv1t, hv2
- a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1t, hv2})
- body = append(body, a)
+ body = append(body, rangeAssign2(nrange, hv1t, hv2))
} else {
// v1 = hv1t
- body = append(body, ir.NewAssignStmt(base.Pos, v1, hv1t))
+ body = append(body, rangeAssign(nrange, hv1t))
}
}
}
return n
}
+// rangeAssign returns "n.Key = key".
+func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node {
+ // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go.
+ return ir.NewAssignStmt(n.Pos(), n.Key, key)
+}
+
+// rangeAssign2 returns "n.Key, n.Value = key, value".
+func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node {
+ // Use OAS2 to correctly handle assignments
+ // of the form "v1, a[v1] = range".
+ // TODO(mdempsky): Implicit conversions for test/typeparam/mdempsky/17.go.
+ return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value})
+}
+
// isMapClear checks if n is of the form:
//
// for k := range m {
}
// mapClear constructs a call to runtime.mapclear for the map m.
-func mapClear(m ir.Node) ir.Node {
+func mapClear(nrange *ir.RangeStmt) ir.Node {
+ m := nrange.X
+ origPos := ir.SetPos(m)
+ defer func() { base.Pos = origPos }()
+
t := m.Type()
// instantiate mapclear(typ *type, hmap map[any]any)
fn := typecheck.LookupRuntime("mapclear")
fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem())
- n := mkcallstmt1(fn, reflectdata.TypePtr(t), m)
+ n := mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), m)
return walkStmt(typecheck.Stmt(n))
}
defaultGoto = jmp
}
- for _, n1 := range ncase.List {
- s.Add(ncase.Pos(), n1, jmp)
+ for i, n1 := range ncase.List {
+ var rtype ir.Node
+ if i < len(ncase.RTypes) {
+ rtype = ncase.RTypes[i]
+ }
+ s.Add(ncase.Pos(), n1, rtype, jmp)
}
// Process body.
type exprClause struct {
pos src.XPos
lo, hi ir.Node
+ rtype ir.Node // *runtime._type for OEQ node
jmp ir.Node
}
-func (s *exprSwitch) Add(pos src.XPos, expr, jmp ir.Node) {
- c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
+func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) {
+ c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp}
if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL {
s.clauses = append(s.clauses, c)
return
// Add length case to outer switch.
cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run)))
jmp := ir.NewBranchStmt(pos, ir.OGOTO, label)
- outer.Add(pos, cas, jmp)
+ outer.Add(pos, cas, nil, jmp)
}
s.done.Append(ir.NewLabelStmt(s.pos, outerLabel))
outer.Emit(&s.done)
}
}
- return ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
+ n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
+ n.RType = c.rtype
+ return n
}
func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
}
name := r.String()
- height := r.Len()
- // Was: "pkg := types.NewPackageHeight(path, name, height)"
- pkg, _ := types.NewPackage(path, name), height
+ pkg := types.NewPackage(path, name)
r.p.imports[path] = pkg
imports := make([]*types.Package, r.Len())
SyncExprs
SyncExpr
SyncExprType
+ SyncAssign
SyncOp
SyncFuncLit
SyncCompLit
SyncStmtsEnd
SyncLabel
SyncOptLabel
+
+ SyncMultiExpr
)
_ = x[SyncExprs-35]
_ = x[SyncExpr-36]
_ = x[SyncExprType-37]
- _ = x[SyncOp-38]
- _ = x[SyncFuncLit-39]
- _ = x[SyncCompLit-40]
- _ = x[SyncDecl-41]
- _ = x[SyncFuncBody-42]
- _ = x[SyncOpenScope-43]
- _ = x[SyncCloseScope-44]
- _ = x[SyncCloseAnotherScope-45]
- _ = x[SyncDeclNames-46]
- _ = x[SyncDeclName-47]
- _ = x[SyncStmts-48]
- _ = x[SyncBlockStmt-49]
- _ = x[SyncIfStmt-50]
- _ = x[SyncForStmt-51]
- _ = x[SyncSwitchStmt-52]
- _ = x[SyncRangeStmt-53]
- _ = x[SyncCaseClause-54]
- _ = x[SyncCommClause-55]
- _ = x[SyncSelectStmt-56]
- _ = x[SyncDecls-57]
- _ = x[SyncLabeledStmt-58]
- _ = x[SyncUseObjLocal-59]
- _ = x[SyncAddLocal-60]
- _ = x[SyncLinkname-61]
- _ = x[SyncStmt1-62]
- _ = x[SyncStmtsEnd-63]
- _ = x[SyncLabel-64]
- _ = x[SyncOptLabel-65]
+ _ = x[SyncAssign-38]
+ _ = x[SyncOp-39]
+ _ = x[SyncFuncLit-40]
+ _ = x[SyncCompLit-41]
+ _ = x[SyncDecl-42]
+ _ = x[SyncFuncBody-43]
+ _ = x[SyncOpenScope-44]
+ _ = x[SyncCloseScope-45]
+ _ = x[SyncCloseAnotherScope-46]
+ _ = x[SyncDeclNames-47]
+ _ = x[SyncDeclName-48]
+ _ = x[SyncStmts-49]
+ _ = x[SyncBlockStmt-50]
+ _ = x[SyncIfStmt-51]
+ _ = x[SyncForStmt-52]
+ _ = x[SyncSwitchStmt-53]
+ _ = x[SyncRangeStmt-54]
+ _ = x[SyncCaseClause-55]
+ _ = x[SyncCommClause-56]
+ _ = x[SyncSelectStmt-57]
+ _ = x[SyncDecls-58]
+ _ = x[SyncLabeledStmt-59]
+ _ = x[SyncUseObjLocal-60]
+ _ = x[SyncAddLocal-61]
+ _ = x[SyncLinkname-62]
+ _ = x[SyncStmt1-63]
+ _ = x[SyncStmtsEnd-64]
+ _ = x[SyncLabel-65]
+ _ = x[SyncOptLabel-66]
}
-const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprAssertTypeOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel"
+const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel"
-var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 228, 230, 237, 244, 248, 256, 265, 275, 292, 301, 309, 314, 323, 329, 336, 346, 355, 365, 375, 385, 390, 401, 412, 420, 428, 433, 441, 446, 454}
+var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458}
func (i SyncMarker) String() string {
i -= 1
*(&v) = x.(int)
*(&v), *(&ok) = y.(int)
}
- {
- i := 0
- j := 0
- var ok bool
- var x interface{} = i // ERROR "i does not escape"
- var y interface{} = j // ERROR "j does not escape"
-
- sink = x.(int) // ERROR "x.\(int\) escapes to heap"
- sink, *(&ok) = y.(int)
- }
{
i := 0 // ERROR "moved to heap: i"
j := 0 // ERROR "moved to heap: j"
--- /dev/null
+// errorcheck -0 -m -l
+//go:build !goexperiment.unified
+// +build !goexperiment.unified
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package escape
+
+var sink interface{}
+
+func dotTypeEscape2() { // #13805, #15796
+ {
+ i := 0
+ j := 0
+ var ok bool
+ var x interface{} = i // ERROR "i does not escape"
+ var y interface{} = j // ERROR "j does not escape"
+
+ sink = x.(int) // ERROR "x.\(int\) escapes to heap"
+ // BAD: should be "y.\(int\) escapes to heap" too
+ sink, *(&ok) = y.(int)
+ }
+}
--- /dev/null
+// errorcheck -0 -m -l
+//go:build goexperiment.unified
+// +build goexperiment.unified
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package escape
+
+var sink interface{}
+
+func dotTypeEscape2() { // #13805, #15796
+ {
+ i := 0
+ j := 0
+ var ok bool
+ var x interface{} = i // ERROR "i does not escape"
+ var y interface{} = j // ERROR "j does not escape"
+
+ sink = x.(int) // ERROR "x.\(int\) escapes to heap"
+ sink, *(&ok) = y.(int) // ERROR "autotmp_.* escapes to heap"
+ }
+}
import "./a"
func g() {
- h := a.E() // ERROR "inlining call to a.E" "a.I\(a.T\(0\)\) does not escape"
+ h := a.E() // ERROR "inlining call to a.E" "T\(0\) does not escape"
h.M() // ERROR "devirtualizing h.M to a.T"
// BAD: T(0) could be stack allocated.
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type I interface{}
+
+type F func()
+
+type s struct {
+ f F
+}
+
+func NewWithF(f F) *s {
+ return &s{f: f}
+}
+
+func NewWithFuncI(func() I) *s {
+ return &s{}
+}
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package b
+
+import (
+ "./a"
+)
+
+type S struct{}
+
+func (s *S) M1() a.I {
+ return a.NewWithF(s.M2)
+}
+
+func (s *S) M2() {}
--- /dev/null
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import (
+ "./a"
+ "./b"
+)
+
+func f() {
+ a.NewWithFuncI((&b.S{}).M1)
+}
--- /dev/null
+// compiledir
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ignored
func bufferNoEscape4() []byte {
var b bytes.Buffer
- b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" "string\(.*\) escapes to heap"
+ b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" `".+" escapes to heap`
useBuffer(&b)
return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$"
}
return foo() // ERROR "inlining call to q.func1"
}
-func r(z int) int {
- foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
- return x + z
- }
- bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
- return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3"
- return 2*y + x*z
- }(x) // ERROR "inlining call to r.func2.1"
- }
- return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3"
-}
-
func s0(x int) int { // ERROR "can inline s0"
foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
x = x + 1
--- /dev/null
+// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1
+//go:build !goexperiment.unified
+// +build !goexperiment.unified
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+
+func r(z int) int {
+ foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
+ return x + z
+ }
+ bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
+ return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3"
+ return 2*y + x*z
+ }(x) // ERROR "inlining call to r.func2.1"
+ }
+ return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3"
+}
--- /dev/null
+// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1
+//go:build goexperiment.unified
+// +build goexperiment.unified
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+
+func r(z int) int {
+ foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
+ return x + z
+ }
+ bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
+ return x + func(y int) int { // ERROR "can inline r.func2.1"
+ return 2*y + x*z
+ }(x) // ERROR "inlining call to r.func2.1"
+ }
+ return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "can inline r.func3" "inlining call to r.func3"
+}
// errorcheckwithauto -0 -l -live -wb=0 -d=ssa/insert_resched_checks/off
+//go:build (amd64 && goexperiment.regabiargs) || (arm64 && goexperiment.regabiargs)
// +build amd64,goexperiment.regabiargs arm64,goexperiment.regabiargs
// Copyright 2014 The Go Authors. All rights reserved.
printnl()
case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ string$"
printnl()
- case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$"
+ case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to f[ibc]38:( .autotmp_[0-9]+)+$"
printnl()
}
printnl()
func f5(m map[string]struct{}) bool {
// Existence-only map lookups should not generate a nil check
- _, ok := m[""]
+ tmp1, tmp2 := m[""] // ERROR "removed nil check"
+ _, ok := tmp1, tmp2
return ok
}
)
var go118Failures = setOf(
- "typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics
- "typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error
- "typeparam/issue53419.go", // 1.18 compiler mishandles generic selector resolution
+ "typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics
+ "typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error
+ "typeparam/issue53419.go", // 1.18 compiler mishandles generic selector resolution
+ "typeparam/mdempsky/16.go", // 1.18 compiler uses interface shape type in failed type assertions
+ "typeparam/mdempsky/17.go", // 1.18 compiler mishandles implicit conversions from range loops
+ "typeparam/mdempsky/18.go", // 1.18 compiler mishandles implicit conversions in select statements
)
// In all of these cases, the 1.17 compiler reports reasonable errors, but either the
)
var unifiedFailures = setOf(
- "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures
- "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this
- "inline.go", // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures
- "linkname3.go", // unified IR is missing some linkname errors
-
- "fixedbugs/issue42284.go", // prints "T(0) does not escape", but test expects "a.I(a.T(0)) does not escape"
- "fixedbugs/issue7921.go", // prints "… escapes to heap", but test expects "string(…) escapes to heap"
- "typeparam/issue47631.go", // unified IR can handle local type declarations
- "fixedbugs/issue42058a.go", // unified IR doesn't report channel element too large
- "fixedbugs/issue42058b.go", // unified IR doesn't report channel element too large
- "fixedbugs/issue49767.go", // unified IR doesn't report channel element too large
- "fixedbugs/issue49814.go", // unified IR doesn't report array type too large
+ "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures
+ "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this
+
+ "typeparam/issue47631.go", // unified IR can handle local type declarations
)
func setOf(keys ...string) map[string]bool {
--- /dev/null
+// run
+
+// Copyright 2022 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.
+
+// Test that type assertion panics mention the real interface type,
+// not their shape type.
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+)
+
+func main() {
+ // The exact error message isn't important, but it should mention
+ // `main.T`, not `go.shape.int_0`.
+ if have := F[T](); !strings.Contains(have, "interface { T() main.T }") {
+ fmt.Printf("FAIL: unexpected panic message: %q\n", have)
+ }
+}
+
+type T int
+
+func F[T any]() (res string) {
+ defer func() {
+ res = recover().(runtime.Error).Error()
+ }()
+ _ = interface{ T() T }(nil).(T)
+ return
+}
--- /dev/null
+// run
+
+// Copyright 2022 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.
+
+// Test that implicit conversions of derived types to interface type
+// in range loops work correctly.
+
+package main
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func main() {
+ test{"int", "V"}.match(RangeArrayAny[V]())
+ test{"int", "V"}.match(RangeArrayIface[V]())
+ test{"V"}.match(RangeChanAny[V]())
+ test{"V"}.match(RangeChanIface[V]())
+ test{"K", "V"}.match(RangeMapAny[K, V]())
+ test{"K", "V"}.match(RangeMapIface[K, V]())
+ test{"int", "V"}.match(RangeSliceAny[V]())
+ test{"int", "V"}.match(RangeSliceIface[V]())
+}
+
+type test []string
+
+func (t test) match(args ...any) {
+ if len(t) != len(args) {
+ fmt.Printf("FAIL: want %v values, have %v\n", len(t), len(args))
+ return
+ }
+ for i, want := range t {
+ if have := reflect.TypeOf(args[i]).Name(); want != have {
+ fmt.Printf("FAIL: %v: want type %v, have %v\n", i, want, have)
+ }
+ }
+}
+
+type iface interface{ M() int }
+
+type K int
+type V int
+
+func (K) M() int { return 0 }
+func (V) M() int { return 0 }
+
+func RangeArrayAny[V any]() (k, v any) {
+ for k, v = range [...]V{zero[V]()} {
+ }
+ return
+}
+
+func RangeArrayIface[V iface]() (k any, v iface) {
+ for k, v = range [...]V{zero[V]()} {
+ }
+ return
+}
+
+func RangeChanAny[V any]() (v any) {
+ for v = range chanOf(zero[V]()) {
+ }
+ return
+}
+
+func RangeChanIface[V iface]() (v iface) {
+ for v = range chanOf(zero[V]()) {
+ }
+ return
+}
+
+func RangeMapAny[K comparable, V any]() (k, v any) {
+ for k, v = range map[K]V{zero[K](): zero[V]()} {
+ }
+ return
+}
+
+func RangeMapIface[K interface {
+ iface
+ comparable
+}, V iface]() (k, v iface) {
+ for k, v = range map[K]V{zero[K](): zero[V]()} {
+ }
+ return
+}
+
+func RangeSliceAny[V any]() (k, v any) {
+ for k, v = range []V{zero[V]()} {
+ }
+ return
+}
+
+func RangeSliceIface[V iface]() (k any, v iface) {
+ for k, v = range []V{zero[V]()} {
+ }
+ return
+}
+
+func chanOf[T any](elems ...T) chan T {
+ c := make(chan T, len(elems))
+ for _, elem := range elems {
+ c <- elem
+ }
+ close(c)
+ return c
+}
+
+func zero[T any]() (_ T) { return }
--- /dev/null
+// run
+
+// Copyright 2022 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.
+
+// Test that implicit conversions to interface type in a select/case
+// clause are compiled correctly.
+
+package main
+
+import "fmt"
+
+func main() { f[int]() }
+
+func f[T any]() {
+ ch := make(chan T)
+ close(ch)
+
+ var i, ok any
+ select {
+ case i, ok = <-ch:
+ }
+
+ fmt.Printf("%T %T\n", i, ok)
+}