Make `append` return the new slice
08a54eea6c5233e8931434b9e9de1c7c195182fcd0f366afcfae1415d7b4bf7a
1 parent
5f74ebc0
lib/std/arch/rv64/tests/slice.append.rad
+27 -0
| 157 | 157 | // Verify old u8 elements survived. |
|
| 158 | 158 | if bytes[0] != 0xAB { |
|
| 159 | 159 | return 23; |
|
| 160 | 160 | } |
|
| 161 | 161 | ||
| 162 | + | // Test that .append() returns the slice, allowing rebinding. |
|
| 163 | + | let ptr2 = arenaAlloc(&mut arena, @sizeOf(i32) * 2, @alignOf(i32)); |
|
| 164 | + | let mut vals = @sliceOf(ptr2 as *mut i32, 0, 2); |
|
| 165 | + | ||
| 166 | + | // Append within capacity, rebind via return value. |
|
| 167 | + | vals = vals.append(42, a); |
|
| 168 | + | assert vals.len == 1; |
|
| 169 | + | assert vals[0] == 42; |
|
| 170 | + | ||
| 171 | + | vals = vals.append(43, a); |
|
| 172 | + | assert vals.len == 2; |
|
| 173 | + | ||
| 174 | + | // Append past capacity -- triggers growth and returns updated slice. |
|
| 175 | + | let oldPtr = &vals[0] as *opaque; |
|
| 176 | + | vals = vals.append(44, a); |
|
| 177 | + | assert vals.len == 3; |
|
| 178 | + | assert vals.cap == 5; |
|
| 179 | + | ||
| 180 | + | // After growth, the data pointer should have changed. |
|
| 181 | + | let newPtr = &vals[0] as *opaque; |
|
| 182 | + | assert oldPtr != newPtr; |
|
| 183 | + | ||
| 184 | + | // Verify all elements survived. |
|
| 185 | + | assert vals[0] == 42; |
|
| 186 | + | assert vals[1] == 43; |
|
| 187 | + | assert vals[2] == 44; |
|
| 188 | + | ||
| 162 | 189 | return 0; |
|
| 163 | 190 | } |
lib/std/lang/lower.rad
+4 -3
| 6203 | 6203 | /// |
|
| 6204 | 6204 | /// @store: |
|
| 6205 | 6205 | /// store element at ptr + len * stride |
|
| 6206 | 6206 | /// increment len |
|
| 6207 | 6207 | /// |
|
| 6208 | - | fn lowerSliceAppend(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) { |
|
| 6208 | + | fn lowerSliceAppend(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) { |
|
| 6209 | 6209 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6210 | 6210 | else throw LowerError::MissingMetadata; |
|
| 6211 | 6211 | ||
| 6212 | 6212 | // Get the address of the slice header. |
|
| 6213 | 6213 | let sliceVal = try lowerExpr(self, access.parent); |
| 6281 | 6281 | try emitStore(self, elemDst, 0, *elemType, elemVal); |
|
| 6282 | 6282 | ||
| 6283 | 6283 | // Increment len. |
|
| 6284 | 6284 | let newLen = emitTypedBinOp(self, il::BinOp::Add, il::Type::W32, lenVal, il::Val::Imm(1)); |
|
| 6285 | 6285 | emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET); |
|
| 6286 | + | ||
| 6287 | + | return il::Val::Reg(sliceReg); |
|
| 6286 | 6288 | } |
|
| 6287 | 6289 | ||
| 6288 | 6290 | /// Lower `slice.delete(index)`. |
|
| 6289 | 6291 | /// |
|
| 6290 | 6292 | /// Bounds-check the index, shift elements after it by one stride |
| 6332 | 6334 | fn lowerCallOrCtor(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6333 | 6335 | let nodeData = resolver::nodeData(self.low.resolver, node).extra; |
|
| 6334 | 6336 | ||
| 6335 | 6337 | // Check for slice method dispatch. |
|
| 6336 | 6338 | if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData { |
|
| 6337 | - | try lowerSliceAppend(self, call, elemType); |
|
| 6338 | - | return il::Val::Undef; |
|
| 6339 | + | return try lowerSliceAppend(self, call, elemType); |
|
| 6339 | 6340 | } |
|
| 6340 | 6341 | if let case resolver::NodeExtra::SliceDelete { elemType } = nodeData { |
|
| 6341 | 6342 | try lowerSliceDelete(self, call, elemType); |
|
| 6342 | 6343 | return il::Val::Undef; |
|
| 6343 | 6344 | } |
lib/std/lang/resolver.rad
+5 -3
| 4812 | 4812 | let subjectTy = autoDeref(parentTy); |
|
| 4813 | 4813 | ||
| 4814 | 4814 | if let case Type::Slice { item, mutable } = subjectTy { |
|
| 4815 | 4815 | let methodName = try nodeName(self, access.child); |
|
| 4816 | 4816 | if methodName == "append" { |
|
| 4817 | - | return try resolveSliceAppend(self, node, access.parent, call.args, item, mutable); |
|
| 4817 | + | return try resolveSliceAppend(self, node, access.parent, parentTy, call.args, item, mutable); |
|
| 4818 | 4818 | } |
|
| 4819 | 4819 | if methodName == "delete" { |
|
| 4820 | 4820 | return try resolveSliceDelete(self, node, access.parent, call.args, item, mutable); |
|
| 4821 | 4821 | } |
|
| 4822 | 4822 | } |
| 4884 | 4884 | /// Resolve `slice.append(val, allocator)`. |
|
| 4885 | 4885 | fn resolveSliceAppend( |
|
| 4886 | 4886 | self: *mut Resolver, |
|
| 4887 | 4887 | node: *ast::Node, |
|
| 4888 | 4888 | parent: *ast::Node, |
|
| 4889 | + | parentType: Type, |
|
| 4889 | 4890 | args: *mut [*ast::Node], |
|
| 4890 | 4891 | elemType: *Type, |
|
| 4891 | 4892 | mutable: bool |
|
| 4892 | 4893 | ) -> Type throws (ResolveError) { |
|
| 4893 | 4894 | if not mutable { |
| 4900 | 4901 | })); |
|
| 4901 | 4902 | } |
|
| 4902 | 4903 | // First argument must be assignable to the element type. |
|
| 4903 | 4904 | try checkAssignable(self, args[0], *elemType); |
|
| 4904 | 4905 | // Second argument: the allocator. We accept any type -- the lowerer |
|
| 4905 | - | // reads .func and .ctx at fixed offsets (structurally typed). |
|
| 4906 | + | // reads `.func` and `.ctx` at fixed offsets. |
|
| 4906 | 4907 | try visit(self, args[1], Type::Unknown); |
|
| 4907 | 4908 | self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
|
| 4908 | 4909 | ||
| 4909 | - | return setNodeType(self, node, Type::Void); |
|
| 4910 | + | // Return the parent's type so the caller can rebind: |
|
| 4911 | + | return setNodeType(self, node, parentType); |
|
| 4910 | 4912 | } |
|
| 4911 | 4913 | ||
| 4912 | 4914 | /// Resolve `slice.delete(index)`. |
|
| 4913 | 4915 | fn resolveSliceDelete( |
|
| 4914 | 4916 | self: *mut Resolver, |