Make slice append report allocation failures

a8669da64479a5fd13b47a125dd331f281b8de0597fafc9c43718f08e1bfcee0
Alexis Sellier committed ago 1 parent dee95968
compiler/radiance.rad +11 -10
655 655
    }
656 656
    set funcPath[desc.modPath.len - 1] = desc.fnName;
657 657
    let funcArg = synthScopeAccess(arena, &funcPath[..desc.modPath.len]);
658 658
659 659
    let a = alloc::arenaAllocator(&mut arena.arena);
660 -
    let args = ast::nodeSlice(arena, 3)
661 -
        .append(modArg, a)
662 -
        .append(nameArg, a)
663 -
        .append(funcArg, a);
660 +
    let args = ast::nodeSlice(arena, 3);
661 +
    try! args.append(modArg, a);
662 +
    try! args.append(nameArg, a);
663 +
    try! args.append(funcArg, a);
664 664
665 665
    return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args }));
666 666
}
667 667
668 668
/// Inject a test runner into the entry package's root module.
720 720
unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[TestDesc]) -> *ast::Node {
721 721
    // Build array literal: `[testing::test(...), ...]`.
722 722
    let a = alloc::arenaAllocator(&mut arena.arena);
723 723
    let mut elements = ast::nodeSlice(arena, tests.len as u32);
724 724
    for i in 0..tests.len {
725 -
        elements.append(synthTestCall(arena, &tests[i]), a);
725 +
        try! elements.append(synthTestCall(arena, &tests[i]), a);
726 726
    }
727 727
    let arrayLit = ast::synthNode(arena, ast::NodeValue::ArrayLit(elements));
728 728
729 729
    // Build: `&[...]`.
730 730
    let testsRef = ast::synthNode(arena, ast::NodeValue::AddressOf(ast::AddressOf {
731 731
        target: arrayLit, mutable: false,
732 732
    }));
733 733
734 734
    // Build: `testing::runAllTests(&[...])`.
735 735
    let runFn = synthScopeAccess(arena, &["testing", "runAllTests"]);
736 -
    let callArgs = ast::nodeSlice(arena, 1).append(testsRef, a);
736 +
    let callArgs = try! ast::nodeSlice(arena, 1).append(testsRef, a);
737 737
    let callExpr = ast::synthNode(arena, ast::NodeValue::Call(ast::Call {
738 738
        callee: runFn, args: callArgs,
739 739
    }));
740 740
741 741
    // Build: `return testing::runAllTests(&[...]);`
742 742
    let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr });
743 -
    let bodyStmts = ast::nodeSlice(arena, 1).append(retStmt, a);
743 +
    let bodyStmts = try! ast::nodeSlice(arena, 1).append(retStmt, a);
744 744
    let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts, isUnsafe: false }));
745 745
746 746
    // Build: `unsafe fn #testMain() -> i32`
747 747
    let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain")));
748 748
    let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer {
755 755
    };
756 756
757 757
    // Entry and function safety attributes.
758 758
    let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default));
759 759
    let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe));
760 -
    let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a);
760 +
    let attrList = try! ast::nodeSlice(arena, 2).append(attrNode, a);
761 +
    try! attrList.append(unsafeAttr, a);
761 762
    let fnAttrs = ast::Attributes { list: attrList };
762 763
763 764
    return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl {
764 765
        name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs,
765 766
    }));
775 776
        panic "injectIntoBlock: expected Block node";
776 777
    };
777 778
    let allocator = alloc::arenaAllocator(&mut arena.arena);
778 779
    let mut stmts = ast::nodeSlice(arena, block.statements.len + 1);
779 780
    for stmt in block.statements {
780 -
        stmts.append(stmt, allocator);
781 +
        try! stmts.append(stmt, allocator);
781 782
    }
782 -
    stmts.append(decl, allocator);
783 +
    try! stmts.append(decl, allocator);
783 784
    return ast::allocNode(arena, blockNode.span, ast::NodeValue::Block(
784 785
        ast::Block { statements: stmts, isUnsafe: block.isUnsafe }
785 786
    ));
786 787
}
787 788
lib/std/arch/rv64/asm/emit.rad +9 -5
13 13
    let idx = a.symbols.len;
14 14
    let offset: i32 = a.data.len as i32
15 15
        if a.section == super::Section::Data
16 16
        else a.text.len as i32 * rv64::INSTR_SIZE;
17 17
18 -
    a.symbols.append(super::Symbol {
18 +
    try! a.symbols.append(super::Symbol {
19 19
        name,
20 20
        section: a.section,
21 21
        offset,
22 22
        isExported: dict::get(&a.exportMap, name) <> nil,
23 23
    }, alloc::arenaAllocator(a.arena));
24 24
    dict::insert(&mut a.symbolMap, name, idx as i32);
25 25
}
26 26
27 27
/// Append one encoded instruction word to the text section.
28 28
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
29 -
    a.text.append(word, alloc::arenaAllocator(a.arena));
29 +
    try a.text.append(word, alloc::arenaAllocator(a.arena)) catch {
30 +
        throw super::Error::TextOverflow;
31 +
    };
30 32
}
31 33
32 34
/// Append `words` no-op instructions to the text section.
33 35
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
34 36
    for _ in 0..words {
36 38
    }
37 39
}
38 40
39 41
/// Append one byte to the data section.
40 42
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
41 -
    a.data.append(byte, alloc::arenaAllocator(a.arena));
43 +
    try a.data.append(byte, alloc::arenaAllocator(a.arena)) catch {
44 +
        throw super::Error::DataOverflow;
45 +
    };
42 46
}
43 47
44 48
/// Emit a little-endian integer with `bytes` bytes.
45 49
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
46 50
    for i in 0..bytes {
78 82
    }
79 83
}
80 84
81 85
/// Record a pending symbol fixup.
82 86
unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
83 -
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
87 +
    try! a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
84 88
}
85 89
86 90
/// Record a text fixup that must be resolved after all program text is known.
87 91
unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
88 -
    a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
92 +
    try! a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
89 93
}
90 94
91 95
/// Record a text-section symbol fixup and reserve its instruction words.
92 96
export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
93 97
    recordFixup(a, symbol, info);
lib/std/arch/rv64/emit.rad +7 -6
201 201
    let pendingBranchesBuf = pendingBranches as *mut [PendingBranch];
202 202
    let pendingCallsBuf = pendingCalls as *mut [PendingCall];
203 203
    let pendingJumpsBuf = pendingJumps as *mut [PendingJump];
204 204
    let pendingAddrLoadsBuf = pendingAddrLoads as *mut [PendingAddrLoad];
205 205
    let funcsBuf = funcs as *mut [types::FuncAddr];
206 +
206 207
    return Emitter {
207 208
        allocator: alloc::arenaAllocator(arena),
208 209
        code: code as *mut [u32],
209 210
        codeLen: 0,
210 211
        pendingBranches: &mut pendingBranchesBuf[..0],
263 264
    recordFuncAt(e, name, codeLen);
264 265
}
265 266
266 267
/// Record a function's start position at `index` for printing.
267 268
export fn recordFuncAt(e: &mut Emitter, name: *[u8], index: u32) {
268 -
    e.funcs.append(types::FuncAddr { name, index }, e.allocator);
269 +
    try! e.funcs.append(types::FuncAddr { name, index }, e.allocator);
269 270
}
270 271
271 272
/// Record a local branch needing later patching.
272 273
/// Unconditional jumps use a single slot (J-type, +-1MB range).
273 274
/// Conditional branches use two slots (B-type has only +-4KB range,
274 275
/// so large functions may need the inverted-branch + JAL fallback).
275 276
export fn recordBranch(e: &mut Emitter, targetBlock: u32, kind: BranchKind) {
276 -
    e.pendingBranches.append(PendingBranch {
277 +
    try! e.pendingBranches.append(PendingBranch {
277 278
        index: e.codeLen,
278 279
        target: targetBlock,
279 280
        kind: kind,
280 281
    }, e.allocator);
281 282
289 290
290 291
/// Record a function call needing later patching.
291 292
/// Emits placeholder instructions that will be patched later.
292 293
/// Uses two slots to support long-distance calls.
293 294
export fn recordCall(e: &mut Emitter, target: *[u8]) {
294 -
    e.pendingCalls.append(PendingCall {
295 +
    try! e.pendingCalls.append(PendingCall {
295 296
        index: e.codeLen,
296 297
        target,
297 298
    }, e.allocator);
298 299
299 300
    emit(e, encode::nop()); // Placeholder for AUIPC.
300 301
    emit(e, encode::nop()); // Placeholder for JALR.
301 302
}
302 303
303 304
/// Record a jump emitted by assembly that needs whole-program patching.
304 305
export fn recordJumpAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
305 -
    e.pendingJumps.append(PendingJump {
306 +
    try! e.pendingJumps.append(PendingJump {
306 307
        index,
307 308
        target,
308 309
        rd,
309 310
    }, e.allocator);
310 311
}
320 321
    emit(e, encode::nop()); // Placeholder for ADDI.
321 322
}
322 323
323 324
/// Record a function address load already reserved by assembly.
324 325
export fn recordAddrLoadAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
325 -
    e.pendingAddrLoads.append(PendingAddrLoad {
326 +
    try! e.pendingAddrLoads.append(PendingAddrLoad {
326 327
        index,
327 328
        target,
328 329
        rd: rd,
329 330
        isData: false,
330 331
    }, e.allocator);
331 332
}
332 333
333 334
/// Record a data address load needing later patching.
334 335
/// Uses an absolute 32-bit load sequence matching the current data memory map.
335 336
export fn recordDataAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) {
336 -
    e.pendingAddrLoads.append(PendingAddrLoad {
337 +
    try! e.pendingAddrLoads.append(PendingAddrLoad {
337 338
        index: e.codeLen,
338 339
        target,
339 340
        rd: rd,
340 341
        isData: true,
341 342
    }, e.allocator);
lib/std/lang/alloc.rad +5 -5
108 108
///
109 109
/// Bundles an allocation function with an opaque context pointer so that
110 110
/// any allocation strategy (arena, free-list, mmap, pool) can be used
111 111
/// through a uniform interface. The `func` field is called with the context
112 112
/// pointer, a byte size and an alignment, and must return a pointer to
113 -
/// the allocated memory or panic on failure.
113 +
/// the allocated memory or throw `AllocError` on failure.
114 114
export record Allocator: Copy {
115 115
    /// Allocation function. Returns a pointer to `size` bytes
116 -
    /// aligned to `alignment`, or panics on failure.
117 -
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
116 +
    /// aligned to `alignment`, or throws `AllocError` on failure.
117 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (AllocError),
118 118
    /// Opaque context pointer passed to `func`.
119 119
    ctx: *unsafe mut opaque,
120 120
}
121 121
122 122
/// Create an allocator whose arena must outlive all uses of the allocator.
126 126
        ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque,
127 127
    };
128 128
}
129 129
130 130
/// Arena allocation function conforming to the `Allocator` interface.
131 -
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
131 +
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
132 132
    let arena = ctx as *unsafe mut Arena;
133 -
    return try! alloc(arena, size, alignment);
133 +
    return try alloc(arena, size, alignment);
134 134
}
135 135
136 136
/// Allocate raw storage that remains valid until the arena is reset.
137 137
export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) {
138 138
    let owner = try alloc(arena, size, alignment);
lib/std/lang/alloc/tests.rad +21 -2
109 109
    static STORAGE: [u8; 256] = [0; 256];
110 110
    let mut arena = super::new(&mut STORAGE[..]);
111 111
    let a = super::arenaAllocator(&mut arena);
112 112
113 113
    // Allocate through the Allocator indirection.
114 -
    let p1 = a.func(a.ctx, 16, 4);
114 +
    let p1 = try! a.func(a.ctx, 16, 4);
115 115
    try testing::expect(super::used(&arena) == 16);
116 116
117 -
    let p2 = a.func(a.ctx, 8, 8);
117 +
    let p2 = try! a.func(a.ctx, 8, 8);
118 118
    try testing::expect(super::used(&arena) == 24);
119 119
120 120
    // Verify the pointers are distinct.
121 121
    try testing::expect(p1 as u64 <> p2 as u64);
122 122
}
123 +
124 +
/// Append reports arena exhaustion and preserves the slice and arena state.
125 +
@test unsafe fn testAllocatorAppendExhausted() throws (testing::TestError) {
126 +
    static storage: [u8; 4] = [0; 4];
127 +
    let mut arena = super::new(&mut storage[..]);
128 +
    let a = super::arenaAllocator(&mut arena);
129 +
    let mut items: *mut [u32] = &mut [];
130 +
    try! items.append(42, a);
131 +
    let mut caught = false;
132 +
    try items.append(99, a) catch error as super::AllocError {
133 +
        try testing::expect(error == super::AllocError::OutOfMemory);
134 +
        set caught = true;
135 +
    };
136 +
    try testing::expect(caught);
137 +
    try testing::expect(items.len == 1);
138 +
    try testing::expect(items.cap == 1);
139 +
    try testing::expect(items[0] == 42);
140 +
    try testing::expect(arena.offset == 4);
141 +
}
lib/std/lang/ast.rad +1 -1
867 867
    let fnBody: *Node = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false }));
868 868
    let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl {
869 869
        name: fnName, sig: fnSig, body: fnBody, attrs: nil,
870 870
    }));
871 871
    let mut rootStmts: *mut [*Node] = &mut [];
872 -
    rootStmts.append(fnDecl, a);
872 +
    try! rootStmts.append(fnDecl, a);
873 873
    let modBody = synthNode(arena, NodeValue::Block(Block { statements: rootStmts, isUnsafe: false }));
874 874
875 875
    return SynthFnMod { modBody, fnBody };
876 876
}
lib/std/lang/lower.rad +70 -33
93 93
use std::lang::types;
94 94
use std::mem;
95 95
use std::lang::ast;
96 96
use std::lang::il;
97 97
use std::lang::module;
98 +
use std::lang::parser;
98 99
use std::lang::resolver;
99 100
100 101
// TODO: Search for all `_ as i32` to ensure that casts from u32 to i32 don't
101 102
// happen, since they are potentially truncating values.
102 103
349 350
        }
350 351
    }
351 352
    let tag = self.errTagCounter;
352 353
353 354
    set self.errTagCounter += 1;
354 -
    self.errTags.append(ErrTagEntry { ty: errType, tag }, self.allocator);
355 +
    try! self.errTags.append(ErrTagEntry { ty: errType, tag }, self.allocator);
355 356
356 357
    return tag;
