Make `append` return the new slice

08a54eea6c5233e8931434b9e9de1c7c195182fcd0f366afcfae1415d7b4bf7a
Alexis Sellier committed ago 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,