Make slice append report allocation failures
a8669da64479a5fd13b47a125dd331f281b8de0597fafc9c43718f08e1bfcee0
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 = ¶mTypes[..]; |
|
| 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: ¶mTypes[..], |
|
| 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: ¶mTypes[..], |
|
| 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; |