357 358
}
358 359
359 360
/// Emit one function to the active output.
360 361
unsafe fn emitFunction(self: &mut Lowerer, func: *unsafe il::Fn, role: FnRole) {
361 362
    match self.output {
362 363
        case FnOutput::Accumulate(accumulated) => {
363 364
            let mut fns = accumulated;
364 -
            fns.append(func, self.allocator);
365 +
            try! fns.append(func, self.allocator);
365 366
            set self.output = FnOutput::Accumulate(fns);
366 367
        }
367 368
        case FnOutput::Stream(sink) => {
368 369
            sink.emitFn(sink.ctx, func, role);
369 370
        }
420 421
    return true;
421 422
}
422 423
423 424
/// Append a data value to the builder.
424 425
fn dataBuilderPush(b: &mut DataValueBuilder, value: il::DataValue) {
425 -
    b.values.append(value, b.allocator);
426 +
    try! b.values.append(value, b.allocator);
426 427
427 428
    if value.count > 0 and not dataIsZero(value.item) {
428 429
        set b.zeroInit = false;
429 430
    }
430 431
}
906 907
907 908
/// Register a function symbol with its qualified name.
908 909
/// Called when lowering function declarations, so cross-package calls can find
909 910
/// the function by name.
910 911
fn registerFnSym(self: &mut Lowerer, sym: *unsafe resolver::Symbol, qualName: *[u8]) {
911 -
    self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
912 +
    try! self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
912 913
}
913 914
914 915
/// Look up a function's qualified name by its symbol.
915 916
/// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered).
916 917
// TODO: This is kind of dubious as an optimization, if it depends on the order
1143 1144
        set values[i] = il::DataValue {
1144 1145
            item: il::DataItem::Fn(methodNames[i]),
1145 1146
            count: 1,
1146 1147
        };
1147 1148
    }
1148 -
    self.data.append(il::Data {
1149 +
    try! self.data.append(il::Data {
1149 1150
        name: vName,
1150 1151
        size: traitInfo.methods.len as u32 * resolver::PTR_SIZE,
1151 1152
        alignment: resolver::PTR_SIZE,
1152 1153
        readOnly: true,
1153 1154
        isZeroInit: false,
1351 1352
    let qualName = qualifyName(self, nil, sym.name);
1352 1353
    let mut b = dataBuilder(self.allocator);
1353 1354
    try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b);
1354 1355
    let result = dataBuilderFinish(b);
1355 1356
1356 -
    self.data.append(il::Data {
1357 +
    try! self.data.append(il::Data {
1357 1358
        name: qualName,
1358 1359
        size: layout.size,
1359 1360
        alignment: layout.alignment,
1360 1361
        readOnly,
1361 1362
        isZeroInit: not readOnly and result.zeroInit,
1762 1763
    values: *[il::DataValue],
1763 1764
    dataPrefix: *[u8]
1764 1765
) -> *[u8] throws (LowerError) {
1765 1766
    let dataCount = self.data.len;
1766 1767
    let name = try nextDeclDataName(self, dataPrefix, dataCount, "literal");
1767 -
    self.data.append(il::Data {
1768 +
    try! self.data.append(il::Data {
1768 1769
        name,
1769 1770
        size,
1770 1771
        alignment,
1771 1772
        readOnly,
1772 1773
        isZeroInit: not readOnly and dataValuesAreZeroInit(values),
1894 1895
    }
1895 1896
    if let name = found {
1896 1897
        set dataName = name;
1897 1898
    } else {
1898 1899
        set dataName = try nextDataName(self);
1899 -
        self.low.data.append(il::Data {
1900 +
        try! self.low.data.append(il::Data {
1900 1901
            name: dataName,
1901 1902
            size,
1902 1903
            alignment,
1903 1904
            readOnly,
1904 1905
            isZeroInit: not readOnly and result.zeroInit,
2039 2040
    let vars = try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val];
2040 2041
2041 2042
    for i in 0..varCount {
2042 2043
        set vars[i] = nil;
2043 2044
    }
2044 -
    self.blockData.append(BlockData {
2045 +
    try! self.blockData.append(BlockData {
2045 2046
        label,
2046 2047
        params: &mut [],
2047 2048
        paramVars: &mut [],
2048 2049
        instrs: &mut [],
2049 2050
        locs: &mut [],
2062 2063
    labelBase: *[u8],
2063 2064
    param: il::Param
2064 2065
) -> BlockId throws (LowerError) {
2065 2066
    let block = try createBlock(self, labelBase);
2066 2067
    let blk = getBlockMut(self, block);
2067 -
    blk.params.append(param, self.allocator);
2068 +
    try! blk.params.append(param, self.allocator);
2068 2069
2069 2070
    return block;
2070 2071
}
2071 2072
2072 2073
/// Switch to building a different block.
2136 2137
            else => {},
2137 2138
        }
2138 2139
    }
2139 2140
    // Record source location alongside instruction when enabled.
2140 2141
    if self.low.options.debug {
2141 -
        block.locs.append(self.srcLoc, self.allocator);
2142 +
        try! block.locs.append(self.srcLoc, self.allocator);
2142 2143
    }
2143 -
    block.instrs.append(instr, self.allocator);
2144 +
    try! block.instrs.append(instr, self.allocator);
2144 2145
}
2145 2146
2146 2147
/// Emit an unconditional jump to `target`.
2147 2148
unsafe fn emitJmp(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2148 2149
    emit(self, il::Instr::Jmp { target: *target, args: &mut [] });
2614 2615
    for i in 0..preds.len {
2615 2616
        if preds[i] == *pred { // Avoid duplicate predecessor entries.
2616 2617
            return;
2617 2618
        }
2618 2619
    }
2619 -
    preds.append(*pred, self.allocator);
2620 +
    try! preds.append(*pred, self.allocator);
2620 2621
}
2621 2622
2622 2623
/// Finalize all blocks and return the block array.
2623 2624
unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *unsafe [il::Block] throws (LowerError) {
2624 2625
    let blockCount = self.blockData.len;
2791 2792
    type: il::Type,
2792 2793
    mutable: bool,
2793 2794
    val: il::Val
2794 2795
) -> Var {
2795 2796
    let id = self.vars.len;
2796 -
    self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator);
2797 +
    try! self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator);
2797 2798
2798 2799
    let v = Var(id);
2799 2800
    if self.currentBlock <> nil {
2800 2801
        defVar(self, v, val);
2801 2802
    }
2921 2922
2922 2923
    // Create block parameter and add it to the block.
2923 2924
    let param = il::Param { value: reg, type };
2924 2925
    let blk = getBlockMut(self, block);
2925 2926
    let paramIdx = blk.paramVars.len;
2926 -
    blk.params.append(param, self.allocator);
2927 -
    blk.paramVars.append(*v, self.allocator); // Associate variable with parameter.
2927 +
    try! blk.params.append(param, self.allocator);
2928 +
    try! blk.paramVars.append(*v, self.allocator); // Associate variable with parameter.
2928 2929
2929 2930
    // Record that this variable's value in this block is now the parameter register.
2930 2931
    // This must happen before the predecessor loop to handle self-referential loops.
2931 2932
    set blk.vars[*v] = il::Val::Reg(reg);
2932 2933
3130 3131
        } else {
3131 3132
            set name = try paramName(&astParams[i].value);
3132 3133
        }
3133 3134
        let v = newVar(self, name, type, false, il::Val::Undef);
3134 3135
3135 -
        self.params.append(FnParamBinding { var: v, reg }, self.allocator);
3136 +
        try! self.params.append(FnParamBinding { var: v, reg }, self.allocator);
3136 3137
    }
3137 3138
    return params;
3138 3139
}
3139 3140
3140 3141
/// Resolve match subject.
3591 3592
                set blocks[i] = try createBlock(self, "case");
3592 3593
                for pat in pats {
3593 3594
                    let cv = resolver::constValueEntry(self.low.resolver, pat)
3594 3595
                        else throw LowerError::MissingConst(pat);
3595 3596
3596 -
                    cases.append(il::SwitchCase {
3597 +
                    try! cases.append(il::SwitchCase {
3597 3598
                        value: constToScalar(cv),
3598 3599
                        target: *blocks[i],
3599 3600
                        args: &mut []
3600 3601
                    }, self.allocator);
3601 3602
                }
6279 6280
    // if successful, or an optional of it, if using `try?`.
6280 6281
    let tryExprTy = try typeOf(self, node);
6281 6282
    // Check for trait method dispatch or standalone method call.
6282 6283
    let mut resVal: il::Val = undefined;
6283 6284
    let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra;
6284 -
    if let case resolver::NodeExtra::TraitMethodCall {
6285 +
    if let case resolver::NodeExtra::SliceAppend { elemType } = callNodeExtra {
6286 +
        set resVal = try lowerSliceAppend(self, callExpr, elemType);
6287 +
    } else if let case resolver::NodeExtra::TraitMethodCall {
6285 6288
        traitInfo, methodIndex
6286 6289
    } = callNodeExtra {
6287 6290
        set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex);
6288 6291
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6289 6292
        set resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6436 6439
6437 6440
        if let typeNode = clause.typeNode {
6438 6441
            let errTy = try typeOf(self, typeNode);
6439 6442
            set errTypes[i] = errTy;
6440 6443
6441 -
            cases.append(il::SwitchCase {
6444 +
            try! cases.append(il::SwitchCase {
6442 6445
                value: getOrAssignErrorTag(self.low, errTy) as i64,
6443 6446
                target: *blocks[i],
6444 6447
                args: &mut []
6445 6448
            }, self.allocator);
6446 6449
        } else {
6537 6540
    // Now all predecessors of header are known, seal it.
6538 6541
    try sealBlock(self, header);
6539 6542
    try switchToAndSeal(self, done);
6540 6543
}
6541 6544
6542 -
/// Lower `slice.append(val, allocator)`.
6545 +
/// Lower `slice.append(val, allocator)` and return its result aggregate.
6543 6546
///
6544 6547
/// Emits inline grow-if-needed logic:
6545 6548
///
6546 6549
///     load len, cap from slice header
6547 6550
///     if len < cap: jmp @store
6548 6551
///     else:         jmp @grow
6549 6552
///
6550 6553
///     @grow:
6551 -
///       newCap = max(cap * 2, 1)
6552 -
///       call allocator.func(allocator.ctx, newCap * stride, alignment)
6554 +
///       limit = U32_MAX / stride
6555 +
///       trap if cap > (limit - 1) / 2
6556 +
///       newCap = cap * 2 + 1
6557 +
///       bytes = newCap * stride
6558 +
///       call allocator.func(allocator.ctx, bytes, alignment)
6559 +
///       if allocation failed: return error result
6553 6560
///       copy old data to new pointer
6554 6561
///       update slice ptr and cap
6555 6562
///       jmp @store
6556 6563
///
6557 6564
///     @store:
6558 6565
///       store element at ptr + len * stride
6559 6566
///       increment len
6567 +
///       return success result
6560 6568
///
6561 -
unsafe fn lowerSliceAppend(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) {
6569 +
/// The slice header changes only after allocation succeeds.
6570 +
unsafe fn lowerSliceAppend(
6571 +
    self: &mut FnLowerer,
6572 +
    call: ast::Call,
6573 +
    elemType: *resolver::Type,
6574 +
) -> il::Val throws (LowerError) {
6562 6575
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6563 6576
        else throw LowerError::MissingMetadata;
6577 +
    let case resolver::Type::Fn(info) = try typeOf(self, call.callee)
6578 +
        else throw LowerError::ExpectedFunction;
6579 +
6580 +
    let result = emitReserveLayout(self, resolver::getResultLayout(*info.returnType, info.throwList));
6564 6581
6565 6582
    // Get the address of the slice header.
6566 6583
    let sliceVal = try lowerExpr(self, access.parent);
6567 6584
    let sliceReg = emitValToReg(self, sliceVal);
6568 6585
6579 6596
    let lenVal = loadSliceLen(self, sliceReg);
6580 6597
    let capVal = loadSliceCap(self, sliceReg);
6581 6598
6582 6599
    // Branch: if length is smaller than capacity, go to @store else @grow.
6583 6600
    let storeBlock = try createBlock(self, "append.store");
6601 +
    let doneBlock = try createBlock(self, "append.done");
6584 6602
    let growBlock = try createBlock(self, "append.grow");
6585 6603
    try emitBrCmp(self, il::CmpOp::Ult, il::Type::W32, lenVal, capVal, storeBlock, growBlock);
6586 6604
    try switchToAndSeal(self, growBlock);
6587 6605
6588 6606
    // -- @grow block ----------------------------------------------------------
6589 6607
6590 -
    // `newCap = max(cap * 2, 1)`.
6591 -
    // We are only here when at capacity, so we use `or` with `1` to ensure at least capacity `1`.
6608 +
    // Each allocation size and slice length must fit in u32.
6609 +
    let limit = parser::U32_MAX / stride;
6610 +
    try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, capVal, il::Val::Imm(((limit - 1) / 2 + 1) as i64));
6611 +
6612 +
    // Grow capacity to `cap * 2 + 1`.
6592 6613
    let doubledVal = emitTypedBinOp(self, il::BinOp::Shl, il::Type::W32, capVal, il::Val::Imm(1));
6593 6614
    let newCapVal = emitTypedBinOp(self, il::BinOp::Or, il::Type::W32, doubledVal, il::Val::Imm(1));
6594 6615
6595 6616
    // Call allocator: `a.func(a.ctx, newCap * stride, alignment)`.
6596 6617
    let allocFnReg = nextReg(self);
6598 6619
6599 6620
    let allocCtxReg = nextReg(self);
6600 6621
    emitLoadW64At(self, allocCtxReg, allocReg, 8);
6601 6622
6602 6623
    let byteSize = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, newCapVal, il::Val::Imm(stride as i64));
6603 -
    let args = try allocVals(self, 3);
6604 6624
6605 -
    set args[0] = il::Val::Reg(allocCtxReg);
6606 -
    set args[1] = byteSize;
6607 -
    set args[2] = il::Val::Imm(alignment as i64);
6625 +
    let args = try allocVals(self, 4);
6626 +
6627 +
    // The append result fits the allocation pointer and the shared error types.
6628 +
    set args[0] = il::Val::Reg(result);
6629 +
    set args[1] = il::Val::Reg(allocCtxReg);
6630 +
    set args[2] = byteSize;
6631 +
    set args[3] = il::Val::Imm(alignment as i64);
6608 6632
6609 -
    let newPtrReg = nextReg(self);
6610 6633
    emit(self, il::Instr::Call {
6611 6634
        retTy: il::Type::W64,
6612 -
        dst: newPtrReg,
6635 +
        dst: nil,
6613 6636
        func: il::Val::Reg(allocFnReg),
6614 6637
        args,
6615 6638
    });
6616 6639
6640 +
    let tag = resultTagReg(self, result);
6641 +
    let copyBlock = try createBlock(self, "append.copy");
6642 +
    try emitBr(self, tag, doneBlock, copyBlock);
6643 +
6644 +
    try switchToAndSeal(self, copyBlock);
6645 +
    let newPtrReg = nextReg(self);
6646 +
    emitLoadW64At(self, newPtrReg, result, RESULT_VAL_OFFSET);
6647 +
6617 6648
    // Copy old data byte-by-byte.
6618 6649
    let oldPtrReg = loadSlicePtr(self, sliceReg);
6619 6650
    let copyBytes = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, lenVal, il::Val::Imm(stride as i64));
6620 6651
    try emitByteCopyLoop(self, newPtrReg, oldPtrReg, copyBytes, "append");
6621 6652
6635 6666
6636 6667
    // Increment len.
6637 6668
    let newLen = emitTypedBinOp(self, il::BinOp::Add, il::Type::W32, lenVal, il::Val::Imm(1));
6638 6669
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
6639 6670
6640 -
    return il::Val::Reg(sliceReg);
6671 +
    // Return the append result.
6672 +
    emitStoreW64At(self, il::Val::Imm(0), result, 0);
6673 +
    try emitStore(self, result, RESULT_VAL_OFFSET, *info.returnType, il::Val::Reg(sliceReg));
6674 +
    try emitJmp(self, doneBlock);
6675 +
6676 +
    try switchToAndSeal(self, doneBlock);
6677 +
    return il::Val::Reg(result);
6641 6678
}
6642 6679
6643 6680
/// Lower `slice.delete(index)`.
6644 6681
///
6645 6682
/// Bounds-check the index, shift elements after it by one stride
lib/std/lang/parser.rad +22 -22
265 265
        return node(p, ast::NodeValue::ArrayRepeatLit(
266 266
            ast::ArrayRepeatLit { item: firstExpr, count }
267 267
        ));
268 268
    }
269 269
    // Regular array literal: `[a, b, ...]`.
270 -
    let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator);
270 +
    let mut items = try! ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator);
271 271
272 272
    while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) {
273 273
        let elem = try parseNormalExpr(p);
274 -
        items.append(elem, p.allocator);
274 +
        try! items.append(elem, p.allocator);
275 275
    }
276 276
    try expect(p, scanner::TokenKind::RBracket, "expected `]` after array elements");
277 277
278 278
    return node(p, ast::NodeValue::ArrayLit(items));
279 279
}
704 704
705 705
    if kind == ast::Builtin::SliceOf {
706 706
        // Parse comma-separated expressions until closing paren.
707 707
        // Argument count validation is done in semantic analysis.
708 708
        while not check(p, scanner::TokenKind::RParen) {
709 -
            args.append(try parseExpr(p), p.allocator);
709 +
            try! args.append(try parseExpr(p), p.allocator);
710 710
            if not consume(p, scanner::TokenKind::Comma) {
711 711
                break;
712 712
            }
713 713
        }
714 714
    } else {
715 -
        args.append(try parseType(p), p.allocator);
715 +
        try! args.append(try parseType(p), p.allocator);
716 716
    }
717 717
    try expect(p, scanner::TokenKind::RParen, "expected `)` after builtin argument");
718 718
719 719
    return node(p, ast::NodeValue::BuiltinCall { kind, args });
720 720
}
787 787
/// Parse leading attributes and declaration modifiers.
788 788
unsafe fn parseAttributes(p: &mut Parser) -> ?ast::Attributes {
789 789
    let mut attrs = ast::nodeSlice(p.arena, 4);
790 790
791 791
    if let attr = tryParseAnnotation(p) {
792 -
        attrs.append(attr, p.allocator);
792 +
        try! attrs.append(attr, p.allocator);
793 793
    }
794 794
    if consume(p, scanner::TokenKind::Export) {
795 -
        attrs.append(nodeAttribute(p, ast::Attribute::Export), p.allocator);
795 +
        try! attrs.append(nodeAttribute(p, ast::Attribute::Export), p.allocator);
796 796
    }
797 797
    if consume(p, scanner::TokenKind::Unsafe) {
798 -
        attrs.append(nodeAttribute(p, ast::Attribute::Unsafe), p.allocator);
798 +
        try! attrs.append(nodeAttribute(p, ast::Attribute::Unsafe), p.allocator);
799 799
    }
800 800
    if attrs.len > 0 {
801 801
        return ast::Attributes { list: attrs };
802 802
    }
803 803
    return nil;
961 961
    throws (ParseError)
962 962
{
963 963
    let mut statements = ast::nodeSlice(p.arena, capacity);
964 964
    while not check(p, end) {
965 965
        let stmt = try parseStmt(p);
966 -
        statements.append(stmt, p.allocator);
966 +
        try! statements.append(stmt, p.allocator);
967 967
968 968
        if check(p, end) or check(p, scanner::TokenKind::Eof) {
969 969
            break;
970 970
        }
971 971
        if not consume(p, scanner::TokenKind::Semicolon) {
1001 1001
    return node(p, ast::NodeValue::Block(blk));
1002 1002
}
1003 1003
1004 1004
/// Create a block containing a single statement node.
1005 1005
unsafe fn mkBlockWith(p: &mut Parser, node: *ast::Node) -> ast::Block {
1006 -
    let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator);
1006 +
    let stmts = try! ast::nodeSlice(p.arena, 1).append(node, p.allocator);
1007 1007
    return ast::Block { statements: stmts, isUnsafe: false };
1008 1008
}
1009 1009
1010 1010
/// Parse the branch that follows `else` in let-else style constructs.
1011 1011
///
1369 1369
        }
1370 1370
        let body = try parseBlock(p);
1371 1371
        let clause = node(p, ast::NodeValue::CatchClause(
1372 1372
            ast::CatchClause { binding, typeNode, body }
1373 1373
        ));
1374 -
        catches.append(clause, p.allocator);
1374 +
        try! catches.append(clause, p.allocator);
1375 1375
    }
1376 1376
    return node(p, ast::NodeValue::Try(
1377 1377
        ast::Try { expr, catches, shouldPanic, returnsOptional }
1378 1378
    ));
1379 1379
}
1456 1456
    let mut prongs = ast::nodeSlice(p.arena, 128);
1457 1457
    while not check(p, scanner::TokenKind::RBrace) and
1458 1458
          not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF.
1459 1459
    {
1460 1460
        let prongNode = try parseMatchProng(p);
1461 -
        prongs.append(prongNode, p.allocator);
1461 +
        try! prongs.append(prongNode, p.allocator);
1462 1462
        consume(p, scanner::TokenKind::Comma);
1463 1463
    }
1464 1464
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after match prongs");
1465 1465
1466 1466
    return node(p, ast::NodeValue::Match(
1477 1477
    // Case prong: `case <pattern>, ... if <guard> => <body>`.
1478 1478
    if consume(p, scanner::TokenKind::Case) {
1479 1479
        let mut patterns = ast::nodeSlice(p.arena, 16);
1480 1480
        loop {
1481 1481
            let pattern = try parseMatchPattern(p);
1482 -
            patterns.append(pattern, p.allocator);
1482 +
            try! patterns.append(pattern, p.allocator);
1483 1483
1484 1484
            if not consume(p, scanner::TokenKind::Comma) {
1485 1485
                break;
1486 1486
            }
1487 1487
            // After a comma, check for tokens that start a new prong.
1607 1607
                set recordField = ast::NodeValue::RecordField {
1608 1608
                    field: nil, type, value: nil,
1609 1609
                };
1610 1610
            }
1611 1611
        }
1612 -
        fields.append(node(p, recordField), p.allocator);
1612 +
        try! fields.append(node(p, recordField), p.allocator);
1613 1613
1614 1614
        if not consume(p, scanner::TokenKind::Comma) {
1615 1615
            break;
1616 1616
        }
1617 1617
    }
1627 1627
    if not consume(p, scanner::TokenKind::Colon) {
1628 1628
        return derives;
1629 1629
    }
1630 1630
    loop {
1631 1631
        let t = try parseIdent(p, "expected trait name in derive list");
1632 -
        derives.append(t, p.allocator);
1632 +
        try! derives.append(t, p.allocator);
1633 1633
1634 1634
        if not consume(p, scanner::TokenKind::Plus) {
1635 1635
            break;
1636 1636
        }
1637 1637
    }
1672 1672
        if consume(p, scanner::TokenKind::DotDot) {
1673 1673
            set ignoreRest = true;
1674 1674
            break;
1675 1675
        }
1676 1676
        let field = try parseRecordLitField(p);
1677 -
        fields.append(field, p.allocator);
1677 +
        try! fields.append(field, p.allocator);
1678 1678
1679 1679
        if not consume(p, scanner::TokenKind::Comma) {
1680 1680
            break;
1681 1681
        }
1682 1682
    }
1754 1754
        let variant = node(p, ast::NodeValue::UnionDeclVariant(
1755 1755
            ast::UnionDeclVariant {
1756 1756
                name: variantName, index: variants.len as u32, value: explicitValue, type: payloadType,
1757 1757
            }
1758 1758
        ));
1759 -
        variants.append(variant, p.allocator);
1759 +
        try! variants.append(variant, p.allocator);
1760 1760
1761 1761
        if not consume(p, scanner::TokenKind::Comma) {
1762 1762
            break;
1763 1763
        }
1764 1764
    }
1828 1828
    try expect(p, scanner::TokenKind::LParen, "expected `(` after function name");
1829 1829
    let mut params = ast::nodeSlice(p.arena, 8);
1830 1830
1831 1831
    while not check(p, scanner::TokenKind::RParen) {
1832 1832
        let param = try parseFnParam(p);
1833 -
        params.append(param, p.allocator);
1833 +
        try! params.append(param, p.allocator);
1834 1834
1835 1835
        if not consume(p, scanner::TokenKind::Comma) {
1836 1836
            break;
1837 1837
        }
1838 1838
    }
1875 1875
            // Keep existing attributes unchanged.
1876 1876
        } else {
1877 1877
            let mut list = ast::nodeSlice(p.arena, 4);
1878 1878
            if let a = attrs {
1879 1879
                for i in 0..a.list.len {
1880 -
                    list.append(a.list[i], p.allocator);
1880 +
                    try! list.append(a.list[i], p.allocator);
1881 1881
                }
1882 1882
            }
1883 1883
            let attrNode = nodeAttribute(p, ast::Attribute::Extern);
1884 -
            list.append(attrNode, p.allocator);
1884 +
            try! list.append(attrNode, p.allocator);
1885 1885
            set fnAttrs = ast::Attributes { list };
1886 1886
        }
1887 1887
    } else {
1888 1888
        set body = try parseBlock(p);
1889 1889
    }
2281 2281
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2282 2282
    while not check(p, scanner::TokenKind::RBrace) and
2283 2283
          not check(p, scanner::TokenKind::Eof)
2284 2284
    {
2285 2285
        let method = try parseTraitMethodSig(p);
2286 -
        methods.append(method, p.allocator);
2286 +
        try! methods.append(method, p.allocator);
2287 2287
    }
2288 2288
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after trait methods");
2289 2289
2290 2290
    return node(p, ast::NodeValue::TraitDecl { name, supertraits, methods, attrs });
2291 2291
}
2331 2331
    {
2332 2332
        let attrs = parseAttributes(p);
2333 2333
        try expect(p, scanner::TokenKind::Fn, "expected `fn`");
2334 2334
        let method = try parseMethodDecl(p, attrs);
2335 2335
2336 -
        methods.append(method, p.allocator);
2336 +
        try! methods.append(method, p.allocator);
2337 2337
    }
2338 2338
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods");
2339 2339
2340 2340
    return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods });
