go.tools/ssa: fix a bug building SSA code for ast.CompositeLit.
Map literals should use the same recursion logic as
struct/array/slice literals to apply an implicit &-operator to
the nested literals when a pointer is wanted.
+ test.
Also:
- ensure we set the source location for all Lookup and
MapUpdate instructions.
- remove obsolete address.object field.
R=gri, crawshaw
CC=golang-dev
https://golang.org/cl/12787048
diff --git a/ssa/builder.go b/ssa/builder.go
index 8b0019f..5cf5639 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -370,7 +370,7 @@
if v == nil {
v = fn.lookup(obj, escaping)
}
- return &address{addr: v, expr: e, object: obj}
+ return &address{addr: v, expr: e}
case *ast.CompositeLit:
t := deref(fn.Pkg.typeOf(e))
@@ -401,9 +401,8 @@
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
last := len(sel.Index()) - 1
return &address{
- addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos()),
- expr: e.Sel,
- object: sel.Obj(),
+ addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos()),
+ expr: e.Sel,
}
}
@@ -422,9 +421,10 @@
et = types.NewPointer(t.Elem())
case *types.Map:
return &element{
- m: b.expr(fn, e.X),
- k: emitConv(fn, b.expr(fn, e.Index), t.Key()),
- t: t.Elem(),
+ m: b.expr(fn, e.X),
+ k: emitConv(fn, b.expr(fn, e.Index), t.Key()),
+ t: t.Elem(),
+ pos: e.Lbrack,
}
default:
panic("unexpected container type in IndexExpr: " + t.String())
@@ -452,21 +452,25 @@
// in an addressable location.
//
func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
- if _, ok := loc.(*address); ok {
- if e, ok := e.(*ast.CompositeLit); ok {
- typ := loc.typ()
- switch typ.Underlying().(type) {
- case *types.Pointer: // implicit & -- possibly escaping
+ if e, ok := e.(*ast.CompositeLit); ok {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
+ if isPointer(loc.typ()) {
ptr := b.addr(fn, e, true).address(fn)
loc.store(fn, ptr) // copy address
return
+ }
+ }
- case *types.Interface:
+ if _, ok := loc.(*address); ok {
+ typ := loc.typ()
+ if _, ok := typ.Underlying().(*types.Interface); ok {
// e.g. var x interface{} = T{...}
// Can't in-place initialize an interface value.
// Fall back to copying.
-
- default:
+ } else {
b.compLit(fn, loc.address(fn), e, typ) // in place
return
}
@@ -1246,13 +1250,13 @@
emitStore(fn, addr, fn.emit(m))
for _, e := range e.Elts {
e := e.(*ast.KeyValueExpr)
- up := &MapUpdate{
- Map: m,
- Key: emitConv(fn, b.expr(fn, e.Key), t.Key()),
- Value: emitConv(fn, b.expr(fn, e.Value), t.Elem()),
- pos: e.Colon,
+ loc := &element{
+ m: m,
+ k: emitConv(fn, b.expr(fn, e.Key), t.Key()),
+ t: t.Elem(),
+ pos: e.Colon,
}
- fn.emit(up)
+ b.exprInPlace(fn, loc, e.Value)
}
case *types.Pointer:
diff --git a/ssa/interp/testdata/coverage.go b/ssa/interp/testdata/coverage.go
index e9dc3d1..682bfb3 100644
--- a/ssa/interp/testdata/coverage.go
+++ b/ssa/interp/testdata/coverage.go
@@ -271,6 +271,10 @@
if v, ok := map[string]string{}["foo5"]; v != "" || ok {
panic("oops")
}
+
+ // Regression test: implicit address-taken struct literal
+ // inside literal map element.
+ _ = map[int]*struct{}{0: {}}
}
type mybool bool
diff --git a/ssa/lvalue.go b/ssa/lvalue.go
index 449d0bc..48ccf8d 100644
--- a/ssa/lvalue.go
+++ b/ssa/lvalue.go
@@ -24,9 +24,8 @@
// An address is an lvalue represented by a true pointer.
type address struct {
addr Value
- starPos token.Pos // source position, if from explicit *addr
- expr ast.Expr // source syntax [debug mode]
- object types.Object // source var, if from *ast.Ident
+ starPos token.Pos // source position, if from explicit *addr
+ expr ast.Expr // source syntax [debug mode]
}
func (a *address) load(fn *Function) Value {
@@ -64,6 +63,7 @@
type element struct {
m, k Value // map or string
t types.Type // map element type or string byte type
+ pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
}
func (e *element) load(fn *Function) Value {
@@ -71,16 +71,19 @@
X: e.m,
Index: e.k,
}
+ l.setPos(e.pos)
l.setType(e.t)
return fn.emit(l)
}
func (e *element) store(fn *Function, v Value) {
- fn.emit(&MapUpdate{
+ up := &MapUpdate{
Map: e.m,
Key: e.k,
Value: emitConv(fn, v, e.t),
- })
+ }
+ up.pos = e.pos
+ fn.emit(up)
}
func (e *element) address(fn *Function) Value {
diff --git a/ssa/ssa.go b/ssa/ssa.go
index c73cc87..4dfe2c6 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -1121,7 +1121,8 @@
// The MapUpdate instruction updates the association of Map[Key] to
// Value.
//
-// Pos() returns the ast.KeyValueExpr.Colon, if explicit in the source.
+// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
+// if explicit in the source.
//
// Example printed form:
// t0[t1] = t2