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.
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; |