Lower references to array repeat literals
1ecd58a716ef28bcd7fe2f5f7e356049a32e851da42c2ff72178206e9eaaa493
The resolver accepted references to array repeat literals and exposed them as slices, but `lowerAddressOf` handled only ordinary array literals. Valid `&[value; count]` and `&mut [value; count]` expressions consequently reached `UnexpectedNodeValue` during lowering. Generalize the existing array-literal-to-slice path to include repeat literals and reuse ordinary array lowering for constant and runtime storage.
1 parent
177c9234
lib/std/lang/lower.rad
+36 -64
| 5031 | 5031 | } |
|
| 5032 | 5032 | // Handle dereference address: `&(*ptr) = ptr`. |
|
| 5033 | 5033 | if let case ast::NodeValue::Deref(target) = addr.target.value { |
|
| 5034 | 5034 | return try lowerExpr(self, target); |
|
| 5035 | 5035 | } |
|
| 5036 | - | // Handle slice literal: `&[1, 2, 3]`. |
|
| 5037 | - | if let case ast::NodeValue::ArrayLit(elements) = addr.target.value { |
|
| 5038 | - | return try lowerSliceLiteral(self, node, addr.target, elements); |
|
| 5036 | + | // Array literals become slices when their address is taken. |
|
| 5037 | + | match addr.target.value { |
|
| 5038 | + | case ast::NodeValue::ArrayLit(_), |
|
| 5039 | + | ast::NodeValue::ArrayRepeatLit(_) => |
|
| 5040 | + | { |
|
| 5041 | + | return try lowerArrayLiteralSlice(self, node, addr.target); |
|
| 5042 | + | } |
|
| 5043 | + | else => {} |
|
| 5039 | 5044 | } |
|
| 5040 | 5045 | throw LowerError::UnexpectedNodeValue(addr.target); |
|
| 5041 | 5046 | } |
|
| 5042 | 5047 | ||
| 5043 | - | /// Lower a slice literal like `&[1, 2, 3]`. |
|
| 5044 | - | /// If all elements are constants, creates static data. Otherwise, allocates |
|
| 5045 | - | /// stack space and stores elements at runtime. |
|
| 5046 | - | fn lowerSliceLiteral( |
|
| 5048 | + | /// Lower an addressed array literal as a slice. |
|
| 5049 | + | fn lowerArrayLiteralSlice( |
|
| 5047 | 5050 | self: *mut FnLowerer, |
|
| 5048 | 5051 | sliceNode: *ast::Node, |
|
| 5049 | - | arrayNode: *ast::Node, |
|
| 5050 | - | elements: *mut [*ast::Node] |
|
| 5052 | + | arrayNode: *ast::Node |
|
| 5051 | 5053 | ) -> il::Val throws (LowerError) { |
|
| 5052 | - | // Get the slice type from the address-of expression. |
|
| 5053 | 5054 | let sliceTy = try typeOf(self, sliceNode); |
|
| 5054 | 5055 | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 5055 | 5056 | throw LowerError::UnexpectedType(&sliceTy); |
|
| 5056 | 5057 | }; |
|
| 5057 | - | if elements.len == 0 { // Empty slices don't need to be stored as data. |
|
| 5058 | - | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 5058 | + | let arrayTy = try typeOf(self, arrayNode); |
|
| 5059 | + | let case resolver::Type::Array(arrayInfo) = arrayTy else { |
|
| 5060 | + | throw LowerError::ExpectedArray; |
|
| 5061 | + | }; |
|
| 5062 | + | let length = arrayInfo.length; |
|
| 5063 | + | if length == 0 { |
|
| 5064 | + | return try buildSliceValue( |
|
| 5065 | + | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 5066 | + | ); |
|
| 5059 | 5067 | } |
|
| 5060 | 5068 | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 5061 | - | let elemLayout = resolver::getTypeLayout(*item); |
|
| 5062 | - | return try lowerConstSliceLiteral(self, item, mutable, elements, elemLayout); |
|
| 5063 | - | } else { |
|
| 5064 | - | return try lowerRuntimeSliceLiteral(self, item, mutable, elements); |
|
| 5065 | - | } |
|
| 5066 | - | } |
|
| 5067 | - | ||
| 5068 | - | /// Lower a slice literal with all constant elements to static data. |
|
| 5069 | - | fn lowerConstSliceLiteral( |
|
| 5070 | - | self: *mut FnLowerer, |
|
| 5071 | - | elemTy: *resolver::Type, |
|
| 5072 | - | mutable: bool, |
|
| 5073 | - | elements: *mut [*ast::Node], |
|
| 5074 | - | elemLayout: resolver::Layout |
|
| 5075 | - | ) -> il::Val throws (LowerError) { |
|
| 5076 | - | // Build data values for all elements using standard data lowering. |
|
| 5077 | - | let mut b = dataBuilder(self.low.allocator); |
|
| 5078 | - | for elem in elements { |
|
| 5079 | - | try lowerConstDataInto(self.low, elem, *elemTy, elemLayout.size, self.fnName, &mut b); |
|
| 5080 | - | } |
|
| 5081 | - | let result = dataBuilderFinish(&b); |
|
| 5082 | - | let readOnly = not mutable; |
|
| 5083 | - | ||
| 5084 | - | return try lowerConstDataAsSlice(self, result.values, elemLayout.alignment, readOnly, elemTy, mutable, elements.len); |
|
| 5085 | - | } |
|
| 5086 | - | ||
| 5087 | - | /// Lower a slice literal with non-constant elements. |
|
| 5088 | - | fn lowerRuntimeSliceLiteral( |
|
| 5089 | - | self: *mut FnLowerer, |
|
| 5090 | - | elemTy: *resolver::Type, |
|
| 5091 | - | mutable: bool, |
|
| 5092 | - | elements: *mut [*ast::Node] |
|
| 5093 | - | ) -> il::Val throws (LowerError) { |
|
| 5094 | - | let elemLayout = resolver::getTypeLayout(*elemTy); |
|
| 5095 | - | let arraySize = elements.len * elemLayout.size; |
|
| 5096 | - | let arrayReg = nextReg(self); |
|
| 5097 | - | ||
| 5098 | - | // Reserve stack space for slice elements. |
|
| 5099 | - | emit(self, il::Instr::Reserve { |
|
| 5100 | - | dst: arrayReg, |
|
| 5101 | - | size: il::Val::Imm(arraySize as i64), |
|
| 5102 | - | alignment: elemLayout.alignment |
|
| 5103 | - | }); |
|
| 5104 | - | // Store each element. |
|
| 5105 | - | for elemNode, i in elements { |
|
| 5106 | - | let elemVal = try lowerExpr(self, elemNode); |
|
| 5107 | - | let offset = i * elemLayout.size; |
|
| 5108 | - | ||
| 5109 | - | try emitStore(self, arrayReg, offset as i32, *elemTy, elemVal); |
|
| 5069 | + | let mut b = dataBuilder(self.low.allocator); |
|
| 5070 | + | match arrayNode.value { |
|
| 5071 | + | case ast::NodeValue::ArrayLit(elements) => |
|
| 5072 | + | try lowerConstArrayLitInto(self.low, elements, arrayTy, self.fnName, &mut b), |
|
| 5073 | + | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 5074 | + | try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, self.fnName, &mut b), |
|
| 5075 | + | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5076 | + | } |
|
| 5077 | + | let result = dataBuilderFinish(&b); |
|
| 5078 | + | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5079 | + | return try lowerConstDataAsSlice( |
|
| 5080 | + | self, result.values, alignment, not mutable, item, mutable, length |
|
| 5081 | + | ); |
|
| 5110 | 5082 | } |
|
| 5111 | - | let lenVal = il::Val::Imm(elements.len as i64); |
|
| 5112 | - | ||
| 5113 | - | return try buildSliceValue(self, elemTy, mutable, il::Val::Reg(arrayReg), lenVal, lenVal); |
|
| 5083 | + | let data = try lowerExpr(self, arrayNode); |
|
| 5084 | + | let count = il::Val::Imm(length as i64); |
|
| 5085 | + | return try buildSliceValue(self, item, mutable, data, count, count); |
|
| 5114 | 5086 | } |
|
| 5115 | 5087 | ||
| 5116 | 5088 | /// Lower the common element pointer computation for subscript operations. |
|
| 5117 | 5089 | /// Handles both arrays and slices by resolving the container type, extracting |
|
| 5118 | 5090 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
test/tests/reference.array.repeat.rad
added
+4 -0
| 1 | + | /// Taking a reference to an array repeat literal yields a static slice. |
|
| 2 | + | fn repeatedSlice() -> *[u32] { |
|
| 3 | + | return &[(7 as u32); 3]; |
|
| 4 | + | } |
test/tests/reference.array.repeat.ril
added
+14 -0
| 1 | + | data $repeatedSlice$literal$0 align 4 { |
|
| 2 | + | w32 7 * 3; |
|
| 3 | + | } |
|
| 4 | + | ||
| 5 | + | fn w64 $repeatedSlice(w64 %0) { |
|
| 6 | + | @entry0 |
|
| 7 | + | copy %1 $repeatedSlice$literal$0; |
|
| 8 | + | reserve %2 16 8; |
|
| 9 | + | store w64 %1 %2 0; |
|
| 10 | + | store w32 3 %2 8; |
|
| 11 | + | store w32 3 %2 12; |
|
| 12 | + | blit %0 %2 16; |
|
| 13 | + | ret %0; |
|
| 14 | + | } |