Fix subscript operand evaluation order

69762c5cc9c940c62ef8eb05a90c2778103fcefb80714b31c653ad997388c468
Subscript lowering evaluated the index expression before the container
expression, contrary to source order. When either operand had side
effects, the index could observe stale state or address a different
container, producing incorrect runtime results.

Lower the container before the index in `lowerElemPtr` so their
effects occur left to right.
Alexis Sellier committed ago 1 parent 70e07e31
lib/std/lang/lower.rad +1 -1
5112 5112
fn lowerElemPtr(
5113 5113
    self: *mut FnLowerer, container: *ast::Node, index: *ast::Node
5114 5114
) -> ElemPtrResult throws (LowerError) {
5115 5115
    let containerTy = try typeOf(self, container);
5116 5116
    let subjectTy = resolver::autoDeref(containerTy);
5117 -
    let indexVal = try lowerExpr(self, index);
5118 5117
    let baseVal = try lowerExpr(self, container);
5118 +
    let indexVal = try lowerExpr(self, index);
5119 5119
    let baseReg = emitValToReg(self, baseVal);
5120 5120
5121 5121
    let mut dataReg = baseReg;
5122 5122
    let mut elemType: resolver::Type = undefined;
5123 5123
test/tests/index.eval.order.rad added +18 -0
1 +
//! returns: 42
2 +
//! The container expression of a subscript must run before its index expression.
3 +
4 +
static VALUES: [i32; 2] = [1, 42];
5 +
static NEXT_INDEX: u32 = 0;
6 +
7 +
fn selectValues() -> *[i32] {
8 +
    set NEXT_INDEX = 1;
9 +
    return &VALUES[..];
10 +
}
11 +
12 +
fn selectIndex() -> u32 {
13 +
    return NEXT_INDEX;
14 +
}
15 +
16 +
@default fn main() -> i32 {
17 +
    return selectValues()[selectIndex()];
18 +
}
test/tests/trait.supertrait.ril +4 -4
78 78
    br.ult w32 %2 %8 @guard#pass6 @guard#trap7;
79 79
  @guard#pass6
80 80
    load w64 %9 %3 0;
81 81
    add w64 %10 %9 %2;
82 82
    load w8 %11 %10 0;
83 -
    sload w32 %12 %5 72;
84 -
    add w64 %13 %5 40;
85 -
    br.ult w32 %12 32 @guard#pass8 @guard#trap9;
83 +
    add w64 %12 %5 40;
84 +
    sload w32 %13 %5 72;
85 +
    br.ult w32 %13 32 @guard#pass8 @guard#trap9;
86 86
  @guard#trap7
87 87
    ebreak;
88 88
    unreachable;
89 89
  @guard#pass8
90 -
    add w64 %14 %13 %12;
90 +
    add w64 %14 %12 %13;
91 91
    store w8 %11 %14 0;
92 92
    sload w32 %15 %5 72;
93 93
    add w32 %16 %15 1;
94 94
    store w32 %16 %5 72;
95 95
    add w32 %17 %2 1;