2341 2341
}
2375 2375
    try expect(p, open, listExpectMessage(open));
2376 2376
    let mut items = ast::nodeSlice(p.arena, 8);
2377 2377
2378 2378
    while not check(p, close) {
2379 2379
        let item = try parseItem(p);
2380 -
        items.append(item, p.allocator);
2380 +
        try! items.append(item, p.allocator);
2381 2381
2382 2382
        if not consume(p, scanner::TokenKind::Comma) {
2383 2383
            break;
2384 2384
        }
2385 2385
    }
lib/std/lang/resolver.rad +49 -32
700 700
        /// Method index in the v-table.
701 701
        methodIndex: u32,
702 702
    },
703 703
    /// Standalone method call metadata.
704 704
    MethodCall { method: *unsafe MethodEntry },
705 -
    /// Slice `.append(val, allocator)` method call.
705 +
    /// Slice `.append(value, allocator)` with the allocator's thrown errors.
706 706
    SliceAppend { elemType: *Type },
707 707
    /// Slice `.delete(index)` method call.
708 708
    SliceDelete { elemType: *Type },
709 709
}
710 710
3479 3479
    for paramNode in decl.sig.params {
3480 3480
        let paramTy = try infer(self, paramNode) catch e {
3481 3481
            exitFn(self);
3482 3482
            throw e;
3483 3483
        };
3484 -
        paramTypes.append(allocType(self, paramTy), a);
3484 +
        try! paramTypes.append(allocType(self, paramTy), a);
3485 3485
    }
3486 3486
3487 3487
    if decl.sig.throwList.len > MAX_FN_THROWS {
3488 3488
        exitFn(self);
3489 3489
        throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
3494 3494
    for throwNode in decl.sig.throwList {
3495 3495
        let throwTy = try infer(self, throwNode) catch e {
3496 3496
            exitFn(self);
3497 3497
            throw e;
3498 3498
        };
3499 -
        throwList.append(allocType(self, throwTy), a);
3499 +
        try! throwList.append(allocType(self, throwTy), a);
3500 3500
        try ensureStorableType(self, throwNode, throwTy);
3501 3501
    }
3502 3502
    exitFn(self);
3503 3503
    set fnType.paramTypes = &paramTypes[..];
3504 3504
    set fnType.throwList = &throwList[..];
3680 3680
3681 3681
        // Compute field offset by aligning to field's alignment.
3682 3682
        let fieldLayout = getTypeLayout(fieldType);
3683 3683
        set currentOffset = mem::alignUp(currentOffset, fieldLayout.alignment);
3684 3684
3685 -
        result.append(RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }, a);
3685 +
        try! result.append(RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }, a);
3686 3686
3687 3687
        // Advance offset past this field.
3688 3688
        set currentOffset += fieldLayout.size;
3689 3689
3690 3690
        // Track max alignment for record layout.
3824 3824
        // Copy inherited methods into this trait's method table.
3825 3825
        for inherited in superTrait.methods {
3826 3826
            if let _ = findTraitMethod(traitType, inherited.name) {
3827 3827
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
3828 3828
            }
3829 -
            traitType.methods.append(TraitMethod {
3829 +
            try! traitType.methods.append(TraitMethod {
3830 3830
                name: inherited.name,
3831 3831
                fnType: inherited.fnType,
3832 3832
                mutable: inherited.mutable,
3833 3833
                receiverClass: inherited.receiverClass,
3834 3834
                index: traitType.methods.len as u32,
3835 3835
            }, a);
3836 3836
        }
3837 -
        traitType.supertraits.append(superTrait, a);
3837 +
        try! traitType.supertraits.append(superTrait, a);
3838 3838
    }
3839 3839
3840 3840
    if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS {
3841 3841
        throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3842 3842
            expected: ast::MAX_TRAIT_METHODS,
3883 3883
                actual: sig.params.len,
3884 3884
            }));
