Skip to content

Commit b1b6abb

Browse files
committed
go/ir: emit field and index lvals on demand
This backports 1928cea0f0cc5f74e1840d00b5c809f7ed73402b from x/tools: go/ssa: emit field and index lvals on demand Adds a new lazyAddress construct. This is the same as an *address except it emits a FieldAddr selection, Field selection, or IndexAddr on demand. This fixes issues with ordering on assignment statements. For example, x.f = e panics on x being nil in phase 2 of assignment statements. This change delays the introduction of the FieldAddr for x.f until it is used instead of as a side effect of (*builder).addr. The nil deref panic is from FieldAddr is now after side-effects of evaluating x and e but before the assignment to x.f.
1 parent 3e57c5d commit b1b6abb

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

go/ir/builder.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,16 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {
353353
}
354354
wantAddr := true
355355
v := b.receiver(fn, e.X, wantAddr, escaping, sel, e)
356-
last := len(sel.Index()) - 1
357-
return &address{
358-
addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
359-
expr: e.Sel,
356+
index := sel.Index()[len(sel.Index())-1]
357+
vut := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct)
358+
fld := vut.Field(index)
359+
// Due to the two phases of resolving AssignStmt, a panic from x.f = p()
360+
// when x is nil is required to come after the side-effects of
361+
// evaluating x and p().
362+
emit := func(fn *Function) Value {
363+
return emitFieldSelection(fn, v, index, true, e.Sel)
360364
}
365+
return &lazyAddress{addr: emit, t: fld.Type(), expr: e.Sel}
361366

362367
case *ast.IndexExpr:
363368
var x Value
@@ -411,12 +416,19 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {
411416
panic("unexpected container type in IndexExpr: " + t.String())
412417
}
413418

414-
v := &IndexAddr{
415-
X: x,
416-
Index: b.expr(fn, e.Index),
419+
// Due to the two phases of resolving AssignStmt, a panic from x[i] = p()
420+
// when x is nil or i is out-of-bounds is required to come after the
421+
// side-effects of evaluating x, i and p().
422+
index := b.expr(fn, e.Index)
423+
emit := func(fn *Function) Value {
424+
v := &IndexAddr{
425+
X: x,
426+
Index: index,
427+
}
428+
v.setType(et)
429+
return fn.emit(v, e)
417430
}
418-
v.setType(et)
419-
return &address{addr: fn.emit(v, e), expr: e}
431+
return &lazyAddress{addr: emit, t: deref(et), expr: e}
420432

421433
case *ast.StarExpr:
422434
return &address{addr: b.expr(fn, e.X), expr: e}

go/ir/lvalue.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,40 @@ func (e *element) typ() types.Type {
114114
return e.t
115115
}
116116

117+
// A lazyAddress is an lvalue whose address is the result of an instruction.
118+
// These work like an *address except a new address.address() Value
119+
// is created on each load, store and address call.
120+
// A lazyAddress can be used to control when a side effect (nil pointer
121+
// dereference, index out of bounds) of using a location happens.
122+
type lazyAddress struct {
123+
addr func(fn *Function) Value // emit to fn the computation of the address
124+
t types.Type // type of the location
125+
expr ast.Expr // source syntax of the value (not address) [debug mode]
126+
}
127+
128+
func (l *lazyAddress) load(fn *Function, source ast.Node) Value {
129+
load := emitLoad(fn, l.addr(fn), source)
130+
return load
131+
}
132+
133+
func (l *lazyAddress) store(fn *Function, v Value, source ast.Node) {
134+
store := emitStore(fn, l.addr(fn), v, source)
135+
if l.expr != nil {
136+
// store.Val is v, converted for assignability.
137+
emitDebugRef(fn, l.expr, store.Val, false)
138+
}
139+
}
140+
141+
func (l *lazyAddress) address(fn *Function) Value {
142+
addr := l.addr(fn)
143+
if l.expr != nil {
144+
emitDebugRef(fn, l.expr, addr, true)
145+
}
146+
return addr
147+
}
148+
149+
func (l *lazyAddress) typ() types.Type { return l.t }
150+
117151
// A blank is a dummy variable whose name is "_".
118152
// It is not reified: loads are illegal and stores are ignored.
119153
type blank struct{}

0 commit comments

Comments
 (0)