mirror of
https://github.com/golang/go
synced 2024-11-18 20:54:40 -07:00
go/ssa: fix incorrect SSA code for composite literals
Composite literals are initialized in place where possible, but in cases the initializer expression refers to the variable that is being updated x = T{a: x.a} we must ensure that the RHS is fully evaluated before we execute any stores to x. This means we need to record the sequence of stores in a "store buffer" and execute it only once the entire composite literal has been evaluated. Fixes issue #10127 Change-Id: If94e3b179beb25feea5b298ed43de6a199aaf347 Reviewed-on: https://go-review.googlesource.com/7533 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
ed1e7c5cf2
commit
33fcc815f2
@ -358,7 +358,9 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
|||||||
v = fn.addLocal(t, e.Lbrace)
|
v = fn.addLocal(t, e.Lbrace)
|
||||||
}
|
}
|
||||||
v.Comment = "complit"
|
v.Comment = "complit"
|
||||||
b.compLit(fn, v, e, true) // initialize in place
|
var sb storebuf
|
||||||
|
b.compLit(fn, v, e, true, &sb)
|
||||||
|
sb.emit(fn)
|
||||||
return &address{addr: v, pos: e.Lbrace, expr: e}
|
return &address{addr: v, pos: e.Lbrace, expr: e}
|
||||||
|
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
@ -420,15 +422,39 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
|||||||
panic(fmt.Sprintf("unexpected address expression: %T", e))
|
panic(fmt.Sprintf("unexpected address expression: %T", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// exprInPlace emits to fn code to initialize the lvalue loc with the
|
type store struct {
|
||||||
// value of expression e. If isZero is true, exprInPlace assumes that loc
|
lhs lvalue
|
||||||
// holds the zero value for its type.
|
rhs Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type storebuf struct{ stores []store }
|
||||||
|
|
||||||
|
func (sb *storebuf) store(lhs lvalue, rhs Value) {
|
||||||
|
sb.stores = append(sb.stores, store{lhs, rhs})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *storebuf) emit(fn *Function) {
|
||||||
|
for _, s := range sb.stores {
|
||||||
|
s.lhs.store(fn, s.rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign emits to fn code to initialize the lvalue loc with the value
|
||||||
|
// of expression e. If isZero is true, assign assumes that loc holds
|
||||||
|
// the zero value for its type.
|
||||||
//
|
//
|
||||||
// This is equivalent to loc.store(fn, b.expr(fn, e)) but may
|
// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate
|
||||||
// generate better code in some cases, e.g. for composite literals
|
// better code in some cases, e.g., for composite literals in an
|
||||||
// in an addressable location.
|
// addressable location.
|
||||||
//
|
//
|
||||||
func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr, isZero bool) {
|
// If sb is not nil, assign generates code to evaluate expression e, but
|
||||||
|
// not to update loc. Instead, the necessary stores are appended to the
|
||||||
|
// storebuf sb so that they can be executed later. This allows correct
|
||||||
|
// in-place update of existing variables when the RHS is a composite
|
||||||
|
// literal that may reference parts of the LHS.
|
||||||
|
//
|
||||||
|
func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf) {
|
||||||
|
// Can we initialize it in place?
|
||||||
if e, ok := unparen(e).(*ast.CompositeLit); ok {
|
if e, ok := unparen(e).(*ast.CompositeLit); ok {
|
||||||
// A CompositeLit never evaluates to a pointer,
|
// A CompositeLit never evaluates to a pointer,
|
||||||
// so if the type of the location is a pointer,
|
// so if the type of the location is a pointer,
|
||||||
@ -436,7 +462,12 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr, isZero bool)
|
|||||||
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
|
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
|
||||||
if isPointer(loc.typ()) {
|
if isPointer(loc.typ()) {
|
||||||
ptr := b.addr(fn, e, true).address(fn)
|
ptr := b.addr(fn, e, true).address(fn)
|
||||||
loc.store(fn, ptr) // copy address
|
// copy address
|
||||||
|
if sb != nil {
|
||||||
|
sb.store(loc, ptr)
|
||||||
|
} else {
|
||||||
|
loc.store(fn, ptr)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,14 +478,35 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr, isZero bool)
|
|||||||
// Can't in-place initialize an interface value.
|
// Can't in-place initialize an interface value.
|
||||||
// Fall back to copying.
|
// Fall back to copying.
|
||||||
} else {
|
} else {
|
||||||
|
// x = T{...} or x := T{...}
|
||||||
addr := loc.address(fn)
|
addr := loc.address(fn)
|
||||||
b.compLit(fn, addr, e, isZero) // in place
|
if sb != nil {
|
||||||
|
b.compLit(fn, addr, e, isZero, sb)
|
||||||
|
} else {
|
||||||
|
var sb storebuf
|
||||||
|
b.compLit(fn, addr, e, isZero, &sb)
|
||||||
|
sb.emit(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtle: emit debug ref for aggregate types only;
|
||||||
|
// slice and map are handled by store ops in compLit.
|
||||||
|
switch loc.typ().Underlying().(type) {
|
||||||
|
case *types.Struct, *types.Array:
|
||||||
emitDebugRef(fn, e, addr, true)
|
emitDebugRef(fn, e, addr, true)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.store(fn, b.expr(fn, e)) // copy value
|
|
||||||
|
// simple case: just copy
|
||||||
|
rhs := b.expr(fn, e)
|
||||||
|
if sb != nil {
|
||||||
|
sb.store(loc, rhs)
|
||||||
|
} else {
|
||||||
|
loc.store(fn, rhs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr lowers a single-result expression e to SSA form, emitting code
|
// expr lowers a single-result expression e to SSA form, emitting code
|
||||||
@ -938,7 +990,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
|||||||
fn.addLocalForIdent(id)
|
fn.addLocalForIdent(id)
|
||||||
}
|
}
|
||||||
lval := b.addr(fn, id, false) // non-escaping
|
lval := b.addr(fn, id, false) // non-escaping
|
||||||
b.exprInPlace(fn, lval, spec.Values[i], true)
|
b.assign(fn, lval, spec.Values[i], true, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
case len(spec.Values) == 0:
|
case len(spec.Values) == 0:
|
||||||
@ -973,37 +1025,33 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
|||||||
//
|
//
|
||||||
func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
|
func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
|
||||||
// Side effects of all LHSs and RHSs must occur in left-to-right order.
|
// Side effects of all LHSs and RHSs must occur in left-to-right order.
|
||||||
var lvals []lvalue
|
lvals := make([]lvalue, len(lhss))
|
||||||
for _, lhs := range lhss {
|
isZero := make([]bool, len(lhss))
|
||||||
|
for i, lhs := range lhss {
|
||||||
var lval lvalue = blank{}
|
var lval lvalue = blank{}
|
||||||
if !isBlankIdent(lhs) {
|
if !isBlankIdent(lhs) {
|
||||||
if isDef {
|
if isDef {
|
||||||
if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
|
if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
|
||||||
fn.addNamedLocal(obj)
|
fn.addNamedLocal(obj)
|
||||||
|
isZero[i] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lval = b.addr(fn, lhs, false) // non-escaping
|
lval = b.addr(fn, lhs, false) // non-escaping
|
||||||
}
|
}
|
||||||
lvals = append(lvals, lval)
|
lvals[i] = lval
|
||||||
}
|
}
|
||||||
if len(lhss) == len(rhss) {
|
if len(lhss) == len(rhss) {
|
||||||
// e.g. x, y = f(), g()
|
// Simple assignment: x = f() (!isDef)
|
||||||
if len(lhss) == 1 {
|
// Parallel assignment: x, y = f(), g() (!isDef)
|
||||||
// x = type{...}
|
// or short var decl: x, y := f(), g() (isDef)
|
||||||
// Optimization: in-place construction
|
//
|
||||||
// of composite literals.
|
// In all cases, the RHSs may refer to the LHSs,
|
||||||
b.exprInPlace(fn, lvals[0], rhss[0], false)
|
// so we need a storebuf.
|
||||||
} else {
|
var sb storebuf
|
||||||
// Parallel assignment. All reads must occur
|
for i := range rhss {
|
||||||
// before all updates, precluding exprInPlace.
|
b.assign(fn, lvals[i], rhss[i], isZero[i], &sb)
|
||||||
var rvals []Value
|
|
||||||
for _, rval := range rhss {
|
|
||||||
rvals = append(rvals, b.expr(fn, rval))
|
|
||||||
}
|
|
||||||
for i, lval := range lvals {
|
|
||||||
lval.store(fn, rvals[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
sb.emit(fn)
|
||||||
} else {
|
} else {
|
||||||
// e.g. x, y = pos()
|
// e.g. x, y = pos()
|
||||||
tuple := b.exprN(fn, rhss[0])
|
tuple := b.exprN(fn, rhss[0])
|
||||||
@ -1031,22 +1079,32 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compLit emits to fn code to initialize a composite literal e at
|
// compLit emits to fn code to initialize a composite literal e at
|
||||||
// address addr with type typ, typically allocated by Alloc.
|
// address addr with type typ.
|
||||||
|
//
|
||||||
// Nested composite literals are recursively initialized in place
|
// Nested composite literals are recursively initialized in place
|
||||||
// where possible. If isZero is true, compLit assumes that addr
|
// where possible. If isZero is true, compLit assumes that addr
|
||||||
// holds the zero value for typ.
|
// holds the zero value for typ.
|
||||||
//
|
//
|
||||||
|
// Because the elements of a composite literal may refer to the
|
||||||
|
// variables being updated, as in the second line below,
|
||||||
|
// x := T{a: 1}
|
||||||
|
// x = T{a: x.a}
|
||||||
|
// all the reads must occur before all the writes. Thus all stores to
|
||||||
|
// loc are emitted to the storebuf sb for later execution.
|
||||||
|
//
|
||||||
// A CompositeLit may have pointer type only in the recursive (nested)
|
// A CompositeLit may have pointer type only in the recursive (nested)
|
||||||
// case when the type name is implicit. e.g. in []*T{{}}, the inner
|
// case when the type name is implicit. e.g. in []*T{{}}, the inner
|
||||||
// literal has type *T behaves like &T{}.
|
// literal has type *T behaves like &T{}.
|
||||||
// In that case, addr must hold a T, not a *T.
|
// In that case, addr must hold a T, not a *T.
|
||||||
//
|
//
|
||||||
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool) {
|
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
|
||||||
typ := deref(fn.Pkg.typeOf(e))
|
typ := deref(fn.Pkg.typeOf(e))
|
||||||
switch t := typ.Underlying().(type) {
|
switch t := typ.Underlying().(type) {
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
if !isZero && len(e.Elts) != t.NumFields() {
|
if !isZero && len(e.Elts) != t.NumFields() {
|
||||||
emitMemClear(fn, addr, e.Lbrace)
|
// memclear
|
||||||
|
sb.store(&address{addr, e.Lbrace, nil},
|
||||||
|
zeroValue(fn, deref(addr.Type())))
|
||||||
isZero = true
|
isZero = true
|
||||||
}
|
}
|
||||||
for i, e := range e.Elts {
|
for i, e := range e.Elts {
|
||||||
@ -1071,7 +1129,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
|
|||||||
}
|
}
|
||||||
faddr.setType(types.NewPointer(sf.Type()))
|
faddr.setType(types.NewPointer(sf.Type()))
|
||||||
fn.emit(faddr)
|
fn.emit(faddr)
|
||||||
b.exprInPlace(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero)
|
b.assign(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero, sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *types.Array, *types.Slice:
|
case *types.Array, *types.Slice:
|
||||||
@ -1083,21 +1141,23 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
|
|||||||
alloc := emitNew(fn, at, e.Lbrace)
|
alloc := emitNew(fn, at, e.Lbrace)
|
||||||
alloc.Comment = "slicelit"
|
alloc.Comment = "slicelit"
|
||||||
array = alloc
|
array = alloc
|
||||||
isZero = true
|
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
at = t
|
at = t
|
||||||
array = addr
|
array = addr
|
||||||
}
|
|
||||||
|
|
||||||
if !isZero && int64(len(e.Elts)) != at.Len() {
|
if !isZero && int64(len(e.Elts)) != at.Len() {
|
||||||
emitMemClear(fn, array, e.Lbrace)
|
// memclear
|
||||||
isZero = true
|
sb.store(&address{array, e.Lbrace, nil},
|
||||||
|
zeroValue(fn, deref(array.Type())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var idx *Const
|
var idx *Const
|
||||||
for _, e := range e.Elts {
|
for _, e := range e.Elts {
|
||||||
|
pos := e.Pos()
|
||||||
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||||
idx = b.expr(fn, kv.Key).(*Const)
|
idx = b.expr(fn, kv.Key).(*Const)
|
||||||
|
pos = kv.Colon
|
||||||
e = kv.Value
|
e = kv.Value
|
||||||
} else {
|
} else {
|
||||||
var idxval int64
|
var idxval int64
|
||||||
@ -1112,30 +1172,44 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
|
|||||||
}
|
}
|
||||||
iaddr.setType(types.NewPointer(at.Elem()))
|
iaddr.setType(types.NewPointer(at.Elem()))
|
||||||
fn.emit(iaddr)
|
fn.emit(iaddr)
|
||||||
b.exprInPlace(fn, &address{addr: iaddr, pos: e.Pos(), expr: e}, e, isZero)
|
if t != at { // slice
|
||||||
|
// backing array is unaliased => storebuf not needed.
|
||||||
|
b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, nil)
|
||||||
|
} else {
|
||||||
|
b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, sb)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if t != at { // slice
|
if t != at { // slice
|
||||||
s := &Slice{X: array}
|
s := &Slice{X: array}
|
||||||
s.setPos(e.Lbrace)
|
s.setPos(e.Lbrace)
|
||||||
s.setType(typ)
|
s.setType(typ)
|
||||||
emitStore(fn, addr, fn.emit(s), e.Lbrace)
|
sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, fn.emit(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
|
m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
|
||||||
m.setPos(e.Lbrace)
|
m.setPos(e.Lbrace)
|
||||||
m.setType(typ)
|
m.setType(typ)
|
||||||
emitStore(fn, addr, fn.emit(m), e.Lbrace)
|
fn.emit(m)
|
||||||
for _, e := range e.Elts {
|
for _, e := range e.Elts {
|
||||||
e := e.(*ast.KeyValueExpr)
|
e := e.(*ast.KeyValueExpr)
|
||||||
loc := &element{
|
loc := element{
|
||||||
m: m,
|
m: m,
|
||||||
k: emitConv(fn, b.expr(fn, e.Key), t.Key()),
|
k: emitConv(fn, b.expr(fn, e.Key), t.Key()),
|
||||||
t: t.Elem(),
|
t: t.Elem(),
|
||||||
pos: e.Colon,
|
pos: e.Colon,
|
||||||
}
|
}
|
||||||
b.exprInPlace(fn, loc, e.Value, true)
|
|
||||||
|
// We call assign() only because it takes care
|
||||||
|
// of any &-operation required in the recursive
|
||||||
|
// case, e.g.,
|
||||||
|
// map[int]*struct{}{0: {}} implies &struct{}{}.
|
||||||
|
// In-place update is of course impossible,
|
||||||
|
// and no storebuf is needed.
|
||||||
|
b.assign(fn, &loc, e.Value, true, nil)
|
||||||
}
|
}
|
||||||
|
sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unexpected CompositeLit type: " + t.String())
|
panic("unexpected CompositeLit type: " + t.String())
|
||||||
@ -2231,7 +2305,7 @@ func (p *Package) Build() {
|
|||||||
} else {
|
} else {
|
||||||
lval = blank{}
|
lval = blank{}
|
||||||
}
|
}
|
||||||
b.exprInPlace(init, lval, varinit.Rhs, true)
|
b.assign(init, lval, varinit.Rhs, true, nil)
|
||||||
} else {
|
} else {
|
||||||
// n:1 initialization: var x, y := f()
|
// n:1 initialization: var x, y := f()
|
||||||
tuple := b.exprN(init, varinit.Rhs)
|
tuple := b.exprN(init, varinit.Rhs)
|
||||||
|
@ -430,12 +430,6 @@ func zeroValue(f *Function, t types.Type) Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitMemClear emits to f code to zero the value pointed to by ptr.
|
|
||||||
func emitMemClear(f *Function, ptr Value, pos token.Pos) {
|
|
||||||
// TODO(adonovan): define and use a 'memclr' intrinsic for aggregate types.
|
|
||||||
emitStore(f, ptr, zeroValue(f, deref(ptr.Type())), pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createRecoverBlock emits to f a block of code to return after a
|
// createRecoverBlock emits to f a block of code to return after a
|
||||||
// recovered panic, and sets f.Recover to it.
|
// recovered panic, and sets f.Recover to it.
|
||||||
//
|
//
|
||||||
|
84
go/ssa/interp/testdata/complit.go
vendored
84
go/ssa/interp/testdata/complit.go
vendored
@ -80,5 +80,89 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/golang/go/issues/10127:
|
||||||
|
// composite literal clobbers destination before reading from it.
|
||||||
|
func init() {
|
||||||
|
// map
|
||||||
|
{
|
||||||
|
type M map[string]int
|
||||||
|
m := M{"x": 1, "y": 2}
|
||||||
|
m = M{"x": m["y"], "y": m["x"]}
|
||||||
|
if m["x"] != 2 || m["y"] != 1 {
|
||||||
|
panic(fmt.Sprint(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := M{"x": 3}
|
||||||
|
m, n = M{"x": n["x"]}, M{"x": m["x"]} // parallel assignment
|
||||||
|
if got := fmt.Sprint(m["x"], n["x"]); got != "3 2" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct
|
||||||
|
{
|
||||||
|
type T struct{ x, y, z int }
|
||||||
|
t := T{x: 1, y: 2, z: 3}
|
||||||
|
|
||||||
|
t = T{x: t.y, y: t.z, z: t.x} // all fields
|
||||||
|
if got := fmt.Sprint(t); got != "{2 3 1}" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
t = T{x: t.y, y: t.z + 3} // not all fields
|
||||||
|
if got := fmt.Sprint(t); got != "{3 4 0}" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := T{x: 5, y: 6, z: 7}
|
||||||
|
t, u = T{x: u.x}, T{x: t.x} // parallel assignment
|
||||||
|
if got := fmt.Sprint(t, u); got != "{5 0 0} {3 0 0}" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// array
|
||||||
|
{
|
||||||
|
a := [3]int{0: 1, 1: 2, 2: 3}
|
||||||
|
|
||||||
|
a = [3]int{0: a[1], 1: a[2], 2: a[0]} // all elements
|
||||||
|
if got := fmt.Sprint(a); got != "[2 3 1]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
a = [3]int{0: a[1], 1: a[2] + 3} // not all elements
|
||||||
|
if got := fmt.Sprint(a); got != "[3 4 0]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := [3]int{0: 5, 1: 6, 2: 7}
|
||||||
|
a, b = [3]int{0: b[0]}, [3]int{0: a[0]} // parallel assignment
|
||||||
|
if got := fmt.Sprint(a, b); got != "[5 0 0] [3 0 0]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// slice
|
||||||
|
{
|
||||||
|
s := []int{0: 1, 1: 2, 2: 3}
|
||||||
|
|
||||||
|
s = []int{0: s[1], 1: s[2], 2: s[0]} // all elements
|
||||||
|
if got := fmt.Sprint(s); got != "[2 3 1]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
s = []int{0: s[1], 1: s[2] + 3} // not all elements
|
||||||
|
if got := fmt.Sprint(s); got != "[3 4]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := []int{0: 5, 1: 6, 2: 7}
|
||||||
|
s, t = []int{0: t[0]}, []int{0: s[0]} // parallel assignment
|
||||||
|
if got := fmt.Sprint(s, t); got != "[5] [3]" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ type lvalue interface {
|
|||||||
type address struct {
|
type address struct {
|
||||||
addr Value
|
addr Value
|
||||||
pos token.Pos // source position
|
pos token.Pos // source position
|
||||||
expr ast.Expr // source syntax [debug mode]
|
expr ast.Expr // source syntax of the value (not address) [debug mode]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *address) load(fn *Function) Value {
|
func (a *address) load(fn *Function) Value {
|
||||||
|
10
go/ssa/testdata/valueforexpr.go
vendored
10
go/ssa/testdata/valueforexpr.go
vendored
@ -76,11 +76,11 @@ func complit() {
|
|||||||
// 1. Slices
|
// 1. Slices
|
||||||
print( /*@Slice*/ ([]int{}))
|
print( /*@Slice*/ ([]int{}))
|
||||||
print( /*@Alloc*/ (&[]int{}))
|
print( /*@Alloc*/ (&[]int{}))
|
||||||
print(& /*@Alloc*/ ([]int{}))
|
print(& /*@Slice*/ ([]int{}))
|
||||||
|
|
||||||
sl1 := /*@Slice*/ ([]int{})
|
sl1 := /*@Slice*/ ([]int{})
|
||||||
sl2 := /*@Alloc*/ (&[]int{})
|
sl2 := /*@Alloc*/ (&[]int{})
|
||||||
sl3 := & /*@Alloc*/ ([]int{})
|
sl3 := & /*@Slice*/ ([]int{})
|
||||||
_, _, _ = sl1, sl2, sl3
|
_, _, _ = sl1, sl2, sl3
|
||||||
|
|
||||||
_ = /*@Slice*/ ([]int{})
|
_ = /*@Slice*/ ([]int{})
|
||||||
@ -98,18 +98,18 @@ func complit() {
|
|||||||
_, _, _ = arr1, arr2, arr3
|
_, _, _ = arr1, arr2, arr3
|
||||||
|
|
||||||
_ = /*@UnOp*/ ([1]int{})
|
_ = /*@UnOp*/ ([1]int{})
|
||||||
_ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{})) // & optimized away
|
_ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{}))
|
||||||
_ = & /*@Alloc*/ ([1]int{})
|
_ = & /*@Alloc*/ ([1]int{})
|
||||||
|
|
||||||
// 3. Maps
|
// 3. Maps
|
||||||
type M map[int]int
|
type M map[int]int
|
||||||
print( /*@MakeMap*/ (M{}))
|
print( /*@MakeMap*/ (M{}))
|
||||||
print( /*@Alloc*/ (&M{}))
|
print( /*@Alloc*/ (&M{}))
|
||||||
print(& /*@Alloc*/ (M{}))
|
print(& /*@MakeMap*/ (M{}))
|
||||||
|
|
||||||
m1 := /*@MakeMap*/ (M{})
|
m1 := /*@MakeMap*/ (M{})
|
||||||
m2 := /*@Alloc*/ (&M{})
|
m2 := /*@Alloc*/ (&M{})
|
||||||
m3 := & /*@Alloc*/ (M{})
|
m3 := & /*@MakeMap*/ (M{})
|
||||||
_, _, _ = m1, m2, m3
|
_, _, _ = m1, m2, m3
|
||||||
|
|
||||||
_ = /*@MakeMap*/ (M{})
|
_ = /*@MakeMap*/ (M{})
|
||||||
|
Loading…
Reference in New Issue
Block a user