3885 3885
        }
3886 3886
        for paramNode in sig.params {
3887 3887
            let paramTy = try infer(self, paramNode);
3888 -
            paramTypes.append(allocType(self, paramTy), a);
3888 +
            try! paramTypes.append(allocType(self, paramTy), a);
3889 3889
        }
3890 3890
        if let ret = sig.returnType {
3891 3891
            set retType = allocType(self, try infer(self, ret));
3892 3892
        }
3893 3893
        // Resolve throws list.
3897 3897
                actual: sig.throwList.len,
3898 3898
            }));
3899 3899
        }
3900 3900
        for throwNode in sig.throwList {
3901 3901
            let throwTy = try infer(self, throwNode);
3902 -
            throwList.append(allocType(self, throwTy), a);
3902 +
            try! throwList.append(allocType(self, throwTy), a);
3903 3903
        }
3904 3904
        let fnType = FnType {
3905 3905
            paramTypes: &paramTypes[..],
3906 3906
            returnType: retType,
3907 3907
            throwList: &throwList[..],
3908 3908
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3909 3909
        };
3910 -
        traitType.methods.append(TraitMethod {
3910 +
        try! traitType.methods.append(TraitMethod {
3911 3911
            name: methodName,
3912 3912
            fnType: allocFnType(self, fnType),
3913 3913
            mutable,
3914 3914
            receiverClass,
3915 3915
            index: traitType.methods.len as u32,
4090 4090
4091 4091
        // Build final function type: receiver plus trait's canonical types.
4092 4092
        let a = alloc::arenaAllocator(&mut self.arena);
4093 4093
        // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`?
4094 4094
        let mut paramTypes: *mut [*Type] = &mut [];
4095 -
        paramTypes.append(allocType(self, receiverPtrType), a);
4095 +
        try! paramTypes.append(allocType(self, receiverPtrType), a);
4096 4096
4097 4097
        for ty in tm.fnType.paramTypes {
4098 -
            paramTypes.append(ty, a);
4098 +
            try! paramTypes.append(ty, a);
4099 4099
        }
4100 4100
        let fnType = FnType {
4101 4101
            paramTypes: &paramTypes[..],
4102 4102
            returnType: tm.fnType.returnType,
4103 4103
            throwList: tm.fnType.throwList,
4241 4241
    let receiverPtrType = Type::Pointer {
4242 4242
        class: receiverClass,
4243 4243
        target: allocType(self, concreteType),
4244 4244
        mutable: receiverMut,
4245 4245
    };
4246 -
    paramTypes.append(allocType(self, receiverPtrType), a);
4246 +
    try! paramTypes.append(allocType(self, receiverPtrType), a);
4247 4247
4248 4248
    for paramNode in sig.params {
4249 4249
        let case ast::NodeValue::FnParam(param) = paramNode.value
4250 4250
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4251 4251
        let paramTy = try resolveValueType(self, param.type);
4252 -
        paramTypes.append(allocType(self, paramTy), a);
4252 +
        try! paramTypes.append(allocType(self, paramTy), a);
4253 4253
    }
4254 4254
4255 4255
    // Resolve return type.
4256 4256
    let mut returnType = Type::Void;
4257 4257
    if let retNode = sig.returnType {
4260 4260
4261 4261
    // Resolve throw list.
4262 4262
    let mut throwTypes: *mut [*Type] = &mut [];
4263 4263
    for throwNode in sig.throwList {
4264 4264
        let throwTy = try resolveValueType(self, throwNode);
4265 -
        throwTypes.append(allocType(self, throwTy), a);
4265 +
        try! throwTypes.append(allocType(self, throwTy), a);
4266 4266
    }
4267 4267
4268 4268
    let retTypePtr = allocType(self, returnType);
4269 4269
    let throwList = &throwTypes[..];
4270 4270
4393 4393
        let tag = variantTag(variantDecl, &mut iota);
4394 4394
        // Create a symbol for this variant.
4395 4395
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
4396 4396
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
4397 4397
4398 -
        variants.append(UnionVariant {
4398 +
        try! variants.append(UnionVariant {
4399 4399
            name: variantName,
4400 4400
            valueType: variantType,
4401 4401
            symbol: variantSym,
4402 4402
        }, a);
4403 4403
    }
5594 5594
5595 5595
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5596 5596
            let methodName = try nodeName(self, access.child);
5597 5597
            if methodName == "append" {
5598 5598
                return try resolveSliceAppend(
5599 -
                    self, node, access.parent, parentTy, call.args, item, mutable
5599 +
                    self, node, call, parentTy, item, mutable, ctx
5600 5600
                );
5601 5601
            }
5602 5602
            if methodName == "delete" {
5603 5603
                return try resolveSliceDelete(
5604 5604
                    self, node, access.parent, call.args, item, mutable
5706 5706
    {
5707 5707
        return false;
5708 5708
    }
5709 5709
    let case Type::Fn(callback) = func.fieldType else return false;
5710 5710
    let case Type::Pointer { target, .. } = ctx.fieldType else return false;
5711 -
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 {
5711 +
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len == 0 {
5712 5712
        return false;
5713 5713
    }
5714 5714
    if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or
5715 5715
       *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32
5716 5716
    {
5719 5719
    let case Type::Pointer { class, target: result, mutable } = *callback.returnType
5720 5720
        else return false;
5721 5721
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
5722 5722
}
5723 5723
5724 -
/// Resolve `slice.append(val, allocator)`.
5724 +
/// Resolve `slice.append(value, allocator)` with its allocation error.
5725 5725
unsafe fn resolveSliceAppend(
5726 5726
    self: &mut Resolver,
5727 5727
    node: *ast::Node,
5728 -
    parent: *ast::Node,
5728 +
    call: ast::Call,
5729 5729
    parentType: Type,
5730 -
    args: *[*ast::Node],
5731 5730
    elemType: *Type,
5732 -
    mutable: bool
5731 +
    mutable: bool,
5732 +
    ctx: CallCtx,
5733 5733
) -> Type throws (ResolveError) {
5734 +
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
5735 +
        else panic "resolveSliceAppend: expected method call";
5734 5736
    if not mutable {
5735 -
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5737 +
        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5736 5738
    }
5737 -
    if args.len <> 2 {
5739 +
    let count: u32 = 2;
5740 +
    let args = call.args;
5741 +
    if args.len <> count {
5738 5742
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5739 -
            expected: 2,
5740 -
            actual: args.len as u32,
5743 +
            expected: count,
5744 +
            actual: args.len,
5741 5745
        }));
5742 5746
    }
5743 5747
    // First argument must be assignable to the element type.
5744 5748
    try checkAssignable(self, args[0], *elemType);
5749 +
    let params = try! alloc::allocSlice(&mut self.arena, @sizeOf(*Type), @alignOf(*Type), count) as *mut [*Type];
5750 +
    set params[0] = elemType;
5745 5751
    // The allocator stores its callback and context at fixed offsets.
5746 5752
    let allocatorTy = try infer(self, args[1]);
5747 -
    if let case Type::Nominal(info) = allocatorTy {
5748 -
        try ensureNominalResolved(self, info, args[1]);
5749 -
    }
5753 +
    try ensureTypeResolved(self, allocatorTy, args[1]);
5750 5754
    if not isSliceAllocator(allocatorTy) {
5751 5755
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
5752 5756
    }
5757 +
    let case Type::Nominal(NominalType::Record(rec)) = allocatorTy else panic;
5758 +
    let case Type::Fn(callback) = rec.fields[0].fieldType else panic;
5759 +
    set params[1] = allocType(self, allocatorTy);
5760 +
    let info = allocFnType(self, FnType {
5761 +
        paramTypes: params,
5762 +
        returnType: allocType(self, parentType),
5763 +
        throwList: callback.throwList,
5764 +
        isUnsafe: false,
5765 +
    });
5766 +
    setNodeType(self, call.callee, Type::Fn(info));
5767 +
    if ctx == CallCtx::Normal {
5768 +
        throw emitError(self, node, ErrorKind::MissingTry);
5769 +
    }
5753 5770
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
5754 5771
5755 -
    // Return the parent's type so the caller can rebind:
5772 +
    // Return the parent's type so the caller can rebind.
5756 5773
    return setNodeType(self, node, parentType);
5757 5774
}
5758 5775
5759 5776
/// Resolve `slice.delete(index)`.
5760 5777
unsafe fn resolveSliceDelete(
7463 7480
                }));
7464 7481
            }
7465 7482
7466 7483
            for paramNode in t.params {
7467 7484
                let paramTy = try resolveValueType(self, paramNode);
7468 -
                paramTypes.append(allocType(self, paramTy), a);
7485 +
                try! paramTypes.append(allocType(self, paramTy), a);
7469 7486
            }
7470 7487
            for tyNode in t.throwList {
7471 7488
                let throwTy = try resolveValueType(self, tyNode);
7472 7489
                try ensureStorableType(self, tyNode, throwTy);
7473 -
                throwList.append(allocType(self, throwTy), a);
7490 +
                try! throwList.append(allocType(self, throwTy), a);
7474 7491
            }
7475 7492
            let mut retType = allocType(self, Type::Void);
7476 7493
            if let ret = t.returnType {
7477 7494
                let resolvedRet = try resolveValueType(self, ret);
7478 7495
                try ensureStorableType(self, ret, resolvedRet);
7515 7532
export unsafe fn resolveExpr(
7516 7533
    self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena
7517 7534
) -> Diagnostics throws (ResolveError) {
7518 7535
    let a = alloc::arenaAllocator(&mut arena.arena);
7519 7536
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
7520 -
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
7537 +
    let bodyStmts = try! ast::nodeSlice(arena, 1).append(exprStmt, a);
7521 7538
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
7522 7539
7523 7540
    let case ast::NodeValue::Block(block) = module.modBody.value
7524 7541
        else panic "resolveExpr: expected block for module body";
7525 7542
    enterScope(self, module.modBody);
lib/std/lang/resolver/printer.rad +1 -1
370 370
        }
371 371
        case super::ErrorKind::ExpectedPointer => {
372 372
            io::print("expected pointer type");
373 373
        }
374 374
        case super::ErrorKind::InvalidSliceAllocator => {
375 -
            io::print("expected allocator with func callback and matching opaque ctx pointer");
375 +
            io::print("expected allocator with throwing func callback and matching opaque ctx pointer");
376 376
        }
377 377
        case super::ErrorKind::ExpectedRecord => {
378 378
            io::print("expected record type");
379 379
        }
380 380
        case super::ErrorKind::ExpectedIndexable => {
lib/std/lang/resolver/tests.rad +32 -8
4330 4330
}
4331 4331
4332 4332
/// Test `.append()` on immutable slice produces an error.
4333 4333
@test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4334 4334
    let mut a = testResolver();
4335 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }";
4335 +
    let program = "union E: Copy { Exhausted } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn f(s: *[i32], a: A) { try! s.append(1, a); }";
4336 4336
    let result = try resolveProgramStr(&mut a, program);
4337 4337
    let err = try expectError(&result);
4338 4338
    let case super::ErrorKind::ImmutableBinding = err.kind
4339 4339
        else throw testing::TestError::Failed;
4340 4340
}
4342 4342
/// Test `.append()` with wrong argument count produces an error.
4343 4343
@test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4344 4344
    // Too few arguments.
4345 4345
    {
4346 4346
        let mut a = testResolver();
4347 -
        let program = "fn f(s: *mut [i32]) { s.append(1); }";
4347 +
        let program = "fn f(s: *mut [i32]) { try! s.append(1); }";
4348 4348
        let result = try resolveProgramStr(&mut a, program);
4349 4349
        let err = try expectError(&result);
4350 4350
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4351 4351
            else throw testing::TestError::Failed;
4352 4352
        try testing::expect(m.expected == 2);
4353 4353
        try testing::expect(m.actual == 1);
4354 4354
    }
4355 4355
    // Too many arguments.
4356 4356
    {
4357 4357
        let mut a = testResolver();
4358 -
        let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }";
4358 +
        let program = "union E: Copy { Exhausted } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn f(s: *mut [i32], a: A) { try! s.append(1, a, 0); }";
4359 4359
        let result = try resolveProgramStr(&mut a, program);
4360 4360
        let err = try expectError(&result);
4361 4361
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4362 4362
            else throw testing::TestError::Failed;
4363 4363
        try testing::expect(m.expected == 2);
4366 4366
}
4367 4367
4368 4368
/// Test `.append()` with correct arguments succeeds.
4369 4369
@test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4370 4370
    let mut a = testResolver();
4371 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }";
4371 +
    let program = "union E: Copy { Exhausted } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn f(s: *mut [i32], a: A) { try! s.append(1, a); }";
4372 4372
    let result = try resolveProgramStr(&mut a, program);
4373 4373
    try expectNoErrors(&result);
4374 4374
}
4375 4375
4376 4376
/// Test `.append()` with wrong element type produces an error.
4377 4377
@test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4378 4378
    let mut a = testResolver();
4379 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }";
4379 +
    let program = "union E: Copy { Exhausted } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn f(s: *mut [i32], a: A) { try! s.append(true, a); }";
4380 4380
    let result = try resolveProgramStr(&mut a, program);
4381 4381
    let err = try expectError(&result);
4382 4382
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4383 4383
        else throw testing::TestError::Failed;
4384 4384
}
6108 6108
        "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6109 6109
        "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6110 6110
        "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6111 6111
        "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6112 6112
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6113 -
        "union E: Copy { Bad } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6113 +
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { try! s.append(1, a); }",
6114 6114
    ];
6115 6115
    for program in programs {
6116 6116
        let mut a = testResolver();
6117 6117
        let result = try resolveProgramStr(&mut a, program);
6118 6118
        try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator);
6119 6119
    }
6120 6120
}
6121 6121
6122 6122
/// Slice allocators can use raw context pointers.
6123 6123
@test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) {
6124 -
    try expectAnalyzeOk("record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }");
6124 +
    try expectAnalyzeOk("union E: Copy { Exhausted } record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { try! s.append(1, a); }");
6125 6125
}
6126 6126
6127 6127
/// A live pattern reference protects its payload from writes through copied pointers.
6128 6128
@test unsafe fn testPatternPayloadPointerAliasesRejected() throws (testing::TestError) {
6129 6129
    let programs = &[
6313 6313
6314 6314
/// Slice mutations must preserve storage held by a local reference.
6315 6315
@test unsafe fn testLocalReferenceSliceMutationRejected() throws (testing::TestError) {
6316 6316
    let programs = &[
6317 6317
        "fn run(s: *mut [u8]) { let p: &u8 = &s[0]; s.delete(0); }",
6318 -
        "record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { let p: &u8 = &s[0]; s.append(1, a); }",
6318 +
        "union E: Copy { Exhausted } record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { let p: &u8 = &s[0]; try! s.append(1, a); }",
6319 6319
    ];
6320 6320
    for program in programs {
6321 6321
        let mut a = testResolver();
6322 6322
        let result = try resolveProgramStr(&mut a, program);
6323 6323
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s"));
6341 6341
@test unsafe fn testLocalReferenceDeclarationContext() throws (testing::TestError) {
6342 6342
    let mut a = testResolver();
6343 6343
    let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;");
6344 6344
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
6345 6345
}
6346 +
6347 +
/// Allocating append requires an explicit error handler.
6348 +
@test unsafe fn testSliceAppendRequiresTry() throws (testing::TestError) {
6349 +
    let mut a = testResolver();
6350 +
    let program = "union E: Copy { Full } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }";
6351 +
    let result = try resolveProgramStr(&mut a, program);
6352 +
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
6353 +
}
6354 +
6355 +
/// Append propagation requires every allocator error in the caller's throw list.
6356 +
@test unsafe fn testSliceAppendIncompatibleError() throws (testing::TestError) {
6357 +
    let mut a = testResolver();
6358 +
    let program = "union E: Copy { Full } union F: Copy { Invalid } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E, F), ctx: *mut opaque } fn run(s: *mut [u8], a: A) throws (E) { try s.append(1, a); }";
6359 +
    let result = try resolveProgramStr(&mut a, program);
6360 +
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
6361 +
}
6362 +
6363 +
/// Typed catches must cover all errors from the allocator.
6364 +
@test unsafe fn testSliceAppendIncompleteCatch() throws (testing::TestError) {
6365 +
    let mut a = testResolver();
6366 +
    let program = "union E: Copy { Full } union F: Copy { Invalid } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E, F), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { try s.append(1, a) catch e as E {}; }";
6367 +
    let result = try resolveProgramStr(&mut a, program);
6368 +
    try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
6369 +
}
seed/radiance.rv64 +0 -0

Binary file changed.

seed/radiance.rv64.git +1 -1
1 -
cbc895eb5c6c569c7648e51c5e4ff0579de8b925415ceb368100d07370b9fd7e
1 +
dee95968ebbc34ae4674a5b576baf5f0c7b8f055b2fa45b36c2aca09c2cfe41c
test/tests/slice.append.errors.rad added +89 -0
1 +
//! returns: 0
2 +
//! Slice append preserves allocator error types and payloads.
3 +
4 +
/// Allocation denial with a diagnostic value.
5 +
union Denied: Copy {
6 +
    /// Bytes requested by the caller.
7 +
    Request(u32),
8 +
}
9 +
10 +
/// Allocation state failure with a large payload.
11 +
record Unavailable: Copy {
12 +
    /// First diagnostic word.
13 +
    first: u64,
14 +
    /// Second diagnostic word.
15 +
    second: u64,
16 +
    /// Third diagnostic word.
17 +
    third: u64,
18 +
}
19 +
20 +
/// Allocator with two independent error types.
21 +
record Allocator: Copy {
22 +
    /// Allocation function.
23 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (Denied, Unavailable),
24 +
    /// Error selector and call count.
25 +
    ctx: *unsafe mut opaque,
26 +
}
27 +
28 +
/// Select which error to report.
29 +
record State {
30 +
    /// Select the large error payload.
31 +
    unavailable: bool,
32 +
    /// Number of allocation calls.
33 +
    calls: u32,
34 +
}
35 +
36 +
/// Return an error with its payload.
37 +
unsafe fn reject(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque throws (Denied, Unavailable) {
38 +
    let state = ctx as *unsafe mut State;
39 +
    set state.calls += 1;
40 +
    if state.unavailable {
41 +
        throw Unavailable { first: 11, second: 22, third: 33 };
42 +
    }
43 +
    throw Denied::Request(size);
44 +
}
45 +
46 +
/// Propagate each error from append.
47 +
fn append(items: &mut *mut [u32], a: Allocator) throws (Denied, Unavailable) {
48 +
    try items.append(7, a);
49 +
}
50 +
51 +
@default unsafe fn main() -> i32 {
52 +
    static state: State = State { unavailable: false, calls: 0 };
53 +
    let a = Allocator { func: reject, ctx: &mut state as *unsafe mut opaque };
54 +
    let mut items: *mut [u32] = &mut [];
55 +
    let mut caught: u32 = 0;
56 +
    try append(&mut items, a) catch e as Denied {
57 +
        let case Denied::Request(size) = e else panic;
58 +
        assert size == 4;
59 +
        set caught = 1;
60 +
    } catch e as Unavailable {
61 +
        panic "main: unexpected error";
62 +
    };
63 +
    assert caught == 1;
64 +
    assert items.len == 0;
65 +
    assert items.cap == 0;
66 +
    set state.unavailable = true;
67 +
    try items.append(8, a) catch e as Denied {
68 +
        panic "main: unexpected error";
69 +
    } catch e as Unavailable {
70 +
        assert e.first == 11;
71 +
        assert e.second == 22;
72 +
        assert e.third == 33;
73 +
        set caught = 2;
74 +
    };
75 +
    assert caught == 2;
76 +
    assert items.len == 0;
77 +
    assert items.cap == 0;
78 +
    let absent = try? items.append(9, a);
79 +
    assert absent == nil;
80 +
    assert state.calls == 3;
81 +
    static storage: [u32; 1] = [0];
82 +
    let mut reserved: *mut [u32] = &mut storage[..0];
83 +
    let present = try? reserved.append(10, a);
84 +
    assert present <> nil;
85 +
    assert reserved.len == 1;
86 +
    assert reserved[0] == 10;
87 +
    assert state.calls == 3;
88 +
    return 0;
89 +
}
test/tests/slice.append.failure.rad added +43 -0
1 +
//! returns: 0
2 +
//! Failed allocation leaves a slice unchanged.
3 +
4 +
/// Allocation failures.
5 +
union AllocError: Copy {
6 +
    /// The allocator cannot provide storage.
7 +
    OutOfMemory,
8 +
}
9 +
10 +
/// Allocation callback and context.
11 +
record Allocator: Copy {
12 +
    /// Fallible allocation function.
13 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (AllocError),
14 +
    /// Callback state.
15 +
    ctx: *unsafe mut opaque,
16 +
}
17 +
18 +
/// Reject every allocation request.
19 +
unsafe fn reject(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
20 +
    throw AllocError::OutOfMemory;
21 +
}
22 +
23 +
@default unsafe fn main() -> i32 {
24 +
    static storage: [i32; 2] = [11, 22];
25 +
    let a = Allocator { func: reject, ctx: &mut storage as *unsafe mut opaque };
26 +
    let mut items: *mut [i32] = (&mut storage[..1]) as *mut [i32];
27 +
    try! items.append(33, a);
28 +
    assert items.len == 2;
29 +
    assert items[1] == 33;
30 +
    let ptr = &items[0] as *opaque;
31 +
    let mut failed = false;
32 +
    try items.append(44, a) catch error as AllocError {
33 +
        assert error == AllocError::OutOfMemory;
34 +
        set failed = true;
35 +
    };
36 +
    assert failed;
37 +
    assert (&items[0] as *opaque) == ptr;
38 +
    assert items.len == 2;
39 +
    assert items.cap == 2;
40 +
    assert items[0] == 11;
41 +
    assert items[1] == 33;
42 +
    return 0;
43 +
}
test/tests/slice.append.rad +12 -184
1 -
//! returns: 0
2 -
//! Test slice .append() method with inline allocator.
1 +
//! Allocating append returns the updated slice or the allocator's error.
3 2
4 -
/// Simple bump allocator for testing.
5 -
record Arena {
6 -
    data: *mut [u8],
7 -
    offset: u32,
3 +
/// Allocation failures.
4 +
union Error: Copy {
5 +
    /// Diagnostic payload from the allocator.
6 +
    Failed(u64),
8 7
}
9 8
10 -
fn newArena(data: *mut [u8]) -> Arena {
11 -
    return Arena { data, offset: 0 };
12 -
}
13 -
14 -
fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque {
15 -
    let aligned = (arena.offset + al - 1) / al * al;
16 -
    let newOffset = aligned + size;
17 -
18 -
    assert newOffset <= arena.data.len as u32;
19 -
20 -
    let base: *mut u8 = &mut arena.data[aligned];
21 -
    set arena.offset = newOffset;
22 -
23 -
    return base as *mut opaque;
24 -
}
25 -
26 -
/// Allocator record matching the compiler's expected layout.
9 +
/// Allocation callback and context.
27 10
record Allocator: Copy {
28 -
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
11 +
    /// Fallible allocation function.
12 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (Error),
13 +
    /// Allocation state.
29 14
    ctx: *unsafe mut opaque,
30 15
}
31 16
32 -
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, al: u32) -> *mut opaque {
33 -
    let arena = ctx as *unsafe mut Arena;
34 -
    return arenaAlloc(&mut *arena, size, al);
35 -
}
36 -
37 -
unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator {
38 -
    return Allocator {
39 -
        func: arenaAllocFn,
40 -
        ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque,
41 -
    };
42 -
}
43 -
44 -
unsafe static BUF: [u8; 4096] = undefined;
45 -
46 -
@default unsafe fn main() -> i32 {
47 -
    static arena: Arena = undefined;
48 -
    set arena = newArena(&mut BUF[..]);
49 -
    let a = arenaAllocator(&mut arena);
50 -
51 -
    // Allocate initial capacity of 4.
52 -
    let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32));
53 -
    let mut nums = @sliceOf(ptr as *mut i32, 0, 4);
54 -
55 -
    // Append within capacity.
56 -
    nums.append(10, a);
57 -
    nums.append(20, a);
58 -
    nums.append(30, a);
59 -
60 -
    if nums.len <> 3 {
61 -
        return 1;
62 -
    }
63 -
    if nums.cap <> 4 {
64 -
        return 2;
65 -
    }
66 -
    if nums[0] <> 10 {
67 -
        return 3;
68 -
    }
69 -
    if nums[1] <> 20 {
70 -
        return 4;
71 -
    }
72 -
    if nums[2] <> 30 {
73 -
        return 5;
74 -
    }
75 -
76 -
    // Append to fill capacity.
77 -
    nums.append(40, a);
78 -
79 -
    if nums.len <> 4 {
80 -
        return 6;
81 -
    }
82 -
    if nums.cap <> 4 {
83 -
        return 7;
84 -
    }
85 -
86 -
    // Append past capacity -- triggers growth.
87 -
    nums.append(50, a);
88 -
89 -
    if nums.len <> 5 {
90 -
        return 8;
91 -
    }
92 -
    // Cap should have grown (cap * 2 | 1 = 4 * 2 | 1 = 9).
93 -
    if nums.cap <> 9 {
94 -
        return 9;
95 -
    }
96 -
    if nums[4] <> 50 {
97 -
        return 10;
98 -
    }
99 -
100 -
    // Verify old elements survived the copy.
101 -
    if nums[0] <> 10 {
102 -
        return 11;
103 -
    }
104 -
    if nums[3] <> 40 {
105 -
        return 12;
106 -
    }
107 -
108 -
    // Append from empty (cap == 0 triggers growth with cap = 1).
109 -
    let mut empty: *mut [i32] = &mut [];
110 -
    empty.append(99, a);
111 -
112 -
    if empty.len <> 1 {
113 -
        return 13;
114 -
    }
115 -
    if empty.cap <> 1 {
116 -
        return 14;
117 -
    }
118 -
    if empty[0] <> 99 {
119 -
        return 15;
120 -
    }
121 -
122 -
    // Sum all elements in nums.
123 -
    let mut sum: i32 = 0;
124 -
    for n in nums {
125 -
        set sum += n;
126 -
    }
127 -
    if sum <> 150 {
128 -
        return 16;
129 -
    }
130 -
131 -
    // Test append with u8 elements (stride = 1).
132 -
    let bytePtr = arenaAlloc(&mut arena, 2, 1);
133 -
    let mut bytes = @sliceOf(bytePtr as *mut u8, 0, 2);
134 -
    bytes.append(0xAB, a);
135 -
    bytes.append(0xCD, a);
136 -
137 -
    if bytes.len <> 2 {
138 -
        return 17;
139 -
    }
140 -
    if bytes[0] <> 0xAB {
141 -
        return 18;
142 -
    }
143 -
    if bytes[1] <> 0xCD {
144 -
        return 19;
145 -
    }
146 -
147 -
    // Trigger growth on u8 slice.
148 -
    bytes.append(0xEF, a);
149 -
150 -
    if bytes.len <> 3 {
151 -
        return 20;
152 -
    }
153 -
    if bytes.cap <> 5 {
154 -
        return 21;
155 -
    }
156 -
    if bytes[2] <> 0xEF {
157 -
        return 22;
158 -
    }
159 -
    // Verify old u8 elements survived.
160 -
    if bytes[0] <> 0xAB {
161 -
        return 23;
162 -
    }
163 -
164 -
    // Test that .append() returns the slice, allowing rebinding.
165 -
    let ptr2 = arenaAlloc(&mut arena, @sizeOf(i32) * 2, @alignOf(i32));
166 -
    let mut vals = @sliceOf(ptr2 as *mut i32, 0, 2);
167 -
168 -
    // Append within capacity, rebind via return value.
169 -
    set vals = vals.append(42, a);
170 -
    assert vals.len == 1;
171 -
    assert vals[0] == 42;
172 -
173 -
    set vals = vals.append(43, a);
174 -
    assert vals.len == 2;
175 -
176 -
    // Append past capacity -- triggers growth and returns updated slice.
177 -
    let oldPtr = &vals[0] as *opaque;
178 -
    set vals = vals.append(44, a);
179 -
    assert vals.len == 3;
180 -
    assert vals.cap == 5;
181 -
182 -
    // After growth, the data pointer should have changed.
183 -
    let newPtr = &vals[0] as *opaque;
184 -
    assert oldPtr <> newPtr;
185 -
186 -
    // Verify all elements survived.
187 -
    assert vals[0] == 42;
188 -
    assert vals[1] == 43;
189 -
    assert vals[2] == 44;
190 -
191 -
    return 0;
17 +
/// Append and propagate the allocator error.
18 +
fn append(items: *mut [i32], value: i32, a: Allocator) -> *mut [i32] throws (Error) {
19 +
    return try items.append(value, a);
192 20
}
test/tests/slice.append.ril +69 -794
1 -
data mut $BUF align 1 {
2 -
    undef * 4096;
3 -
}
4 -
5 -
data mut $main$nominal$arena align 8 {
6 -
    undef * 24;
7 -
}
8 -
9 -
fn w64 $newArena(w64 %0, w64 %1) {
10 -
  @entry0
11 -
    reserve %2 24 8;
12 -
    blit %2 %1 16;
13 -
    store w32 0 %2 16;
14 -
    blit %0 %2 24;
15 -
    ret %0;
16 -
}
17 -
18 -
fn w64 $arenaAlloc(w64 %0, w32 %1, w32 %2) {
19 -
  @entry0
20 -
    load w32 %3 %0 16;
21 -
    add w32 %4 %3 %2;
22 -
    sub w32 %5 %4 1;
23 -
    udiv w32 %6 %5 %2;
24 -
    mul w32 %7 %6 %2;
25 -
    add w32 %8 %7 %1;
26 -
    load w32 %9 %0 8;
27 -
    br.ult w32 %9 %8 @assert.fail1 @assert.ok2;
28 -
  @assert.fail1
29 -
    unreachable;
30 -
  @assert.ok2
31 -
    load w32 %10 %0 8;
32 -
    br.ult w32 %7 %10 @guard#pass3 @guard#trap4;
33 -
  @guard#pass3
34 -
    load w64 %11 %0 0;
35 -
    add w64 %12 %11 %7;
36 -
    store w32 %8 %0 16;
37 -
    ret %12;
38 -
  @guard#trap4
39 -
    ebreak;
40 -
    unreachable;
41 -
}
42 -
43 -
fn w64 $arenaAllocFn(w64 %0, w32 %1, w32 %2) {
44 -
  @entry0
45 -
    call w64 %3 $arenaAlloc(%0, %1, %2);
46 -
    ret %3;
47 -
}
48 -
49 -
fn w64 $arenaAllocator(w64 %0, w64 %1) {
1 +
fn w64 $append(w64 %0, w64 %1, w32 %2, w64 %3) {
50 2
  @entry0
51 -
    reserve %2 16 8;
52 -
    copy %3 $arenaAllocFn;
53 -
    store w64 %3 %2 0;
54 -
    store w64 %1 %2 8;
55 -
    blit %0 %2 16;
56 -
    ret %0;
57 -
}
58 -
59 -
fn w32 $main() {
60 -
  @entry0
61 -
    copy %0 $BUF;
62 -
    reserve %1 16 8;
63 -
    store w64 %0 %1 0;
64 -
    store w32 4096 %1 8;
65 -
    store w32 4096 %1 12;
66 -
    reserve %2 24 8;
67 -
    call w64 %3 $newArena(%2, %1);
68 -
    copy %4 $main$nominal$arena;
69 -
    blit %4 %3 24;
70 -
    copy %5 $main$nominal$arena;
71 -
    reserve %6 16 8;
72 -
    call w64 %7 $arenaAllocator(%6, %5);
73 -
    copy %8 $main$nominal$arena;
74 -
    mul w32 %9 4 4;
75 -
    call w64 %10 $arenaAlloc(%8, %9, 4);
76 -
    reserve %11 16 8;
77 -
    store w64 %10 %11 0;
78 -
    store w32 0 %11 8;
79 -
    store w32 4 %11 12;
80 -
    load w32 %12 %11 8;
81 -
    load w32 %13 %11 12;
82 -
    br.ult w32 %12 %13 @append.store1 @append.grow2;
3 +
    reserve %4 24 8;
4 +
    load w32 %5 %1 8;
5 +
    load w32 %6 %1 12;
6 +
    br.ult w32 %5 %6 @append.store1 @append.grow3;
83 7
  @append.store1
84 -
    load w64 %27 %11 0;
85 -
    mul w64 %28 %12 4;
86 -
    add w64 %29 %27 %28;
87 -
    store w32 10 %29 0;
88 -
    add w32 %30 %12 1;
89 -
    store w32 %30 %11 8;
90 -
    load w32 %35 %11 8;
91 -
    load w32 %36 %11 12;
92 -
    br.ult w32 %35 %36 @append.store6 @append.grow7;
93 -
  @append.grow2
94 -
    shl w32 %14 %13 1;
95 -
    or w32 %15 %14 1;
96 -
    load w64 %16 %7 0;
97 -
    load w64 %17 %7 8;
98 -
    mul w32 %18 %15 4;
99 -
    call w64 %19 %16(%17, %18, 4);
100 -
    load w64 %20 %11 0;
101 -
    mul w32 %21 %12 4;
102 -
    jmp @append3(0);
103 -
  @append3(w32 %22)
104 -
    br.ult w32 %22 %21 @append4 @append5;
105 -
  @append4
106 -
    add w64 %23 %20 %22;
107 -
    load w8 %24 %23 0;
108 -
    add w64 %25 %19 %22;
109 -
    store w8 %24 %25 0;
110 -
    add w32 %26 %22 1;
111 -
    jmp @append3(%26);
112 -
  @append5
113 -
    store w64 %19 %11 0;
114 -
    store w32 %15 %11 12;
115 -
    jmp @append.store1;
116 -
  @append.store6
117 -
    load w64 %50 %11 0;
118 -
    mul w64 %51 %35 4;
119 -
    add w64 %52 %50 %51;
120 -
    store w32 20 %52 0;
121 -
    add w32 %53 %35 1;
122 -
    store w32 %53 %11 8;
123 -
    load w32 %58 %11 8;
124 -
    load w32 %59 %11 12;
125 -
    br.ult w32 %58 %59 @append.store11 @append.grow12;
126 -
  @append.grow7
127 -
    shl w32 %37 %36 1;
128 -
    or w32 %38 %37 1;
129 -
    load w64 %39 %7 0;
130 -
    load w64 %40 %7 8;
131 -
    mul w32 %41 %38 4;
132 -
    call w64 %42 %39(%40, %41, 4);
133 -
    load w64 %43 %11 0;
134 -
    mul w32 %44 %35 4;
135 -
    jmp @append8(0);
136 -
  @append8(w32 %45)
137 -
    br.ult w32 %45 %44 @append9 @append10;
8 +
    load w64 %21 %1 0;
9 +
    mul w64 %22 %5 4;
10 +
    add w64 %23 %21 %22;
11 +
    store w32 %2 %23 0;
12 +
    add w32 %24 %5 1;
13 +
    store w32 %24 %1 8;
14 +
    store w64 0 %4 0;
15 +
    add w64 %25 %4 8;
16 +
    blit %25 %1 16;
17 +
    jmp @append.done2;
18 +
  @append.done2
19 +
    load w64 %26 %4 0;
20 +
    reserve %27 16 8;
21 +
    br.ne w32 %26 0 @err11 @ok10;
22 +
  @append.grow3
23 +
    br.ult w32 %6 536870912 @guard#pass4 @guard#trap5;
24 +
  @guard#pass4
25 +
    shl w32 %7 %6 1;
26 +
    or w32 %8 %7 1;
27 +
    load w64 %9 %3 0;
28 +
    load w64 %10 %3 8;
29 +
    mul w32 %11 %8 4;
30 +
    call w64 %9(%4, %10, %11, 4);
31 +
    load w64 %12 %4 0;
32 +
    br.ne w32 %12 0 @append.done2 @append.copy6;
33 +
  @guard#trap5
34 +
    ebreak;
35 +
    unreachable;
36 +
  @append.copy6
37 +
    load w64 %13 %4 8;
38 +
    load w64 %14 %1 0;
39 +
    mul w32 %15 %5 4;
40 +
    jmp @append7(0);
41 +
  @append7(w32 %16)
42 +
    br.ult w32 %16 %15 @append8 @append9;
43 +
  @append8
44 +
    add w64 %17 %14 %16;
45 +
    load w8 %18 %17 0;
46 +
    add w64 %19 %13 %16;
47 +
    store w8 %18 %19 0;
48 +
    add w32 %20 %16 1;
49 +
    jmp @append7(%20);
138 50
  @append9
139 -
    add w64 %46 %43 %45;
140 -
    load w8 %47 %46 0;
141 -
    add w64 %48 %42 %45;
142 -
    store w8 %47 %48 0;
143 -
    add w32 %49 %45 1;
144 -
    jmp @append8(%49);
145 -
  @append10
146 -
    store w64 %42 %11 0;
147 -
    store w32 %38 %11 12;
148 -
    jmp @append.store6;
149 -
  @append.store11
150 -
    load w64 %73 %11 0;
151 -
    mul w64 %74 %58 4;
152 -
    add w64 %75 %73 %74;
153 -
    store w32 30 %75 0;
154 -
    add w32 %76 %58 1;
155 -
    store w32 %76 %11 8;
156 -
    load w32 %79 %11 8;
157 -
    br.ne w32 %79 3 @then16 @merge17;
158 -
  @append.grow12
159 -
    shl w32 %60 %59 1;
160 -
    or w32 %61 %60 1;
161 -
    load w64 %62 %7 0;
162 -
    load w64 %63 %7 8;
163 -
    mul w32 %64 %61 4;
164 -
    call w64 %65 %62(%63, %64, 4);
165 -
    load w64 %66 %11 0;
166 -
    mul w32 %67 %58 4;
167 -
    jmp @append13(0);
168 -
  @append13(w32 %68)
169 -
    br.ult w32 %68 %67 @append14 @append15;
170 -
  @append14
171 -
    add w64 %69 %66 %68;
172 -
    load w8 %70 %69 0;
173 -
    add w64 %71 %65 %68;
174 -
    store w8 %70 %71 0;
175 -
    add w32 %72 %68 1;
176 -
    jmp @append13(%72);
177 -
  @append15
178 -
    store w64 %65 %11 0;
179 -
    store w32 %61 %11 12;
180 -
    jmp @append.store11;
181 -
  @then16
182 -
    ret 1;
183 -
  @merge17
184 -
    load w32 %80 %11 12;
185 -
    br.ne w32 %80 4 @then18 @merge19;
186 -
  @then18
187 -
    ret 2;
188 -
  @merge19
189 -
    load w32 %81 %11 8;
190 -
    br.ult w32 0 %81 @guard#pass22 @guard#trap23;
191 -
  @then20
192 -
    ret 3;
193 -
  @merge21
194 -
    load w32 %84 %11 8;
195 -
    br.ult w32 1 %84 @guard#pass26 @guard#trap27;
196 -
  @guard#pass22
197 -
    load w64 %82 %11 0;
198 -
    sload w32 %83 %82 0;
199 -
    br.ne w32 %83 10 @then20 @merge21;
200 -
  @guard#trap23
201 -
    ebreak;
202 -
    unreachable;
203 -
  @then24
204 -
    ret 4;
205 -
  @merge25
206 -
    load w32 %89 %11 8;
207 -
    br.ult w32 2 %89 @guard#pass30 @guard#trap31;
208 -
  @guard#pass26
209 -
    load w64 %85 %11 0;
210 -
    mul w64 %86 1 4;
211 -
    add w64 %87 %85 %86;
212 -
    sload w32 %88 %87 0;
213 -
    br.ne w32 %88 20 @then24 @merge25;
214 -
  @guard#trap27
215 -
    ebreak;
216 -
    unreachable;
217 -
  @then28
218 -
    ret 5;
219 -
  @merge29
220 -
    load w32 %96 %11 8;
221 -
    load w32 %97 %11 12;
222 -
    br.ult w32 %96 %97 @append.store32 @append.grow33;
223 -
  @guard#pass30
224 -
    load w64 %90 %11 0;
225 -
    mul w64 %91 2 4;
226 -
    add w64 %92 %90 %91;
227 -
    sload w32 %93 %92 0;
228 -
    br.ne w32 %93 30 @then28 @merge29;
229 -
  @guard#trap31
230 -
    ebreak;
231 -
    unreachable;
232 -
  @append.store32
233 -
    load w64 %111 %11 0;
234 -
    mul w64 %112 %96 4;
235 -
    add w64 %113 %111 %112;
236 -
    store w32 40 %113 0;
237 -
    add w32 %114 %96 1;
238 -
    store w32 %114 %11 8;
239 -
    load w32 %117 %11 8;
240 -
    br.ne w32 %117 4 @then37 @merge38;
241 -
  @append.grow33
242 -
    shl w32 %98 %97 1;
243 -
    or w32 %99 %98 1;
244 -
    load w64 %100 %7 0;
245 -
    load w64 %101 %7 8;
246 -
    mul w32 %102 %99 4;
247 -
    call w64 %103 %100(%101, %102, 4);
248 -
    load w64 %104 %11 0;
249 -
    mul w32 %105 %96 4;
250 -
    jmp @append34(0);
251 -
  @append34(w32 %106)
252 -
    br.ult w32 %106 %105 @append35 @append36;
253 -
  @append35
254 -
    add w64 %107 %104 %106;
255 -
    load w8 %108 %107 0;
256 -
    add w64 %109 %103 %106;
257 -
    store w8 %108 %109 0;
258 -
    add w32 %110 %106 1;
259 -
    jmp @append34(%110);
260 -
  @append36
261 -
    store w64 %103 %11 0;
262 -
    store w32 %99 %11 12;
263 -
    jmp @append.store32;
264 -
  @then37
265 -
    ret 6;
266 -
  @merge38
267 -
    load w32 %118 %11 12;
268 -
    br.ne w32 %118 4 @then39 @merge40;
269 -
  @then39
270 -
    ret 7;
271 -
  @merge40
272 -
    load w32 %121 %11 8;
273 -
    load w32 %122 %11 12;
274 -
    br.ult w32 %121 %122 @append.store41 @append.grow42;
275 -
  @append.store41
276 -
    load w64 %136 %11 0;
277 -
    mul w64 %137 %121 4;
278 -
    add w64 %138 %136 %137;
279 -
    store w32 50 %138 0;
280 -
    add w32 %139 %121 1;
281 -
    store w32 %139 %11 8;
282 -
    load w32 %142 %11 8;
283 -
    br.ne w32 %142 5 @then46 @merge47;
284 -
  @append.grow42
285 -
    shl w32 %123 %122 1;
286 -
    or w32 %124 %123 1;
287 -
    load w64 %125 %7 0;
288 -
    load w64 %126 %7 8;
289 -
    mul w32 %127 %124 4;
290 -
    call w64 %128 %125(%126, %127, 4);
291 -
    load w64 %129 %11 0;
292 -
    mul w32 %130 %121 4;
293 -
    jmp @append43(0);
294 -
  @append43(w32 %131)
295 -
    br.ult w32 %131 %130 @append44 @append45;
296 -
  @append44
297 -
    add w64 %132 %129 %131;
298 -
    load w8 %133 %132 0;
299 -
    add w64 %134 %128 %131;
300 -
    store w8 %133 %134 0;
301 -
    add w32 %135 %131 1;
302 -
    jmp @append43(%135);
303 -
  @append45
304 -
    store w64 %128 %11 0;
305 -
    store w32 %124 %11 12;
306 -
    jmp @append.store41;
307 -
  @then46
308 -
    ret 8;
309 -
  @merge47
310 -
    load w32 %143 %11 12;
311 -
    br.ne w32 %143 9 @then48 @merge49;
312 -
  @then48
313 -
    ret 9;
314 -
  @merge49
315 -
    load w32 %144 %11 8;
316 -
    br.ult w32 4 %144 @guard#pass52 @guard#trap53;
317 -
  @then50
318 -
    ret 10;
319 -
  @merge51
320 -
    load w32 %149 %11 8;
321 -
    br.ult w32 0 %149 @guard#pass56 @guard#trap57;
322 -
  @guard#pass52
323 -
    load w64 %145 %11 0;
324 -
    mul w64 %146 4 4;
325 -
    add w64 %147 %145 %146;
326 -
    sload w32 %148 %147 0;
327 -
    br.ne w32 %148 50 @then50 @merge51;
328 -
  @guard#trap53
329 -
    ebreak;
330 -
    unreachable;
331 -
  @then54
332 -
    ret 11;
333 -
  @merge55
334 -
    load w32 %152 %11 8;
335 -
    br.ult w32 3 %152 @guard#pass60 @guard#trap61;
336 -
  @guard#pass56
337 -
    load w64 %150 %11 0;
338 -
    sload w32 %151 %150 0;
339 -
    br.ne w32 %151 10 @then54 @merge55;
340 -
  @guard#trap57
341 -
    ebreak;
342 -
    unreachable;
343 -
  @then58
344 -
    ret 12;
345 -
  @merge59
346 -
    reserve %157 16 8;
347 -
    store w64 0 %157 0;
348 -
    store w32 0 %157 8;
349 -
    store w32 0 %157 12;
350 -
    load w32 %160 %157 8;
351 -
    load w32 %161 %157 12;
352 -
    br.ult w32 %160 %161 @append.store62 @append.grow63;
353 -
  @guard#pass60
354 -
    load w64 %153 %11 0;
355 -
    mul w64 %154 3 4;
356 -
    add w64 %155 %153 %154;
357 -
    sload w32 %156 %155 0;
358 -
    br.ne w32 %156 40 @then58 @merge59;
359 -
  @guard#trap61
360 -
    ebreak;
361 -
    unreachable;
362 -
  @append.store62
363 -
    load w64 %175 %157 0;
364 -
    mul w64 %176 %160 4;
365 -
    add w64 %177 %175 %176;
366 -
    store w32 99 %177 0;
367 -
    add w32 %178 %160 1;
368 -
    store w32 %178 %157 8;
369 -
    load w32 %181 %157 8;
370 -
    br.ne w32 %181 1 @then67 @merge68;
371 -
  @append.grow63
372 -
    shl w32 %162 %161 1;
373 -
    or w32 %163 %162 1;
374 -
    load w64 %164 %7 0;
375 -
    load w64 %165 %7 8;
376 -
    mul w32 %166 %163 4;
377 -
    call w64 %167 %164(%165, %166, 4);
378 -
    load w64 %168 %157 0;
379 -
    mul w32 %169 %160 4;
380 -
    jmp @append64(0);
381 -
  @append64(w32 %170)
382 -
    br.ult w32 %170 %169 @append65 @append66;
383 -
  @append65
384 -
    add w64 %171 %168 %170;
385 -
    load w8 %172 %171 0;
386 -
    add w64 %173 %167 %170;
387 -
    store w8 %172 %173 0;
388 -
    add w32 %174 %170 1;
389 -
    jmp @append64(%174);
390 -
  @append66
391 -
    store w64 %167 %157 0;
392 -
    store w32 %163 %157 12;
393 -
    jmp @append.store62;
394 -
  @then67
395 -
    ret 13;
396 -
  @merge68
397 -
    load w32 %182 %157 12;
398 -
    br.ne w32 %182 1 @then69 @merge70;
399 -
  @then69
400 -
    ret 14;
401 -
  @merge70
402 -
    load w32 %183 %157 8;
403 -
    br.ult w32 0 %183 @guard#pass73 @guard#trap74;
404 -
  @then71
405 -
    ret 15;
406 -
  @merge72
407 -
    load w32 %188 %11 8;
408 -
    load w64 %189 %11 0;
409 -
    jmp @loop75(0, 0);
410 -
  @guard#pass73
411 -
    load w64 %184 %157 0;
412 -
    sload w32 %185 %184 0;
413 -
    br.ne w32 %185 99 @then71 @merge72;
414 -
  @guard#trap74
415 -
    ebreak;
416 -
    unreachable;
417 -
  @loop75(w32 %190, w32 %194)
418 -
    br.slt w32 %190 %188 @body76 @merge77;
419 -
  @body76
420 -
    mul w64 %191 %190 4;
421 -
    add w64 %192 %189 %191;
422 -
    sload w32 %193 %192 0;
423 -
    add w32 %195 %194 %193;
424 -
    add w32 %196 %190 1;
425 -
    jmp @loop75(%196, %195);
426 -
  @merge77
427 -
    br.ne w32 %194 150 @then78 @merge79;
428 -
  @then78
429 -
    ret 16;
430 -
  @merge79
431 -
    copy %197 $main$nominal$arena;
432 -
    call w64 %198 $arenaAlloc(%197, 2, 1);
433 -
    reserve %199 16 8;
434 -
    store w64 %198 %199 0;
435 -
    store w32 0 %199 8;
436 -
    store w32 2 %199 12;
437 -
    load w32 %203 %199 8;
438 -
    load w32 %204 %199 12;
439 -
    br.ult w32 %203 %204 @append.store80 @append.grow81;
440 -
  @append.store80
441 -
    load w64 %218 %199 0;
442 -
    add w64 %219 %218 %203;
443 -
    store w8 171 %219 0;
444 -
    add w32 %220 %203 1;
445 -
    store w32 %220 %199 8;
446 -
    load w32 %225 %199 8;
447 -
    load w32 %226 %199 12;
448 -
    br.ult w32 %225 %226 @append.store85 @append.grow86;
449 -
  @append.grow81
450 -
    shl w32 %205 %204 1;
451 -
    or w32 %206 %205 1;
452 -
    load w64 %207 %7 0;
453 -
    load w64 %208 %7 8;
454 -
    mul w32 %209 %206 1;
455 -
    call w64 %210 %207(%208, %209, 1);
456 -
    load w64 %211 %199 0;
457 -
    mul w32 %212 %203 1;
458 -
    jmp @append82(0);
459 -
  @append82(w32 %213)
460 -
    br.ult w32 %213 %212 @append83 @append84;
461 -
  @append83
462 -
    add w64 %214 %211 %213;
463 -
    load w8 %215 %214 0;
464 -
    add w64 %216 %210 %213;
465 -
    store w8 %215 %216 0;
466 -
    add w32 %217 %213 1;
467 -
    jmp @append82(%217);
468 -
  @append84
469 -
    store w64 %210 %199 0;
470 -
    store w32 %206 %199 12;
471 -
    jmp @append.store80;
472 -
  @append.store85
473 -
    load w64 %240 %199 0;
474 -
    add w64 %241 %240 %225;
475 -
    store w8 205 %241 0;
476 -
    add w32 %242 %225 1;
477 -
    store w32 %242 %199 8;
478 -
    load w32 %245 %199 8;
479 -
    br.ne w32 %245 2 @then90 @merge91;
480 -
  @append.grow86
481 -
    shl w32 %227 %226 1;
482 -
    or w32 %228 %227 1;
483 -
    load w64 %229 %7 0;
484 -
    load w64 %230 %7 8;
485 -
    mul w32 %231 %228 1;
486 -
    call w64 %232 %229(%230, %231, 1);
487 -
    load w64 %233 %199 0;
488 -
    mul w32 %234 %225 1;
489 -
    jmp @append87(0);
490 -
  @append87(w32 %235)
491 -
    br.ult w32 %235 %234 @append88 @append89;
492 -
  @append88
493 -
    add w64 %236 %233 %235;
494 -
    load w8 %237 %236 0;
495 -
    add w64 %238 %232 %235;
496 -
    store w8 %237 %238 0;
497 -
    add w32 %239 %235 1;
498 -
    jmp @append87(%239);
499 -
  @append89
500 -
    store w64 %232 %199 0;
501 -
    store w32 %228 %199 12;
502 -
    jmp @append.store85;
503 -
  @then90
504 -
    ret 17;
505 -
  @merge91
506 -
    load w32 %246 %199 8;
507 -
    br.ult w32 0 %246 @guard#pass94 @guard#trap95;
508 -
  @then92
509 -
    ret 18;
510 -
  @merge93
511 -
    load w32 %249 %199 8;
512 -
    br.ult w32 1 %249 @guard#pass98 @guard#trap99;
513 -
  @guard#pass94
514 -
    load w64 %247 %199 0;
515 -
    load w8 %248 %247 0;
516 -
    br.ne w8 %248 171 @then92 @merge93;
517 -
  @guard#trap95
518 -
    ebreak;
519 -
    unreachable;
520 -
  @then96
521 -
    ret 19;
522 -
  @merge97
523 -
    load w32 %255 %199 8;
524 -
    load w32 %256 %199 12;
525 -
    br.ult w32 %255 %256 @append.store100 @append.grow101;
526 -
  @guard#pass98
527 -
    load w64 %250 %199 0;
528 -
    add w64 %251 %250 1;
529 -
    load w8 %252 %251 0;
530 -
    br.ne w8 %252 205 @then96 @merge97;
531 -
  @guard#trap99
532 -
    ebreak;
533 -
    unreachable;
534 -
  @append.store100
535 -
    load w64 %270 %199 0;
536 -
    add w64 %271 %270 %255;
537 -
    store w8 239 %271 0;
538 -
    add w32 %272 %255 1;
539 -
    store w32 %272 %199 8;
540 -
    load w32 %275 %199 8;
541 -
    br.ne w32 %275 3 @then105 @merge106;
542 -
  @append.grow101
543 -
    shl w32 %257 %256 1;
544 -
    or w32 %258 %257 1;
545 -
    load w64 %259 %7 0;
546 -
    load w64 %260 %7 8;
547 -
    mul w32 %261 %258 1;
548 -
    call w64 %262 %259(%260, %261, 1);
549 -
    load w64 %263 %199 0;
550 -
    mul w32 %264 %255 1;
551 -
    jmp @append102(0);
552 -
  @append102(w32 %265)
553 -
    br.ult w32 %265 %264 @append103 @append104;
554 -
  @append103
555 -
    add w64 %266 %263 %265;
556 -
    load w8 %267 %266 0;
557 -
    add w64 %268 %262 %265;
558 -
    store w8 %267 %268 0;
559 -
    add w32 %269 %265 1;
560 -
    jmp @append102(%269);
561 -
  @append104
562 -
    store w64 %262 %199 0;
563 -
    store w32 %258 %199 12;
564 -
    jmp @append.store100;
565 -
  @then105
566 -
    ret 20;
567 -
  @merge106
568 -
    load w32 %276 %199 12;
569 -
    br.ne w32 %276 5 @then107 @merge108;
570 -
  @then107
571 -
    ret 21;
572 -
  @merge108
573 -
    load w32 %277 %199 8;
574 -
    br.ult w32 2 %277 @guard#pass111 @guard#trap112;
575 -
  @then109
576 -
    ret 22;
577 -
  @merge110
578 -
    load w32 %281 %199 8;
579 -
    br.ult w32 0 %281 @guard#pass115 @guard#trap116;
580 -
  @guard#pass111
581 -
    load w64 %278 %199 0;
582 -
    add w64 %279 %278 2;
583 -
    load w8 %280 %279 0;
584 -
    br.ne w8 %280 239 @then109 @merge110;
585 -
  @guard#trap112
586 -
    ebreak;
587 -
    unreachable;
588 -
  @then113
589 -
    ret 23;
590 -
  @merge114
591 -
    copy %284 $main$nominal$arena;
592 -
    mul w32 %285 4 2;
593 -
    call w64 %286 $arenaAlloc(%284, %285, 4);
594 -
    reserve %287 16 8;
595 -
    store w64 %286 %287 0;
596 -
    store w32 0 %287 8;
597 -
    store w32 2 %287 12;
598 -
    load w32 %290 %287 8;
599 -
    load w32 %291 %287 12;
600 -
    br.ult w32 %290 %291 @append.store117 @append.grow118;
601 -
  @guard#pass115
602 -
    load w64 %282 %199 0;
603 -
    load w8 %283 %282 0;
604 -
    br.ne w8 %283 171 @then113 @merge114;
605 -
  @guard#trap116
606 -
    ebreak;
607 -
    unreachable;
608 -
  @append.store117
609 -
    load w64 %305 %287 0;
610 -
    mul w64 %306 %290 4;
611 -
    add w64 %307 %305 %306;
612 -
    store w32 42 %307 0;
613 -
    add w32 %308 %290 1;
614 -
    store w32 %308 %287 8;
615 -
    blit %287 %287 16;
616 -
    load w32 %311 %287 8;
617 -
    br.eq w32 %311 1 @assert.ok123 @assert.fail122;
618 -
  @append.grow118
619 -
    shl w32 %292 %291 1;
620 -
    or w32 %293 %292 1;
621 -
    load w64 %294 %7 0;
622 -
    load w64 %295 %7 8;
623 -
    mul w32 %296 %293 4;
624 -
    call w64 %297 %294(%295, %296, 4);
625 -
    load w64 %298 %287 0;
626 -
    mul w32 %299 %290 4;
627 -
    jmp @append119(0);
628 -
  @append119(w32 %300)
629 -
    br.ult w32 %300 %299 @append120 @append121;
630 -
  @append120
631 -
    add w64 %301 %298 %300;
632 -
    load w8 %302 %301 0;
633 -
    add w64 %303 %297 %300;
634 -
    store w8 %302 %303 0;
635 -
    add w32 %304 %300 1;
636 -
    jmp @append119(%304);
637 -
  @append121
638 -
    store w64 %297 %287 0;
639 -
    store w32 %293 %287 12;
640 -
    jmp @append.store117;
641 -
  @assert.fail122
642 -
    unreachable;
643 -
  @assert.ok123
644 -
    load w32 %312 %287 8;
645 -
    br.ult w32 0 %312 @guard#pass126 @guard#trap127;
646 -
  @assert.fail124
647 -
    unreachable;
648 -
  @assert.ok125
649 -
    load w32 %317 %287 8;
650 -
    load w32 %318 %287 12;
651 -
    br.ult w32 %317 %318 @append.store128 @append.grow129;
652 -
  @guard#pass126
653 -
    load w64 %313 %287 0;
654 -
    sload w32 %314 %313 0;
655 -
    br.eq w32 %314 42 @assert.ok125 @assert.fail124;
656 -
  @guard#trap127
657 -
    ebreak;
658 -
    unreachable;
659 -
  @append.store128
660 -
    load w64 %332 %287 0;
661 -
    mul w64 %333 %317 4;
662 -
    add w64 %334 %332 %333;
663 -
    store w32 43 %334 0;
664 -
    add w32 %335 %317 1;
665 -
    store w32 %335 %287 8;
666 -
    blit %287 %287 16;
667 -
    load w32 %338 %287 8;
668 -
    br.eq w32 %338 2 @assert.ok134 @assert.fail133;
669 -
  @append.grow129
670 -
    shl w32 %319 %318 1;
671 -
    or w32 %320 %319 1;
672 -
    load w64 %321 %7 0;
673 -
    load w64 %322 %7 8;
674 -
    mul w32 %323 %320 4;
675 -
    call w64 %324 %321(%322, %323, 4);
676 -
    load w64 %325 %287 0;
677 -
    mul w32 %326 %317 4;
678 -
    jmp @append130(0);
679 -
  @append130(w32 %327)
680 -
    br.ult w32 %327 %326 @append131 @append132;
681 -
  @append131
682 -
    add w64 %328 %325 %327;
683 -
    load w8 %329 %328 0;
684 -
    add w64 %330 %324 %327;
685 -
    store w8 %329 %330 0;
686 -
    add w32 %331 %327 1;
687 -
    jmp @append130(%331);
688 -
  @append132
689 -
    store w64 %324 %287 0;
690 -
    store w32 %320 %287 12;
691 -
    jmp @append.store128;
692 -
  @assert.fail133
693 -
    unreachable;
694 -
  @assert.ok134
695 -
    load w32 %339 %287 8;
696 -
    br.ult w32 0 %339 @guard#pass135 @guard#trap136;
697 -
  @guard#pass135
698 -
    load w64 %340 %287 0;
699 -
    load w32 %343 %287 8;
700 -
    load w32 %344 %287 12;
701 -
    br.ult w32 %343 %344 @append.store137 @append.grow138;
702 -
  @guard#trap136
703 -
    ebreak;
704 -
    unreachable;
705 -
  @append.store137
706 -
    load w64 %358 %287 0;
707 -
    mul w64 %359 %343 4;
708 -
    add w64 %360 %358 %359;
709 -
    store w32 44 %360 0;
710 -
    add w32 %361 %343 1;
711 -
    store w32 %361 %287 8;
712 -
    blit %287 %287 16;
713 -
    load w32 %364 %287 8;
714 -
    br.eq w32 %364 3 @assert.ok143 @assert.fail142;
715 -
  @append.grow138
716 -
    shl w32 %345 %344 1;
717 -
    or w32 %346 %345 1;
718 -
    load w64 %347 %7 0;
719 -
    load w64 %348 %7 8;
720 -
    mul w32 %349 %346 4;
721 -
    call w64 %350 %347(%348, %349, 4);
722 -
    load w64 %351 %287 0;
723 -
    mul w32 %352 %343 4;
724 -
    jmp @append139(0);
725 -
  @append139(w32 %353)
726 -
    br.ult w32 %353 %352 @append140 @append141;
727 -
  @append140
728 -
    add w64 %354 %351 %353;
729 -
    load w8 %355 %354 0;
730 -
    add w64 %356 %350 %353;
731 -
    store w8 %355 %356 0;
732 -
    add w32 %357 %353 1;
733 -
    jmp @append139(%357);
734 -
  @append141
735 -
    store w64 %350 %287 0;
736 -
    store w32 %346 %287 12;
737 -
    jmp @append.store137;
738 -
  @assert.fail142
739 -
    unreachable;
740 -
  @assert.ok143
741 -
    load w32 %365 %287 12;
742 -
    br.eq w32 %365 5 @assert.ok145 @assert.fail144;
743 -
  @assert.fail144
744 -
    unreachable;
745 -
  @assert.ok145
746 -
    load w32 %366 %287 8;
747 -
    br.ult w32 0 %366 @guard#pass146 @guard#trap147;
748 -
  @guard#pass146
749 -
    load w64 %367 %287 0;
750 -
    br.ne w64 %340 %367 @assert.ok149 @assert.fail148;
751 -
  @guard#trap147
752 -
    ebreak;
753 -
    unreachable;
754 -
  @assert.fail148
755 -
    unreachable;
756 -
  @assert.ok149
757 -
    load w32 %370 %287 8;
758 -
    br.ult w32 0 %370 @guard#pass152 @guard#trap153;
759 -
  @assert.fail150
760 -
    unreachable;
761 -
  @assert.ok151
762 -
    load w32 %373 %287 8;
763 -
    br.ult w32 1 %373 @guard#pass156 @guard#trap157;
764 -
  @guard#pass152
765 -
    load w64 %371 %287 0;
766 -
    sload w32 %372 %371 0;
767 -
    br.eq w32 %372 42 @assert.ok151 @assert.fail150;
768 -
  @guard#trap153
769 -
    ebreak;
770 -
    unreachable;
771 -
  @assert.fail154
772 -
    unreachable;
773 -
  @assert.ok155
774 -
    load w32 %378 %287 8;
775 -
    br.ult w32 2 %378 @guard#pass160 @guard#trap161;
776 -
  @guard#pass156
777 -
    load w64 %374 %287 0;
778 -
    mul w64 %375 1 4;
779 -
    add w64 %376 %374 %375;
780 -
    sload w32 %377 %376 0;
781 -
    br.eq w32 %377 43 @assert.ok155 @assert.fail154;
782 -
  @guard#trap157
783 -
    ebreak;
784 -
    unreachable;
785 -
  @assert.fail158
786 -
    unreachable;
787 -
  @assert.ok159
788 -
    ret 0;
789 -
  @guard#pass160
790 -
    load w64 %379 %287 0;
791 -
    mul w64 %380 2 4;
792 -
    add w64 %381 %379 %380;
793 -
    sload w32 %382 %381 0;
794 -
    br.eq w32 %382 44 @assert.ok159 @assert.fail158;
795 -
  @guard#trap161
796 -
    ebreak;
797 -
    unreachable;
51 +
    store w64 %13 %1 0;
52 +
    store w32 %8 %1 12;
53 +
    jmp @append.store1;
54 +
  @ok10
55 +
    add w64 %28 %4 8;
56 +
    blit %27 %28 16;
57 +
    jmp @merge12;
58 +
  @err11
59 +
    reserve %29 24 8;
60 +
    store w64 %26 %29 0;
61 +
    add w64 %30 %4 8;
62 +
    add w64 %31 %29 8;
63 +
    blit %31 %30 16;
64 +
    blit %0 %29 24;
65 +
    ret %0;
66 +
  @merge12
67 +
    reserve %32 24 8;
68 +
    store w64 0 %32 0;
69 +
    add w64 %33 %32 8;
70 +
    blit %33 %27 16;
71 +
    blit %0 %32 24;
72 +
    ret %0;
798 73
}
test/tests/slice.append.values.rad added +198 -0
1 +
//! returns: 0
2 +
//! Test slice .append() method with inline allocator.
3 +
4 +
/// Allocation failures.
5 +
union AllocError: Copy {
6 +
    /// The allocator cannot provide storage.
7 +
    OutOfMemory,
8 +
}
9 +
10 +
/// Simple bump allocator for testing.
11 +
record Arena {
12 +
    data: *mut [u8],
13 +
    offset: u32,
14 +
}
15 +
16 +
fn newArena(data: *mut [u8]) -> Arena {
17 +
    return Arena { data, offset: 0 };
18 +
}
19 +
20 +
fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque {
21 +
    let aligned = (arena.offset + al - 1) / al * al;
22 +
    let newOffset = aligned + size;
23 +
24 +
    assert newOffset <= arena.data.len as u32;
25 +
26 +
    let base: *mut u8 = &mut arena.data[aligned];
27 +
    set arena.offset = newOffset;
28 +
29 +
    return base as *mut opaque;
30 +
}
31 +
32 +
/// Allocator record matching the compiler's expected layout.
33 +
record Allocator: Copy {
34 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (AllocError),
35 +
    ctx: *unsafe mut opaque,
36 +
}
37 +
38 +
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, al: u32) -> *mut opaque throws (AllocError) {
39 +
    let arena = ctx as *unsafe mut Arena;
40 +
    return arenaAlloc(&mut *arena, size, al);
41 +
}
42 +
43 +
unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator {
44 +
    return Allocator {
45 +
        func: arenaAllocFn,
46 +
        ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque,
47 +
    };
48 +
}
49 +
50 +
unsafe static BUF: [u8; 4096] = undefined;
51 +
52 +
@default unsafe fn main() -> i32 {
53 +
    static arena: Arena = undefined;
54 +
    set arena = newArena(&mut BUF[..]);
55 +
    let a = arenaAllocator(&mut arena);
56 +
57 +
    // Allocate initial capacity of 4.
58 +
    let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32));
59 +
    let mut nums = @sliceOf(ptr as *mut i32, 0, 4);
60 +
61 +
    // Append within capacity.
62 +
    try! nums.append(10, a);
63 +
    try! nums.append(20, a);
64 +
    try! nums.append(30, a);
65 +
66 +
    if nums.len <> 3 {
67 +
        return 1;
68 +
    }
69 +
    if nums.cap <> 4 {
70 +
        return 2;
71 +
    }
72 +
    if nums[0] <> 10 {
73 +
        return 3;
74 +
    }
75 +
    if nums[1] <> 20 {
76 +
        return 4;
77 +
    }
78 +
    if nums[2] <> 30 {
79 +
        return 5;
80 +
    }
81 +
82 +
    // Append to fill capacity.
83 +
    try! nums.append(40, a);
84 +
85 +
    if nums.len <> 4 {
86 +
        return 6;
87 +
    }
88 +
    if nums.cap <> 4 {
89 +
        return 7;
90 +
    }
91 +
92 +
    // Append past capacity -- triggers growth.
93 +
    try! nums.append(50, a);
94 +
95 +
    if nums.len <> 5 {
96 +
        return 8;
97 +
    }
98 +
    // Cap should have grown (cap * 2 | 1 = 4 * 2 | 1 = 9).
99 +
    if nums.cap <> 9 {
100 +
        return 9;
101 +
    }
102 +
    if nums[4] <> 50 {
103 +
        return 10;
104 +
    }
105 +
106 +
    // Verify old elements survived the copy.
107 +
    if nums[0] <> 10 {
108 +
        return 11;
109 +
    }
110 +
    if nums[3] <> 40 {
111 +
        return 12;
112 +
    }
113 +
114 +
    // Append from empty (cap == 0 triggers growth with cap = 1).
115 +
    let mut empty: *mut [i32] = &mut [];
116 +
    try! empty.append(99, a);
117 +
118 +
    if empty.len <> 1 {
119 +
        return 13;
120 +
    }
121 +
    if empty.cap <> 1 {
122 +
        return 14;
123 +
    }
124 +
    if empty[0] <> 99 {
125 +
        return 15;
126 +
    }
127 +
128 +
    // Sum all elements in nums.
129 +
    let mut sum: i32 = 0;
130 +
    for n in nums {
131 +
        set sum += n;
132 +
    }
133 +
    if sum <> 150 {
134 +
        return 16;
135 +
    }
136 +
137 +
    // Test append with u8 elements (stride = 1).
138 +
    let bytePtr = arenaAlloc(&mut arena, 2, 1);
139 +
    let mut bytes = @sliceOf(bytePtr as *mut u8, 0, 2);
140 +
    try! bytes.append(0xAB, a);
141 +
    try! bytes.append(0xCD, a);
142 +
143 +
    if bytes.len <> 2 {
144 +
        return 17;
145 +
    }
146 +
    if bytes[0] <> 0xAB {
147 +
        return 18;
148 +
    }
149 +
    if bytes[1] <> 0xCD {
150 +
        return 19;
151 +
    }
152 +
153 +
    // Trigger growth on u8 slice.
154 +
    try! bytes.append(0xEF, a);
155 +
156 +
    if bytes.len <> 3 {
157 +
        return 20;
158 +
    }
159 +
    if bytes.cap <> 5 {
160 +
        return 21;
161 +
    }
162 +
    if bytes[2] <> 0xEF {
163 +
        return 22;
164 +
    }
165 +
    // Verify old u8 elements survived.
166 +
    if bytes[0] <> 0xAB {
167 +
        return 23;
168 +
    }
169 +
170 +
    // Test that .append() returns the slice, allowing rebinding.
171 +
    let ptr2 = arenaAlloc(&mut arena, @sizeOf(i32) * 2, @alignOf(i32));
172 +
    let mut vals = @sliceOf(ptr2 as *mut i32, 0, 2);
173 +
174 +
    // Append within capacity, rebind via return value.
175 +
    set vals = try! vals.append(42, a);
176 +
    assert vals.len == 1;
177 +
    assert vals[0] == 42;
178 +
179 +
    set vals = try! vals.append(43, a);
180 +
    assert vals.len == 2;
181 +
182 +
    // Append past capacity -- triggers growth and returns updated slice.
183 +
    let oldPtr = &vals[0] as *opaque;
184 +
    set vals = try! vals.append(44, a);
185 +
    assert vals.len == 3;
186 +
    assert vals.cap == 5;
187 +
188 +
    // After growth, the data pointer should have changed.
189 +
    let newPtr = &vals[0] as *opaque;
190 +
    assert oldPtr <> newPtr;
191 +
192 +
    // Verify all elements survived.
193 +
    assert vals[0] == 42;
194 +
    assert vals[1] == 43;
195 +
    assert vals[2] == 44;
196 +
197 +
    return 0;
198 +
}
test/tests/slice.range.capacity.rad +11 -5
1 1
//! returns: 0
2 2
//! Slice ranges retain the backing capacity after their start offset.
3 3
4 +
/// Allocation failures.
5 +
union AllocError: Copy {
6 +
    /// The allocator cannot provide storage.
7 +
    OutOfMemory,
8 +
}
9 +
4 10
/// Allocator interface used by append.
5 11
record Allocator: Copy {
6 12
    /// Allocation callback.
7 -
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
13 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque throws (AllocError),
8 14
    /// Callback context.
9 15
    ctx: *unsafe mut opaque,
10 16
}
11 17
12 18
/// Reject allocation when append must use existing storage.
13 -
unsafe fn rejectAlloc(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
19 +
unsafe fn rejectAlloc(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
14 20
    panic "rejectAlloc: unexpected allocation";
15 21
}
16 22
17 23
/// Check dynamic range bounds and nested slice capacity.
18 24
fn checkRange(storage: *mut [i32], start: u32, end: u32) {
45 51
    assert end.cap == 0;
46 52
    checkRange((&mut storage[..2]) as *mut [i32], 1, 2);
47 53
    checkRange((&mut storage[..]) as *mut [i32], 4, 4);
48 54
    let mut items: *mut [i32] = &mut storage[1..1];
49 55
    let allocator = Allocator { func: rejectAlloc, ctx: &mut storage as *unsafe mut opaque };
50 -
    items.append(50, allocator);
51 -
    items.append(60, allocator);
52 -
    items.append(70, allocator);
56 +
    try! items.append(50, allocator);
57 +
    try! items.append(60, allocator);
58 +
    try! items.append(70, allocator);
53 59
    assert items.len == 3;
54 60
    assert items.cap == 3;
55 61
    assert items[0] == 50;
56 62
    assert items[2] == 70;
57 63
    return 0;