Use growable slices throughout the compiler
c4b1fccc71edd1efaf5842fe51a2f83d34229a6401b1cbe83d1abf3e396f2291
This allows for massive simplifications where we have dynamic lists.
1 parent
66129411
compiler/radiance.rad
+19 -19
| 514 | 514 | let case ast::NodeValue::Block(block) = modAst.value else { |
|
| 515 | 515 | return; |
|
| 516 | 516 | }; |
|
| 517 | 517 | let modPath = module::moduleQualifiedPath(entry); |
|
| 518 | 518 | ||
| 519 | - | for i in 0..block.statements.len { |
|
| 520 | - | let stmt = block.statements.list[i]; |
|
| 519 | + | for stmt in block.statements { |
|
| 521 | 520 | if let case ast::NodeValue::FnDecl(decl) = stmt.value { |
|
| 522 | 521 | if let fnName = getTestFnName(&decl) { |
|
| 523 | 522 | if *testCount < tests.len { |
|
| 524 | 523 | tests[*testCount] = TestDesc { modPath, fnName }; |
|
| 525 | 524 | *testCount += 1; |
| 548 | 547 | funcPath[j - 1] = desc.modPath[j]; |
|
| 549 | 548 | } |
|
| 550 | 549 | funcPath[desc.modPath.len - 1] = desc.fnName; |
|
| 551 | 550 | let funcArg = synthScopeAccess(arena, &funcPath[..desc.modPath.len]); |
|
| 552 | 551 | ||
| 553 | - | let mut args = ast::nodeList(arena, 3); |
|
| 554 | - | ast::nodeListPush(&mut args, modArg); |
|
| 555 | - | ast::nodeListPush(&mut args, nameArg); |
|
| 556 | - | ast::nodeListPush(&mut args, funcArg); |
|
| 552 | + | let a = ast::nodeAllocator(arena); |
|
| 553 | + | let mut args = ast::nodeSlice(arena, 3); |
|
| 554 | + | args.append(modArg, a); |
|
| 555 | + | args.append(nameArg, a); |
|
| 556 | + | args.append(funcArg, a); |
|
| 557 | 557 | ||
| 558 | 558 | return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args })); |
|
| 559 | 559 | } |
|
| 560 | 560 | ||
| 561 | 561 | /// Inject a test runner into the entry package's root module. |
| 604 | 604 | } |
|
| 605 | 605 | ||
| 606 | 606 | /// Synthesize the test entry point. |
|
| 607 | 607 | fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node { |
|
| 608 | 608 | // Build array literal: `[testing::test(...), ...]`. |
|
| 609 | - | let mut elements = ast::nodeList(arena, tests.len); |
|
| 609 | + | let a = ast::nodeAllocator(arena); |
|
| 610 | + | let mut elements = ast::nodeSlice(arena, tests.len as u32); |
|
| 610 | 611 | for i in 0..tests.len { |
|
| 611 | - | ast::nodeListPush(&mut elements, synthTestCall(arena, &tests[i])); |
|
| 612 | + | elements.append(synthTestCall(arena, &tests[i]), a); |
|
| 612 | 613 | } |
|
| 613 | 614 | let arrayLit = ast::synthNode(arena, ast::NodeValue::ArrayLit(elements)); |
|
| 614 | 615 | ||
| 615 | 616 | // Build: `&[...]`. |
|
| 616 | 617 | let testsRef = ast::synthNode(arena, ast::NodeValue::AddressOf(ast::AddressOf { |
|
| 617 | 618 | target: arrayLit, mutable: false, |
|
| 618 | 619 | })); |
|
| 619 | 620 | ||
| 620 | 621 | // Build: `testing::runAllTests(&[...])`. |
|
| 621 | 622 | let runFn = synthScopeAccess(arena, &["testing", "runAllTests"]); |
|
| 622 | - | let mut callArgs = ast::nodeList(arena, 1); |
|
| 623 | - | ast::nodeListPush(&mut callArgs, testsRef); |
|
| 623 | + | let mut callArgs = ast::nodeSlice(arena, 1); |
|
| 624 | + | callArgs.append(testsRef, a); |
|
| 624 | 625 | let callExpr = ast::synthNode(arena, ast::NodeValue::Call(ast::Call { |
|
| 625 | 626 | callee: runFn, args: callArgs, |
|
| 626 | 627 | })); |
|
| 627 | 628 | ||
| 628 | 629 | // Build: `return testing::runAllTests(&[...]);` |
|
| 629 | 630 | let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr }); |
|
| 630 | - | let mut bodyStmts = ast::nodeList(arena, 1); |
|
| 631 | - | ast::nodeListPush(&mut bodyStmts, retStmt); |
|
| 631 | + | let mut bodyStmts = ast::nodeSlice(arena, 1); |
|
| 632 | + | bodyStmts.append(retStmt, a); |
|
| 632 | 633 | let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts })); |
|
| 633 | 634 | ||
| 634 | 635 | // Build: `fn #testMain() -> i32` |
|
| 635 | 636 | let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain"))); |
|
| 636 | 637 | let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer { |
|
| 637 | 638 | width: 4, sign: ast::Signedness::Signed, |
|
| 638 | 639 | })); |
|
| 639 | 640 | let fnSig = ast::FnSig { |
|
| 640 | - | params: ast::nodeList(arena, 0), |
|
| 641 | + | params: ast::nodeSlice(arena, 0), |
|
| 641 | 642 | returnType, |
|
| 642 | - | throwList: ast::nodeList(arena, 0), |
|
| 643 | + | throwList: ast::nodeSlice(arena, 0), |
|
| 643 | 644 | }; |
|
| 644 | 645 | ||
| 645 | 646 | // `@default` attribute. |
|
| 646 | 647 | let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default)); |
|
| 647 | - | let mut attrList = ast::nodeList(arena, 1); |
|
| 648 | - | ast::nodeListPush(&mut attrList, attrNode); |
|
| 648 | + | let mut attrList = ast::nodeSlice(arena, 1); |
|
| 649 | + | attrList.append(attrNode, a); |
|
| 649 | 650 | let fnAttrs = ast::Attributes { list: attrList }; |
|
| 650 | 651 | ||
| 651 | 652 | return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl { |
|
| 652 | 653 | name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 653 | 654 | })); |
| 661 | 662 | ) { |
|
| 662 | 663 | let case ast::NodeValue::Block(block) = blockNode.value else { |
|
| 663 | 664 | panic "injectIntoBlock: expected Block node"; |
|
| 664 | 665 | }; |
|
| 665 | 666 | let mut stmts = block.statements; |
|
| 666 | - | ast::nodeListGrow(&mut stmts, arena, 1); |
|
| 667 | - | ast::nodeListPush(&mut stmts, decl); |
|
| 667 | + | stmts.append(decl, ast::nodeAllocator(arena)); |
|
| 668 | 668 | ||
| 669 | 669 | blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts }); |
|
| 670 | 670 | } |
|
| 671 | 671 | ||
| 672 | 672 | /// Write code buffer to file as raw bytes. |
| 771 | 771 | throw Error::Other; |
|
| 772 | 772 | }; |
|
| 773 | 773 | if not resolver::success(&diags) { |
|
| 774 | 774 | resolver::printer::printDiagnostics(&diags, &res); |
|
| 775 | 775 | io::print("radiance: failed: "); |
|
| 776 | - | io::printU32(diags.errors.listLen); |
|
| 776 | + | io::printU32(diags.errors.len); |
|
| 777 | 777 | io::printLn(" errors"); |
|
| 778 | 778 | throw Error::Other; |
|
| 779 | 779 | } |
|
| 780 | 780 | return res; |
|
| 781 | 781 | } |
lib/std/arch/rv64/isel.rad
+4 -6
| 135 | 135 | return getReg(s, ssa); |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | 138 | /// Look up symbol address in data map. |
|
| 139 | 139 | fn lookupDataSym(s: *Selector, name: *[u8]) -> u32 throws (SelectorError) { |
|
| 140 | - | for i in 0..s.dataSyms.len { |
|
| 141 | - | let sym = s.dataSyms[i]; |
|
| 142 | - | ||
| 140 | + | for sym in s.dataSyms { |
|
| 143 | 141 | if mem::eq(sym.name, name) { |
|
| 144 | 142 | return sym.addr; |
|
| 145 | 143 | } |
|
| 146 | 144 | } |
|
| 147 | 145 | throw SelectorError::Internal; |
| 233 | 231 | /// Returns the total size needed for all static reserves, respecting alignment. |
|
| 234 | 232 | fn computeReserveSize(func: *il::Fn) -> i32 { |
|
| 235 | 233 | let mut offset: i32 = 0; |
|
| 236 | 234 | for b in 0..func.blocks.len { |
|
| 237 | 235 | let block = &func.blocks[b]; |
|
| 238 | - | for i in 0..block.instrs.len { |
|
| 239 | - | match block.instrs.list[i] { |
|
| 236 | + | for instr in block.instrs { |
|
| 237 | + | match instr { |
|
| 240 | 238 | case il::Instr::Reserve { size, alignment, .. } => { |
|
| 241 | 239 | if let case il::Val::Imm(sz) = size { |
|
| 242 | 240 | offset = mem::alignUpI32(offset, alignment as i32); |
|
| 243 | 241 | offset += (sz as i32); |
|
| 244 | 242 | } |
| 323 | 321 | // Record debug location before emitting machine instructions. |
|
| 324 | 322 | if hasLocs { |
|
| 325 | 323 | emit::recordSrcLoc(s.e, block.locs[i]); |
|
| 326 | 324 | } |
|
| 327 | 325 | s.pendingSpill = nil; |
|
| 328 | - | selectInstr(s, blockIdx, block.instrs.list[i], frame, func); |
|
| 326 | + | selectInstr(s, blockIdx, block.instrs[i], frame, func); |
|
| 329 | 327 | ||
| 330 | 328 | // Flush the pending spill store, if any. |
|
| 331 | 329 | if let p = s.pendingSpill { |
|
| 332 | 330 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) { |
|
| 333 | 331 | emit::emitSd(s.e, p.rd, super::FP, spillOffset(s, slot)); |
lib/std/lang/alloc.rad
+0 -20
| 102 | 102 | let ptr = try alloc(arena, size * count, alignment); |
|
| 103 | 103 | ||
| 104 | 104 | return @sliceOf(ptr, count); |
|
| 105 | 105 | } |
|
| 106 | 106 | ||
| 107 | - | /// Grow a slice to accommodate more elements. |
|
| 108 | - | /// |
|
| 109 | - | /// Allocates new storage with double the current capacity, copies existing |
|
| 110 | - | /// elements, and returns the new slice. Old storage remains allocated. |
|
| 111 | - | pub fn growSlice(arena: *mut Arena, old: *mut [opaque], len: u32, size: u32, alignment: u32) -> *mut [opaque] throws (AllocError) { |
|
| 112 | - | let mut newCap = old.len * 2; |
|
| 113 | - | if newCap == 0 { |
|
| 114 | - | newCap = 1; |
|
| 115 | - | } |
|
| 116 | - | let new = try allocSlice(arena, size, alignment, newCap); |
|
| 117 | - | ||
| 118 | - | // Copy existing elements (byte-wise). |
|
| 119 | - | let oldBytes = @sliceOf(old.ptr as *u8, len * size); |
|
| 120 | - | let newBytes = @sliceOf(new.ptr as *mut u8, len * size); |
|
| 121 | - | for i in 0..len * size { |
|
| 122 | - | newBytes[i] = oldBytes[i]; |
|
| 123 | - | } |
|
| 124 | - | return new; |
|
| 125 | - | } |
|
| 126 | - | ||
| 127 | 107 | /// Generic allocator interface. |
|
| 128 | 108 | /// |
|
| 129 | 109 | /// Bundles an allocation function with an opaque context pointer so that |
|
| 130 | 110 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
|
| 131 | 111 | /// through a uniform interface. The `func` field is called with the context |
lib/std/lang/ast.rad
+34 -83
| 18 | 18 | arena: alloc::Arena, |
|
| 19 | 19 | /// Next node ID to assign. Incremented on each node allocation. |
|
| 20 | 20 | nextId: u32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | /// List of node pointers that tracks its length. |
|
| 24 | - | pub record NodeList { |
|
| 25 | - | /// Slice of node pointers. |
|
| 26 | - | list: *mut [*Node], |
|
| 27 | - | /// Number of elements currently in the list. |
|
| 28 | - | len: u32, |
|
| 29 | - | } |
|
| 30 | - | ||
| 31 | 23 | /// Initialize a node arena backed by the given byte slice. |
|
| 32 | 24 | pub fn nodeArena(data: *mut [u8]) -> NodeArena { |
|
| 33 | 25 | return NodeArena { |
|
| 34 | 26 | arena: alloc::new(data), |
|
| 35 | 27 | nextId: 0, |
|
| 36 | 28 | }; |
|
| 37 | 29 | } |
|
| 38 | 30 | ||
| 39 | - | /// Create a new node list in the arena with the specified capacity. |
|
| 40 | - | pub fn nodeList(arena: *mut NodeArena, capacity: u32) -> NodeList { |
|
| 31 | + | /// Create an empty `*mut [*Node]` slice with the given capacity. |
|
| 32 | + | pub fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] { |
|
| 41 | 33 | if capacity == 0 { |
|
| 42 | - | // Return an empty list without allocating. |
|
| 43 | - | return NodeList { list: undefined, len: 0 }; |
|
| 34 | + | return &mut []; |
|
| 44 | 35 | } |
|
| 45 | 36 | let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity); |
|
| 46 | - | let list = ptr as *mut [*Node]; |
|
| 47 | 37 | ||
| 48 | - | return NodeList { list, len: 0 }; |
|
| 38 | + | return @sliceOf(ptr.ptr as *mut *Node, 0, capacity); |
|
| 49 | 39 | } |
|
| 50 | 40 | ||
| 51 | - | /// Push a node to the end of the list. |
|
| 52 | - | pub fn nodeListPush(self: *mut NodeList, node: *Node) -> u32 { |
|
| 53 | - | if self.len >= self.list.len { |
|
| 54 | - | panic "nodeListPush: out of space"; |
|
| 55 | - | } |
|
| 56 | - | self.list[self.len] = node; |
|
| 57 | - | self.len += 1; |
|
| 58 | - | ||
| 59 | - | return self.len; |
|
| 60 | - | } |
|
| 61 | - | ||
| 62 | - | /// Grow a node list's backing storage by `extra` slots. |
|
| 63 | - | /// |
|
| 64 | - | /// Allocates a new backing array of size `self.len + extra`, copies existing |
|
| 65 | - | /// node pointers, and updates `self.list`. Existing node data is not copied. |
|
| 66 | - | pub fn nodeListGrow(self: *mut NodeList, arena: *mut NodeArena, extra: u32) { |
|
| 67 | - | if self.list.len - self.len >= extra { |
|
| 68 | - | return; |
|
| 69 | - | } |
|
| 70 | - | let newCap = self.len + extra; |
|
| 71 | - | let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), newCap); |
|
| 72 | - | let newList = ptr as *mut [*Node]; |
|
| 73 | - | ||
| 74 | - | for i in 0..self.len { |
|
| 75 | - | newList[i] = self.list[i]; |
|
| 76 | - | } |
|
| 77 | - | self.list = newList; |
|
| 41 | + | /// Return an allocator backed by the node arena. |
|
| 42 | + | // TODO: Why do we need this? |
|
| 43 | + | pub fn nodeAllocator(arena: *mut NodeArena) -> alloc::Allocator { |
|
| 44 | + | return alloc::arenaAllocator(&mut arena.arena); |
|
| 78 | 45 | } |
|
| 79 | 46 | ||
| 80 | 47 | /// Attribute bit set applied to declarations or fields. |
|
| 81 | 48 | pub union Attribute { |
|
| 82 | 49 | /// Public visibility attribute. |
| 91 | 58 | Intrinsic = 0b10000, |
|
| 92 | 59 | } |
|
| 93 | 60 | ||
| 94 | 61 | /// Ordered collection of attribute nodes applied to a declaration. |
|
| 95 | 62 | pub record Attributes { |
|
| 96 | - | list: NodeList, |
|
| 97 | - | } |
|
| 98 | - | ||
| 99 | - | /// Append an attribute node to the list. |
|
| 100 | - | pub fn attributesPush(self: *mut Attributes, attr: *Node) -> u32 { |
|
| 101 | - | return nodeListPush(&mut self.list, attr); |
|
| 102 | - | } |
|
| 103 | - | ||
| 104 | - | /// Return the number of attributes stored in the list. |
|
| 105 | - | pub fn attributesLen(self: *Attributes) -> u32 { |
|
| 106 | - | return self.list.len; |
|
| 107 | - | } |
|
| 108 | - | ||
| 109 | - | /// Fetch the attribute node at the specified index. |
|
| 110 | - | pub fn attributesGet(self: *Attributes, index: u32) -> *Node { |
|
| 111 | - | assert index < self.list.len, "attributesGet: invalid index"; |
|
| 112 | - | return self.list.list[index]; |
|
| 63 | + | list: *mut [*Node], |
|
| 113 | 64 | } |
|
| 114 | 65 | ||
| 115 | 66 | /// Check if an attributes list contains an attribute. |
|
| 116 | 67 | pub fn attributesContains(self: *Attributes, attr: Attribute) -> bool { |
|
| 117 | - | for i in 0..self.list.len { |
|
| 118 | - | let node = attributesGet(self, i); |
|
| 68 | + | for node in self.list { |
|
| 119 | 69 | if let case NodeValue::Attribute(a) = node.value; a == attr { |
|
| 120 | 70 | return true; |
|
| 121 | 71 | } |
|
| 122 | 72 | } |
|
| 123 | 73 | return false; |
| 276 | 226 | /// Nominal type, points to identifier node. |
|
| 277 | 227 | Nominal(*Node), |
|
| 278 | 228 | /// Inline record type for union variant payloads. |
|
| 279 | 229 | Record { |
|
| 280 | 230 | /// Field declaration nodes. |
|
| 281 | - | fields: NodeList, |
|
| 231 | + | fields: *mut [*Node], |
|
| 282 | 232 | /// Whether this record has labeled fields. |
|
| 283 | 233 | labeled: bool, |
|
| 284 | 234 | }, |
|
| 285 | 235 | /// Anonymous function type. |
|
| 286 | 236 | Fn(FnSig), |
| 294 | 244 | } |
|
| 295 | 245 | ||
| 296 | 246 | /// Function signature. |
|
| 297 | 247 | pub record FnSig { |
|
| 298 | 248 | /// Parameter type nodes in declaration order. |
|
| 299 | - | params: NodeList, |
|
| 249 | + | params: *mut [*Node], |
|
| 300 | 250 | /// Optional return type node. |
|
| 301 | 251 | returnType: ?*Node, |
|
| 302 | 252 | /// Throwable type nodes declared in the signature. |
|
| 303 | - | throwList: NodeList, |
|
| 253 | + | throwList: *mut [*Node], |
|
| 304 | 254 | } |
|
| 305 | 255 | ||
| 306 | 256 | /// Address-of expression metadata. |
|
| 307 | 257 | pub record AddressOf { |
|
| 308 | 258 | /// Target expression being referenced. |
| 312 | 262 | } |
|
| 313 | 263 | ||
| 314 | 264 | /// Compound statement block with optional dedicated scope. |
|
| 315 | 265 | pub record Block { |
|
| 316 | 266 | /// Statements that belong to this block. |
|
| 317 | - | statements: NodeList, |
|
| 267 | + | statements: *mut [*Node], |
|
| 318 | 268 | } |
|
| 319 | 269 | ||
| 320 | 270 | /// Function call expression. |
|
| 321 | 271 | pub record Call { |
|
| 322 | 272 | /// Callee expression. |
|
| 323 | 273 | callee: *Node, |
|
| 324 | 274 | /// Argument expressions in source order. |
|
| 325 | - | args: NodeList, |
|
| 275 | + | args: *mut [*Node], |
|
| 326 | 276 | } |
|
| 327 | 277 | ||
| 328 | 278 | /// Single argument to a function or record literal, optionally labeled. |
|
| 329 | 279 | pub record Arg { |
|
| 330 | 280 | /// Optional label applied to the argument. |
| 364 | 314 | /// Try expression metadata. |
|
| 365 | 315 | pub record Try { |
|
| 366 | 316 | /// Expression evaluated with implicit error propagation. |
|
| 367 | 317 | expr: *Node, |
|
| 368 | 318 | /// Catch clauses. Empty for propagation (`try`), `try!`, or `try?`. |
|
| 369 | - | catches: NodeList, |
|
| 319 | + | catches: *mut [*Node], |
|
| 370 | 320 | /// Whether the try should panic instead of returning an error. |
|
| 371 | 321 | shouldPanic: bool, |
|
| 372 | 322 | /// Whether the try should return an optional instead of propagating error. |
|
| 373 | 323 | returnsOptional: bool, |
|
| 374 | 324 | } |
| 426 | 376 | } |
|
| 427 | 377 | ||
| 428 | 378 | /// Prong arm. |
|
| 429 | 379 | pub union ProngArm { |
|
| 430 | 380 | /// Case arm with pattern list. |
|
| 431 | - | Case(NodeList), |
|
| 381 | + | Case(*mut [*Node]), |
|
| 432 | 382 | /// Binding arm with single identifier or placeholder. |
|
| 433 | 383 | Binding(*Node), |
|
| 434 | 384 | /// Else arm. |
|
| 435 | 385 | Else, |
|
| 436 | 386 | } |
| 468 | 418 | /// `match` statement metadata. |
|
| 469 | 419 | pub record Match { |
|
| 470 | 420 | /// Expression whose value controls the match. |
|
| 471 | 421 | subject: *Node, |
|
| 472 | 422 | /// Prong nodes evaluated in order. |
|
| 473 | - | prongs: NodeList, |
|
| 423 | + | prongs: *mut [*Node], |
|
| 474 | 424 | } |
|
| 475 | 425 | ||
| 476 | 426 | /// `match` prong metadata. |
|
| 477 | 427 | pub record MatchProng { |
|
| 478 | 428 | /// Prong arm. |
| 533 | 483 | pub record RecordLit { |
|
| 534 | 484 | /// Type name associated with the literal. |
|
| 535 | 485 | /// If `nil`, it's an anonymous record literal. |
|
| 536 | 486 | typeName: ?*Node, |
|
| 537 | 487 | /// Field initializer nodes. |
|
| 538 | - | fields: NodeList, |
|
| 488 | + | fields: *mut [*Node], |
|
| 539 | 489 | /// When true, remaining fields are discarded (`{ x, .. }`). |
|
| 540 | 490 | ignoreRest: bool, |
|
| 541 | 491 | } |
|
| 542 | 492 | ||
| 543 | 493 | /// Record declaration. |
|
| 544 | 494 | pub record RecordDecl { |
|
| 545 | 495 | /// Identifier naming the record. |
|
| 546 | 496 | name: *Node, |
|
| 547 | 497 | /// Field declaration nodes. |
|
| 548 | - | fields: NodeList, |
|
| 498 | + | fields: *mut [*Node], |
|
| 549 | 499 | /// Optional attribute list applied to the record. |
|
| 550 | 500 | attrs: ?Attributes, |
|
| 551 | 501 | /// Trait derivations attached to the record. |
|
| 552 | - | derives: NodeList, |
|
| 502 | + | derives: *mut [*Node], |
|
| 553 | 503 | /// Whether this record has labeled fields. |
|
| 554 | 504 | labeled: bool, |
|
| 555 | 505 | } |
|
| 556 | 506 | ||
| 557 | 507 | /// Union declarations. |
|
| 558 | 508 | pub record UnionDecl { |
|
| 559 | 509 | /// Identifier naming the union. |
|
| 560 | 510 | name: *Node, |
|
| 561 | 511 | /// Variant nodes making up the union. |
|
| 562 | - | variants: NodeList, |
|
| 512 | + | variants: *mut [*Node], |
|
| 563 | 513 | /// Optional attribute list applied to the union. |
|
| 564 | 514 | attrs: ?Attributes, |
|
| 565 | 515 | /// Trait derivations attached to the union. |
|
| 566 | - | derives: NodeList, |
|
| 516 | + | derives: *mut [*Node], |
|
| 567 | 517 | } |
|
| 568 | 518 | ||
| 569 | 519 | /// Union variant declaration. |
|
| 570 | 520 | pub record UnionDeclVariant { |
|
| 571 | 521 | /// Identifier naming the variant. |
| 677 | 627 | /// Numeric literal such as `42` or `0xFF`. |
|
| 678 | 628 | Number(IntLiteral), |
|
| 679 | 629 | /// Range expression such as `0..10` or `..`. |
|
| 680 | 630 | Range(Range), |
|
| 681 | 631 | /// Array literal expression. |
|
| 682 | - | ArrayLit(NodeList), |
|
| 632 | + | ArrayLit(*mut [*Node]), |
|
| 683 | 633 | /// Array repeat literal expression. |
|
| 684 | 634 | ArrayRepeatLit(ArrayRepeatLit), |
|
| 685 | 635 | /// Array subscript expression. |
|
| 686 | 636 | Subscript { |
|
| 687 | 637 | /// Array or slice. |
| 696 | 646 | /// Builtin function call (e.g. `@sizeOf(T)`). |
|
| 697 | 647 | BuiltinCall { |
|
| 698 | 648 | /// Builtin function kind. |
|
| 699 | 649 | kind: Builtin, |
|
| 700 | 650 | /// Argument list. |
|
| 701 | - | args: NodeList, |
|
| 651 | + | args: *mut [*Node], |
|
| 702 | 652 | }, |
|
| 703 | 653 | /// Block expression or statement body. |
|
| 704 | 654 | Block(Block), |
|
| 705 | 655 | /// Call expression, eg. `f(x)`. |
|
| 706 | 656 | Call(Call), |
| 818 | 768 | /// Trait declaration. |
|
| 819 | 769 | TraitDecl { |
|
| 820 | 770 | /// Trait name identifier. |
|
| 821 | 771 | name: *Node, |
|
| 822 | 772 | /// Supertrait name nodes. |
|
| 823 | - | supertraits: NodeList, |
|
| 773 | + | supertraits: *mut [*Node], |
|
| 824 | 774 | /// Method signature nodes ([`TraitMethodSig`]). |
|
| 825 | - | methods: NodeList, |
|
| 775 | + | methods: *mut [*Node], |
|
| 826 | 776 | /// Optional attributes. |
|
| 827 | 777 | attrs: ?Attributes, |
|
| 828 | 778 | }, |
|
| 829 | 779 | /// Method signature inside a trait declaration. |
|
| 830 | 780 | TraitMethodSig { |
| 840 | 790 | /// Trait name identifier. |
|
| 841 | 791 | traitName: *Node, |
|
| 842 | 792 | /// Target type identifier. |
|
| 843 | 793 | targetType: *Node, |
|
| 844 | 794 | /// Method definition nodes ([`InstanceMethodDecl`]). |
|
| 845 | - | methods: NodeList, |
|
| 795 | + | methods: *mut [*Node], |
|
| 846 | 796 | }, |
|
| 847 | 797 | /// Method definition inside an instance block. |
|
| 848 | 798 | InstanceMethodDecl { |
|
| 849 | 799 | /// Method name identifier. |
|
| 850 | 800 | name: *Node, |
| 894 | 844 | fnBody: *Node |
|
| 895 | 845 | } |
|
| 896 | 846 | ||
| 897 | 847 | /// Synthesize a module with a function in it with the given name and statements. |
|
| 898 | 848 | pub fn synthFnModule( |
|
| 899 | - | arena: *mut NodeArena, name: *[u8], bodyStmts: NodeList |
|
| 849 | + | arena: *mut NodeArena, name: *[u8], bodyStmts: *mut [*Node] |
|
| 900 | 850 | ) -> SynthFnMod { |
|
| 851 | + | let a = nodeAllocator(arena); |
|
| 901 | 852 | let fnName = synthNode(arena, NodeValue::Ident(name)); |
|
| 902 | - | let params = nodeList(arena, 0); |
|
| 903 | - | let throwList = nodeList(arena, 0); |
|
| 853 | + | let params: *mut [*Node] = &mut []; |
|
| 854 | + | let throwList: *mut [*Node] = &mut []; |
|
| 904 | 855 | let fnSig = FnSig { params, returnType: nil, throwList }; |
|
| 905 | 856 | let fnBody = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts })); |
|
| 906 | 857 | let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl { |
|
| 907 | 858 | name: fnName, sig: fnSig, body: fnBody, attrs: nil, |
|
| 908 | 859 | })); |
|
| 909 | - | let mut rootStmts = nodeList(arena, 1); |
|
| 910 | - | nodeListPush(&mut rootStmts, fnDecl); |
|
| 860 | + | let mut rootStmts: *mut [*Node] = &mut []; |
|
| 861 | + | rootStmts.append(fnDecl, a); |
|
| 911 | 862 | let modBody = synthNode(arena, NodeValue::Block(Block { statements: rootStmts })); |
|
| 912 | 863 | ||
| 913 | 864 | return SynthFnMod { modBody, fnBody }; |
|
| 914 | 865 | } |
lib/std/lang/ast/printer.rad
+34 -34
| 85 | 85 | else sexpr::list(a, "ptr", &[toExpr(a, valueType)]), |
|
| 86 | 86 | case super::TypeSig::Optional { valueType } => |
|
| 87 | 87 | return sexpr::list(a, "?", &[toExpr(a, valueType)]), |
|
| 88 | 88 | case super::TypeSig::Nominal(name) => return toExpr(a, name), |
|
| 89 | 89 | case super::TypeSig::Record { fields, .. } => |
|
| 90 | - | return sexpr::list(a, "record", nodeListToExprs(a, &fields)), |
|
| 90 | + | return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])), |
|
| 91 | 91 | case super::TypeSig::Fn(sig) => { |
|
| 92 | 92 | let mut ret = sexpr::sym("void"); |
|
| 93 | 93 | if let rt = sig.returnType { |
|
| 94 | 94 | ret = toExpr(a, rt); |
|
| 95 | 95 | } |
|
| 96 | - | return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params)), ret]); |
|
| 96 | + | return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]); |
|
| 97 | 97 | } |
|
| 98 | 98 | case super::TypeSig::TraitObject { traitName, mutable } => |
|
| 99 | 99 | return sexpr::list(a, "obj", &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable |
|
| 100 | 100 | else sexpr::list(a, "obj", &[toExpr(a, traitName)]), |
|
| 101 | 101 | } |
|
| 102 | 102 | } |
|
| 103 | 103 | ||
| 104 | - | /// Convert a node list to a slice of expressions. |
|
| 105 | - | fn nodeListToExprs(a: *mut alloc::Arena, nodes: *super::NodeList) -> *[sexpr::Expr] { |
|
| 104 | + | /// Convert a node slice to a slice of expressions. |
|
| 105 | + | fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 106 | 106 | if nodes.len == 0 { |
|
| 107 | 107 | return &[]; |
|
| 108 | 108 | } |
|
| 109 | - | let buf = try! sexpr::allocExprs(a, nodes.len); |
|
| 109 | + | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 110 | 110 | for i in 0..nodes.len { |
|
| 111 | - | buf[i] = toExpr(a, nodes.list[i]); |
|
| 111 | + | buf[i] = toExpr(a, nodes[i]); |
|
| 112 | 112 | } |
|
| 113 | 113 | return buf; |
|
| 114 | 114 | } |
|
| 115 | 115 | ||
| 116 | 116 | /// Convert an optional node to an expression, or return placeholder. |
| 136 | 136 | } |
|
| 137 | 137 | return sexpr::Expr::Null; |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Convert a list of match prongs to expressions. |
|
| 141 | - | fn prongListToExprs(a: *mut alloc::Arena, nodes: *super::NodeList) -> *[sexpr::Expr] { |
|
| 141 | + | fn prongListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 142 | 142 | if nodes.len == 0 { |
|
| 143 | 143 | return &[]; |
|
| 144 | 144 | } |
|
| 145 | - | let buf = try! sexpr::allocExprs(a, nodes.len); |
|
| 145 | + | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 146 | 146 | for i in 0..nodes.len { |
|
| 147 | - | let prong = nodes.list[i]; |
|
| 147 | + | let prong = nodes[i]; |
|
| 148 | 148 | match prong.value { |
|
| 149 | 149 | case super::NodeValue::MatchProng(p) => { |
|
| 150 | 150 | buf[i] = prongToExpr(a, p); |
|
| 151 | 151 | } |
|
| 152 | 152 | else => { |
| 160 | 160 | /// Convert a match prong to an S-expression. |
|
| 161 | 161 | fn prongToExpr(a: *mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr { |
|
| 162 | 162 | match p.arm { |
|
| 163 | 163 | case super::ProngArm::Case(patterns) => { |
|
| 164 | 164 | return sexpr::block(a, "case", &[ |
|
| 165 | - | sexpr::list(a, "patterns", nodeListToExprs(a, &patterns)), |
|
| 165 | + | sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])), |
|
| 166 | 166 | guardExpr(a, p.guard) |
|
| 167 | 167 | ], &[toExpr(a, p.body)]); |
|
| 168 | 168 | } |
|
| 169 | 169 | case super::ProngArm::Else => { |
|
| 170 | 170 | return sexpr::block(a, "else", &[guardExpr(a, p.guard)], &[toExpr(a, p.body)]); |
| 187 | 187 | ) -> sexpr::Expr { |
|
| 188 | 188 | return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]); |
|
| 189 | 189 | } |
|
| 190 | 190 | ||
| 191 | 191 | /// Convert a list of record fields to expressions. |
|
| 192 | - | fn fieldListToExprs(a: *mut alloc::Arena, nodes: *super::NodeList) -> *[sexpr::Expr] { |
|
| 192 | + | fn fieldListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 193 | 193 | if nodes.len == 0 { |
|
| 194 | 194 | return &[]; |
|
| 195 | 195 | } |
|
| 196 | - | let buf = try! sexpr::allocExprs(a, nodes.len); |
|
| 196 | + | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 197 | 197 | for i in 0..nodes.len { |
|
| 198 | - | let node = nodes.list[i]; |
|
| 198 | + | let node = nodes[i]; |
|
| 199 | 199 | match node.value { |
|
| 200 | 200 | case super::NodeValue::RecordField { field, type, value } => { |
|
| 201 | 201 | buf[i] = fieldToExpr(a, field, type, value); |
|
| 202 | 202 | } |
|
| 203 | 203 | else => { |
| 212 | 212 | fn variantToExpr(a: *mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr { |
|
| 213 | 213 | return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]); |
|
| 214 | 214 | } |
|
| 215 | 215 | ||
| 216 | 216 | /// Convert a list of union variants to expressions. |
|
| 217 | - | fn variantListToExprs(a: *mut alloc::Arena, nodes: *super::NodeList) -> *[sexpr::Expr] { |
|
| 217 | + | fn variantListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 218 | 218 | if nodes.len == 0 { |
|
| 219 | 219 | return &[]; |
|
| 220 | 220 | } |
|
| 221 | - | let buf = try! sexpr::allocExprs(a, nodes.len); |
|
| 221 | + | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 222 | 222 | for i in 0..nodes.len { |
|
| 223 | - | let node = nodes.list[i]; |
|
| 223 | + | let node = nodes[i]; |
|
| 224 | 224 | match node.value { |
|
| 225 | 225 | case super::NodeValue::UnionDeclVariant(v) => { |
|
| 226 | 226 | buf[i] = variantToExpr(a, v.name, v.type); |
|
| 227 | 227 | } |
|
| 228 | 228 | else => { |
| 257 | 257 | case super::NodeValue::BinOp(b) => |
|
| 258 | 258 | return sexpr::list(a, binOpName(b.op), &[toExpr(a, b.left), toExpr(a, b.right)]), |
|
| 259 | 259 | case super::NodeValue::UnOp(u) => |
|
| 260 | 260 | return sexpr::list(a, unOpName(u.op), &[toExpr(a, u.value)]), |
|
| 261 | 261 | case super::NodeValue::Call(c) => { |
|
| 262 | - | let buf = try! sexpr::allocExprs(a, c.args.len + 1); |
|
| 262 | + | let buf = try! sexpr::allocExprs(a, c.args.len as u32 + 1); |
|
| 263 | 263 | buf[0] = toExpr(a, c.callee); |
|
| 264 | - | for i in 0..c.args.len { buf[i + 1] = toExpr(a, c.args.list[i]); } |
|
| 264 | + | for i in 0..c.args.len { buf[i + 1] = toExpr(a, c.args[i]); } |
|
| 265 | 265 | return sexpr::Expr::List { head: "call", tail: buf, multiline: false }; |
|
| 266 | 266 | } |
|
| 267 | 267 | case super::NodeValue::BuiltinCall { kind, args } => |
|
| 268 | - | return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args)), |
|
| 268 | + | return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args[..])), |
|
| 269 | 269 | case super::NodeValue::Subscript { container, index } => |
|
| 270 | 270 | return sexpr::list(a, "[]", &[toExpr(a, container), toExpr(a, index)]), |
|
| 271 | 271 | case super::NodeValue::FieldAccess(acc) => |
|
| 272 | 272 | return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]), |
|
| 273 | 273 | case super::NodeValue::ScopeAccess(acc) => |
| 278 | 278 | case super::NodeValue::Deref(target) => |
|
| 279 | 279 | return sexpr::list(a, "deref", &[toExpr(a, target)]), |
|
| 280 | 280 | case super::NodeValue::As(cast) => |
|
| 281 | 281 | return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]), |
|
| 282 | 282 | case super::NodeValue::ArrayLit(elems) => |
|
| 283 | - | return sexpr::list(a, "array", nodeListToExprs(a, &elems)), |
|
| 283 | + | return sexpr::list(a, "array", nodeListToExprs(a, &elems[..])), |
|
| 284 | 284 | case super::NodeValue::ArrayRepeatLit(rep) => |
|
| 285 | 285 | return sexpr::list(a, "array-repeat", &[toExpr(a, rep.item), toExpr(a, rep.count)]), |
|
| 286 | 286 | case super::NodeValue::RecordLit(lit) => { |
|
| 287 | - | let mut total: u32 = lit.fields.len; |
|
| 287 | + | let mut total: u32 = lit.fields.len as u32; |
|
| 288 | 288 | if let _ = lit.typeName { |
|
| 289 | 289 | total += 1; |
|
| 290 | 290 | } |
|
| 291 | 291 | let buf = try! sexpr::allocExprs(a, total); |
|
| 292 | 292 | let mut idx: u32 = 0; |
|
| 293 | 293 | if let tn = lit.typeName { |
|
| 294 | 294 | buf[idx] = toExpr(a, tn); idx = idx + 1; |
|
| 295 | 295 | } |
|
| 296 | 296 | for i in 0..lit.fields.len { |
|
| 297 | - | buf[idx + i] = toExpr(a, lit.fields.list[i]); |
|
| 297 | + | buf[idx + i] = toExpr(a, lit.fields[i]); |
|
| 298 | 298 | } |
|
| 299 | 299 | return sexpr::Expr::List { head: "record-lit", tail: buf, multiline: lit.fields.len > 2 }; |
|
| 300 | 300 | } |
|
| 301 | 301 | case super::NodeValue::RecordLitField(f) => |
|
| 302 | 302 | return sexpr::list(a, "field", &[toExprOpt(a, f.label), toExpr(a, f.value)]), |
| 314 | 314 | } |
|
| 315 | 315 | case super::NodeValue::Try(t) => { |
|
| 316 | 316 | let mut head = "try"; |
|
| 317 | 317 | if t.shouldPanic { head = "try!"; } |
|
| 318 | 318 | if t.catches.len > 0 { |
|
| 319 | - | let catches = nodeListToExprs(a, &t.catches); |
|
| 319 | + | let catches = nodeListToExprs(a, &t.catches[..]); |
|
| 320 | 320 | return sexpr::list(a, head, &[toExpr(a, t.expr), sexpr::block(a, "catches", &[], catches)]); |
|
| 321 | 321 | } |
|
| 322 | 322 | return sexpr::list(a, head, &[toExpr(a, t.expr)]); |
|
| 323 | 323 | } |
|
| 324 | 324 | case super::NodeValue::CatchClause(clause) => { |
| 336 | 336 | children[len] = toExpr(a, clause.body); |
|
| 337 | 337 | len += 1; |
|
| 338 | 338 | return sexpr::list(a, head, &children[..len]); |
|
| 339 | 339 | } |
|
| 340 | 340 | case super::NodeValue::Block(blk) => { |
|
| 341 | - | let children = nodeListToExprs(a, &blk.statements); |
|
| 341 | + | let children = nodeListToExprs(a, &blk.statements[..]); |
|
| 342 | 342 | return sexpr::block(a, "block", &[], children); |
|
| 343 | 343 | } |
|
| 344 | 344 | case super::NodeValue::Let(decl) => { |
|
| 345 | 345 | let mut head = "let"; |
|
| 346 | 346 | if decl.mutable { head = "let-mut"; } |
| 405 | 405 | toExpr(a, f.iterable) |
|
| 406 | 406 | ], &[toExpr(a, f.body), toExprOrNull(a, f.elseBranch)]), |
|
| 407 | 407 | case super::NodeValue::Loop { body } => |
|
| 408 | 408 | return sexpr::block(a, "loop", &[], &[toExpr(a, body)]), |
|
| 409 | 409 | case super::NodeValue::Match(m) => { |
|
| 410 | - | let children = prongListToExprs(a, &m.prongs); |
|
| 410 | + | let children = prongListToExprs(a, &m.prongs[..]); |
|
| 411 | 411 | return sexpr::block(a, "match", &[toExpr(a, m.subject)], children); |
|
| 412 | 412 | } |
|
| 413 | 413 | case super::NodeValue::MatchProng(p) => { |
|
| 414 | 414 | return prongToExpr(a, p); |
|
| 415 | 415 | } |
|
| 416 | 416 | case super::NodeValue::FnDecl(f) => { |
|
| 417 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &f.sig.params)); |
|
| 417 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, &f.sig.params[..])); |
|
| 418 | 418 | let ret = toExprOrNull(a, f.sig.returnType); |
|
| 419 | 419 | if let body = f.body { |
|
| 420 | 420 | return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]); |
|
| 421 | 421 | } |
|
| 422 | 422 | return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]); |
|
| 423 | 423 | } |
|
| 424 | 424 | case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]), |
|
| 425 | 425 | case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]), |
|
| 426 | 426 | case super::NodeValue::RecordDecl(r) => { |
|
| 427 | - | let children = fieldListToExprs(a, &r.fields); |
|
| 427 | + | let children = fieldListToExprs(a, &r.fields[..]); |
|
| 428 | 428 | return sexpr::block(a, "record", &[toExpr(a, r.name)], children); |
|
| 429 | 429 | } |
|
| 430 | 430 | case super::NodeValue::RecordField { field, type, value } => { |
|
| 431 | 431 | return fieldToExpr(a, field, type, value); |
|
| 432 | 432 | } |
|
| 433 | 433 | case super::NodeValue::UnionDecl(u_) => { |
|
| 434 | - | let children = variantListToExprs(a, &u_.variants); |
|
| 434 | + | let children = variantListToExprs(a, &u_.variants[..]); |
|
| 435 | 435 | return sexpr::block(a, "union", &[toExpr(a, u_.name)], children); |
|
| 436 | 436 | } |
|
| 437 | 437 | case super::NodeValue::UnionDeclVariant(v) => { |
|
| 438 | 438 | return variantToExpr(a, v.name, v.type); |
|
| 439 | 439 | } |
|
| 440 | 440 | case super::NodeValue::ExprStmt(e) => return toExpr(a, e), |
|
| 441 | 441 | case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => { |
|
| 442 | - | let children = nodeListToExprs(a, &methods); |
|
| 443 | - | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits)); |
|
| 442 | + | let children = nodeListToExprs(a, &methods[..]); |
|
| 443 | + | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits[..])); |
|
| 444 | 444 | return sexpr::block(a, "trait", &[toExpr(a, name), supers], children); |
|
| 445 | 445 | } |
|
| 446 | 446 | case super::NodeValue::TraitMethodSig { name, receiver, sig } => { |
|
| 447 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params)); |
|
| 447 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 448 | 448 | let ret = toExprOrNull(a, sig.returnType); |
|
| 449 | 449 | return sexpr::list(a, "methodSig", &[toExpr(a, receiver), toExpr(a, name), params, ret]); |
|
| 450 | 450 | } |
|
| 451 | 451 | case super::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 452 | - | let children = nodeListToExprs(a, &methods); |
|
| 452 | + | let children = nodeListToExprs(a, &methods[..]); |
|
| 453 | 453 | return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children); |
|
| 454 | 454 | } |
|
| 455 | 455 | case super::NodeValue::InstanceMethodDecl { name, receiverName, receiverType, sig, body } => { |
|
| 456 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params)); |
|
| 456 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 457 | 457 | let ret = toExprOrNull(a, sig.returnType); |
|
| 458 | 458 | return sexpr::block(a, "method", &[toExpr(a, receiverType), toExpr(a, receiverName), toExpr(a, name), params, ret], &[toExpr(a, body)]); |
|
| 459 | 459 | } |
|
| 460 | 460 | else => return sexpr::sym("?"), |
|
| 461 | 461 | } |
| 464 | 464 | /// Dump the tree rooted at `root`, using the provided arena for allocation. |
|
| 465 | 465 | pub fn printTree(root: *super::Node, arena: *mut alloc::Arena) { |
|
| 466 | 466 | match root.value { |
|
| 467 | 467 | case super::NodeValue::Block(blk) => { |
|
| 468 | 468 | for i in 0..blk.statements.len { |
|
| 469 | - | sexpr::print(toExpr(arena, blk.statements.list[i]), 0); |
|
| 469 | + | sexpr::print(toExpr(arena, blk.statements[i]), 0); |
|
| 470 | 470 | if i < blk.statements.len - 1 { io::print("\n\n"); } |
|
| 471 | 471 | } |
|
| 472 | 472 | io::print("\n"); |
|
| 473 | 473 | } |
|
| 474 | 474 | else => { |
lib/std/lang/gen/regalloc/assign.rad
+1 -1
| 145 | 145 | } |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | // Process each instruction. |
|
| 149 | 149 | for i in 0..block.instrs.len { |
|
| 150 | - | let instr = block.instrs.list[i]; |
|
| 150 | + | let instr = block.instrs[i]; |
|
| 151 | 151 | let mut ctx = InstrCtx { |
|
| 152 | 152 | current: &mut current, |
|
| 153 | 153 | usedRegs: &mut usedRegs, |
|
| 154 | 154 | func, |
|
| 155 | 155 | live, |
lib/std/lang/gen/regalloc/liveness.rad
+6 -6
| 83 | 83 | let block = &func.blocks[b]; |
|
| 84 | 84 | for p in block.params { |
|
| 85 | 85 | maxReg = maxRegNum(p.value.n, maxReg); |
|
| 86 | 86 | } |
|
| 87 | 87 | for i in 0..block.instrs.len { |
|
| 88 | - | il::forEachReg(block.instrs.list[i], maxRegCallback, &mut maxReg as *mut opaque); |
|
| 89 | - | if let dst = il::instrDst(block.instrs.list[i]) { |
|
| 88 | + | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as *mut opaque); |
|
| 89 | + | if let dst = il::instrDst(block.instrs[i]) { |
|
| 90 | 90 | maxReg = maxRegNum(dst.n, maxReg); |
|
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | if maxReg > MAX_SSA_REGS { |
| 150 | 150 | fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) { |
|
| 151 | 151 | for p in block.params { |
|
| 152 | 152 | bitset::set(defs, p.value.n); |
|
| 153 | 153 | } |
|
| 154 | 154 | for i in 0..block.instrs.len { |
|
| 155 | - | let instr = block.instrs.list[i]; |
|
| 155 | + | let instr = block.instrs[i]; |
|
| 156 | 156 | let mut ctx = DefsUses { defs, uses }; |
|
| 157 | 157 | il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque); |
|
| 158 | 158 | ||
| 159 | 159 | if let dst = il::instrDst(instr) { |
|
| 160 | 160 | bitset::set(defs, dst.n); |
| 187 | 187 | /// Add successor "live in" sets to the scratch bitset. |
|
| 188 | 188 | fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], scratch: *mut bitset::Bitset) { |
|
| 189 | 189 | if block.instrs.len == 0 { |
|
| 190 | 190 | return; |
|
| 191 | 191 | } |
|
| 192 | - | let term = block.instrs.list[block.instrs.len - 1]; |
|
| 192 | + | let term = block.instrs[block.instrs.len - 1]; |
|
| 193 | 193 | ||
| 194 | 194 | match term { |
|
| 195 | 195 | case il::Instr::Jmp { target, .. } => |
|
| 196 | 196 | unionBlockLiveIn(target, liveIn, scratch), |
|
| 197 | 197 | case il::Instr::Br { thenTarget, elseTarget, .. } => { |
| 214 | 214 | } |
|
| 215 | 215 | ||
| 216 | 216 | /// Check if this is the last use of a register at this instruction. |
|
| 217 | 217 | pub fn isLastUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 218 | 218 | let block = &func.blocks[blockIdx]; |
|
| 219 | - | let instr = block.instrs.list[instrIdx]; |
|
| 219 | + | let instr = block.instrs[instrIdx]; |
|
| 220 | 220 | ||
| 221 | 221 | if not instrUsesReg(instr, reg) { |
|
| 222 | 222 | return false; |
|
| 223 | 223 | } |
|
| 224 | 224 | if bitset::contains(&info.liveOut[blockIdx], reg.n) { |
|
| 225 | 225 | return false; |
|
| 226 | 226 | } |
|
| 227 | 227 | for i in (instrIdx + 1)..block.instrs.len { |
|
| 228 | - | if instrUsesReg(block.instrs.list[i], reg) { |
|
| 228 | + | if instrUsesReg(block.instrs[i], reg) { |
|
| 229 | 229 | return false; |
|
| 230 | 230 | } |
|
| 231 | 231 | } |
|
| 232 | 232 | return true; |
|
| 233 | 233 | } |
lib/std/lang/gen/regalloc/spill.rad
+2 -2
| 123 | 123 | ||
| 124 | 124 | // Walk instructions backwards. |
|
| 125 | 125 | let mut i = block.instrs.len; |
|
| 126 | 126 | while i > 0 { |
|
| 127 | 127 | i -= 1; |
|
| 128 | - | let instr = block.instrs.list[i]; |
|
| 128 | + | let instr = block.instrs[i]; |
|
| 129 | 129 | ||
| 130 | 130 | // Limit register pressure before processing this instruction. |
|
| 131 | 131 | limitPressure(&mut scratch, &mut spilled, costs, numRegs); |
|
| 132 | 132 | ||
| 133 | 133 | // Enforce cross-call pressure at call sites. |
| 189 | 189 | costs[p.value.n].defs = costs[p.value.n].defs + weight; |
|
| 190 | 190 | } |
|
| 191 | 191 | } |
|
| 192 | 192 | // Count instruction defs and uses. |
|
| 193 | 193 | for i in 0..block.instrs.len { |
|
| 194 | - | let instr = block.instrs.list[i]; |
|
| 194 | + | let instr = block.instrs[i]; |
|
| 195 | 195 | ||
| 196 | 196 | // Count definition. |
|
| 197 | 197 | if let dst = il::instrDst(instr) { |
|
| 198 | 198 | if dst.n < costs.len { |
|
| 199 | 199 | costs[dst.n].defs = costs[dst.n].defs + weight; |
lib/std/lang/il.rad
+1 -31
| 311 | 311 | /// Block label. |
|
| 312 | 312 | label: *[u8], |
|
| 313 | 313 | /// Block parameters. |
|
| 314 | 314 | params: *[Param], |
|
| 315 | 315 | /// Instructions in the block. The last instruction must be a terminator. |
|
| 316 | - | instrs: InstrList, |
|
| 316 | + | instrs: *mut [Instr], |
|
| 317 | 317 | /// Source locations for debugging and error reporting. |
|
| 318 | 318 | /// One entry per instruction when debug info is enabled, empty otherwise. |
|
| 319 | 319 | locs: *[SrcLoc], |
|
| 320 | 320 | /// Predecessor block indices. Used for control flow analysis. |
|
| 321 | 321 | preds: *[u32], |
| 391 | 391 | fns: *[*Fn], |
|
| 392 | 392 | /// Index of entry point function, if any. |
|
| 393 | 393 | defaultFnIdx: ?u32, |
|
| 394 | 394 | } |
|
| 395 | 395 | ||
| 396 | - | /////////////////////// |
|
| 397 | - | // Instruction Lists // |
|
| 398 | - | /////////////////////// |
|
| 399 | - | ||
| 400 | - | /// A growable list of instructions backed by arena storage. |
|
| 401 | - | pub record InstrList { |
|
| 402 | - | /// Pre-allocated instruction storage. |
|
| 403 | - | list: *mut [Instr], |
|
| 404 | - | /// Current number of instructions. |
|
| 405 | - | len: u32, |
|
| 406 | - | } |
|
| 407 | - | ||
| 408 | - | /// Create a new instruction list with the given capacity. |
|
| 409 | - | pub fn instrList(arena: *mut alloc::Arena, capacity: u32) -> InstrList throws (alloc::AllocError) { |
|
| 410 | - | if capacity == 0 { |
|
| 411 | - | return InstrList { list: undefined, len: 0 }; |
|
| 412 | - | } |
|
| 413 | - | let instrs = try alloc::allocSlice(arena, @sizeOf(Instr), @alignOf(Instr), capacity); |
|
| 414 | - | return InstrList { list: instrs as *mut [Instr], len: 0 }; |
|
| 415 | - | } |
|
| 416 | - | ||
| 417 | - | /// Push an instruction onto the list. Panics if at capacity. |
|
| 418 | - | pub fn instrListPush(self: *mut InstrList, instr: Instr) { |
|
| 419 | - | if self.len >= self.list.len { |
|
| 420 | - | panic "instrListPush: out of space"; |
|
| 421 | - | } |
|
| 422 | - | self.list[self.len] = instr; |
|
| 423 | - | self.len += 1; |
|
| 424 | - | } |
|
| 425 | - | ||
| 426 | 396 | /////////////////////// |
|
| 427 | 397 | // Utility Functions // |
|
| 428 | 398 | /////////////////////// |
|
| 429 | 399 | ||
| 430 | 400 | /// Get the destination register of an instruction, if any. |
lib/std/lang/il/printer.rad
+7 -9
| 79 | 79 | sexpr::printStringTo(out, s); |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Check if name contains the path separator. |
|
| 83 | 83 | fn needsQuoting(name: *[u8]) -> bool { |
|
| 84 | - | for i in 0..name.len { |
|
| 85 | - | if name[i] == ':' { |
|
| 84 | + | for ch in name { |
|
| 85 | + | if ch == ':' { |
|
| 86 | 86 | return true; |
|
| 87 | 87 | } |
|
| 88 | 88 | } |
|
| 89 | 89 | return false; |
|
| 90 | 90 | } |
| 333 | 333 | } |
|
| 334 | 334 | } |
|
| 335 | 335 | case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
|
| 336 | 336 | write(out, "switch "); |
|
| 337 | 337 | writeVal(out, a, val); |
|
| 338 | - | for i in 0..cases.len { |
|
| 339 | - | let c = cases[i]; |
|
| 338 | + | for c in cases { |
|
| 340 | 339 | write(out, " ("); |
|
| 341 | 340 | write(out, formatI64(a, c.value)); |
|
| 342 | 341 | write(out, " @"); |
|
| 343 | 342 | write(out, blocks[c.target].label); |
|
| 344 | 343 | if c.args.len > 0 { |
| 433 | 432 | write(out, ")"); |
|
| 434 | 433 | } |
|
| 435 | 434 | write(out, "\n"); |
|
| 436 | 435 | ||
| 437 | 436 | // Instructions. |
|
| 438 | - | let instrs = &block.instrs.list[0..block.instrs.len]; |
|
| 439 | - | for i in 0..instrs.len { |
|
| 437 | + | for instr in block.instrs { |
|
| 440 | 438 | indent(out, 1); |
|
| 441 | - | writeInstr(out, a, blocks, instrs[i]); |
|
| 439 | + | writeInstr(out, a, blocks, instr); |
|
| 442 | 440 | write(out, ";\n"); |
|
| 443 | 441 | } |
|
| 444 | 442 | } |
|
| 445 | 443 | ||
| 446 | 444 | /////////////////////// |
| 525 | 523 | writeSymbol(out, d.name); |
|
| 526 | 524 | write(out, " align "); |
|
| 527 | 525 | write(out, formatU32(a, d.alignment)); |
|
| 528 | 526 | write(out, " {\n"); |
|
| 529 | 527 | ||
| 530 | - | for i in 0..d.values.len { |
|
| 528 | + | for v in d.values { |
|
| 531 | 529 | indent(out, 1); |
|
| 532 | - | writeDataValue(out, a, d.values[i]); |
|
| 530 | + | writeDataValue(out, a, v); |
|
| 533 | 531 | write(out, ";\n"); |
|
| 534 | 532 | } |
|
| 535 | 533 | write(out, "}\n"); |
|
| 536 | 534 | } |
|
| 537 | 535 |
lib/std/lang/lower.rad
+214 -433
| 198 | 198 | ||
| 199 | 199 | /////////////// |
|
| 200 | 200 | // Constants // |
|
| 201 | 201 | /////////////// |
|
| 202 | 202 | ||
| 203 | - | /// Initial capacity for instructions per block. |
|
| 204 | - | const INITIAL_BLOCK_INSTRS: u32 = 8; |
|
| 205 | - | /// Initial capacity for blocks per function. |
|
| 206 | - | const INITIAL_BLOCKS: u32 = 8; |
|
| 207 | - | /// Initial capacity for predecessor lists. |
|
| 208 | - | const INITIAL_PREDS: u32 = 4; |
|
| 209 | - | /// Initial capacity for global data. |
|
| 210 | - | const INITIAL_DATA: u32 = 8; |
|
| 211 | - | /// Initial capacity for functions. |
|
| 212 | - | const INITIAL_FNS: u32 = 16; |
|
| 213 | - | /// Initial capacity for the error tag table. |
|
| 214 | - | const INITIAL_ERR_TAGS: u32 = 8; |
|
| 215 | 203 | /// Maximum nesting depth of loops. |
|
| 216 | 204 | const MAX_LOOP_DEPTH: u32 = 16; |
|
| 217 | - | /// Maximum number of function symbols to track. |
|
| 218 | - | const MAX_FN_SYMS: u32 = 2048; |
|
| 219 | 205 | /// Maximum number of `catch` clauses per `try`. |
|
| 220 | 206 | const MAX_CATCH_CLAUSES: u32 = 32; |
|
| 221 | 207 | ||
| 222 | 208 | // Slice Layout |
|
| 223 | 209 | // |
| 273 | 259 | /// Holds global state like the data section (strings, constants) and provides |
|
| 274 | 260 | /// access to the resolver for type queries. |
|
| 275 | 261 | pub record Lowerer { |
|
| 276 | 262 | /// Arena for IL allocations. All IL nodes are allocated here. |
|
| 277 | 263 | arena: *mut alloc::Arena, |
|
| 264 | + | /// Allocator backed by the arena. |
|
| 265 | + | allocator: alloc::Allocator, |
|
| 278 | 266 | /// Resolver for type information. Used to query types, symbols, and |
|
| 279 | 267 | /// compile-time constant values during lowering. |
|
| 280 | 268 | resolver: *resolver::Resolver, |
|
| 281 | 269 | /// Module graph for cross-module symbol resolution. |
|
| 282 | 270 | moduleGraph: ?*module::ModuleGraph, |
| 285 | 273 | /// Current module being lowered. |
|
| 286 | 274 | currentMod: ?u16, |
|
| 287 | 275 | /// Global data items (string literals, constants, static arrays). |
|
| 288 | 276 | /// These become the data sections in the final binary. |
|
| 289 | 277 | data: *mut [il::Data], |
|
| 290 | - | /// Number of data items currently stored. |
|
| 291 | - | dataCount: u32, |
|
| 292 | 278 | /// Lowered functions. |
|
| 293 | 279 | fns: *mut [*il::Fn], |
|
| 294 | - | /// Number of functions currently stored. |
|
| 295 | - | fnCount: u32, |
|
| 296 | 280 | /// Map of function symbols to qualified names. |
|
| 297 | 281 | fnSyms: *mut [FnSymEntry], |
|
| 298 | - | /// Number of entries in fnSyms. |
|
| 299 | - | fnSymsLen: u32, |
|
| 300 | 282 | /// Global error type tag table. Maps nominal types to unique tags. |
|
| 301 | 283 | errTags: *mut [ErrTagEntry], |
|
| 302 | - | /// Number of entries in the error tag table. |
|
| 303 | - | errTagsLen: u32, |
|
| 304 | 284 | /// Next error tag to assign (starts at 1; 0 = success). |
|
| 305 | 285 | errTagCounter: u32, |
|
| 306 | 286 | /// Lowering options. |
|
| 307 | 287 | options: LowerOptions, |
|
| 308 | 288 | } |
| 322 | 302 | } |
|
| 323 | 303 | ||
| 324 | 304 | /// Compute the maximum size of any error type in a throw list. |
|
| 325 | 305 | fn maxErrSize(throwList: *[*resolver::Type]) -> u32 { |
|
| 326 | 306 | let mut maxSize: u32 = 0; |
|
| 327 | - | for i in 0..throwList.len { |
|
| 328 | - | let size = resolver::getTypeLayout(*throwList[i]).size; |
|
| 307 | + | for ty in throwList { |
|
| 308 | + | let size = resolver::getTypeLayout(*ty).size; |
|
| 329 | 309 | if size > maxSize { |
|
| 330 | 310 | maxSize = size; |
|
| 331 | 311 | } |
|
| 332 | 312 | } |
|
| 333 | 313 | return maxSize; |
|
| 334 | 314 | } |
|
| 335 | 315 | ||
| 336 | 316 | /// Get or assign a globally unique error tag for the given error type. |
|
| 337 | 317 | /// Tag `0` is reserved for success; error tags start at `1`. |
|
| 338 | 318 | fn getOrAssignErrorTag(self: *mut Lowerer, errType: resolver::Type) -> u32 { |
|
| 339 | - | for i in 0..self.errTagsLen { |
|
| 340 | - | if self.errTags[i].ty == errType { |
|
| 341 | - | return self.errTags[i].tag; |
|
| 319 | + | for entry in self.errTags { |
|
| 320 | + | if entry.ty == errType { |
|
| 321 | + | return entry.tag; |
|
| 342 | 322 | } |
|
| 343 | 323 | } |
|
| 344 | 324 | let tag = self.errTagCounter; |
|
| 345 | - | self.errTagCounter += 1; |
|
| 346 | 325 | ||
| 347 | - | if self.errTagsLen >= self.errTags.len { |
|
| 348 | - | self.errTags = try! alloc::growSlice( |
|
| 349 | - | self.arena, |
|
| 350 | - | self.errTags as *mut [opaque], |
|
| 351 | - | self.errTagsLen, |
|
| 352 | - | @sizeOf(ErrTagEntry), |
|
| 353 | - | @alignOf(ErrTagEntry) |
|
| 354 | - | ) as *mut [ErrTagEntry]; |
|
| 355 | - | } |
|
| 356 | - | self.errTags[self.errTagsLen] = ErrTagEntry { ty: errType, tag }; |
|
| 357 | - | self.errTagsLen += 1; |
|
| 326 | + | self.errTagCounter += 1; |
|
| 327 | + | self.errTags.append(ErrTagEntry { ty: errType, tag }, self.allocator); |
|
| 358 | 328 | ||
| 359 | 329 | return tag; |
|
| 360 | 330 | } |
|
| 361 | 331 | ||
| 362 | 332 | /// Builder for accumulating data values during constant lowering. |
|
| 363 | 333 | record DataValueBuilder { |
|
| 364 | - | arena: *mut alloc::Arena, |
|
| 334 | + | allocator: alloc::Allocator, |
|
| 365 | 335 | values: *mut [il::DataValue], |
|
| 366 | - | len: u32, |
|
| 367 | 336 | /// Whether all values pushed are undefined. |
|
| 368 | 337 | allUndef: bool, |
|
| 369 | 338 | } |
|
| 370 | 339 | ||
| 371 | 340 | /// Result of lowering constant data. |
|
| 372 | 341 | record ConstDataResult { |
|
| 373 | 342 | values: *[il::DataValue], |
|
| 374 | 343 | isUndefined: bool, |
|
| 375 | 344 | } |
|
| 376 | 345 | ||
| 377 | - | /// Create a new builder with an initial capacity. |
|
| 378 | - | fn dataBuilder(arena: *mut alloc::Arena) -> DataValueBuilder { |
|
| 379 | - | let values = try! alloc::allocSlice( |
|
| 380 | - | arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), INITIAL_DATA |
|
| 381 | - | ) as *mut [il::DataValue]; |
|
| 382 | - | ||
| 383 | - | return DataValueBuilder { arena, values, len: 0, allUndef: true }; |
|
| 346 | + | /// Create a new builder. |
|
| 347 | + | fn dataBuilder(allocator: alloc::Allocator) -> DataValueBuilder { |
|
| 348 | + | return DataValueBuilder { allocator, values: &mut [], allUndef: true }; |
|
| 384 | 349 | } |
|
| 385 | 350 | ||
| 386 | - | /// Append a data value to the builder, growing the backing array if needed. |
|
| 351 | + | /// Append a data value to the builder. |
|
| 387 | 352 | fn dataBuilderPush(b: *mut DataValueBuilder, value: il::DataValue) { |
|
| 388 | - | if b.len >= b.values.len { |
|
| 389 | - | b.values = try! alloc::growSlice( |
|
| 390 | - | b.arena, b.values, b.len, |
|
| 391 | - | @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 392 | - | ) as *mut [il::DataValue]; |
|
| 393 | - | } |
|
| 394 | - | b.values[b.len] = value; |
|
| 395 | - | b.len += 1; |
|
| 353 | + | b.values.append(value, b.allocator); |
|
| 396 | 354 | ||
| 397 | 355 | if value.item != il::DataItem::Undef { |
|
| 398 | 356 | b.allUndef = false; |
|
| 399 | 357 | } |
|
| 400 | 358 | } |
|
| 401 | 359 | ||
| 402 | 360 | /// Return the accumulated values. |
|
| 403 | 361 | fn dataBuilderFinish(b: *DataValueBuilder) -> ConstDataResult { |
|
| 404 | 362 | return ConstDataResult { |
|
| 405 | - | values: &b.values[..b.len], |
|
| 363 | + | values: &b.values[..], |
|
| 406 | 364 | isUndefined: b.allUndef, |
|
| 407 | 365 | }; |
|
| 408 | 366 | } |
|
| 409 | 367 | ||
| 410 | 368 | /////////////////////////// |
| 457 | 415 | var: Var, |
|
| 458 | 416 | /// SSA register containing the parameter value from the caller. |
|
| 459 | 417 | reg: il::Reg, |
|
| 460 | 418 | } |
|
| 461 | 419 | ||
| 462 | - | /// A growable list of u32 values. |
|
| 463 | - | // TODO: I don't love these `u32` lists. |
|
| 464 | - | record U32List { |
|
| 465 | - | list: *mut [u32], |
|
| 466 | - | len: u32, |
|
| 467 | - | } |
|
| 468 | - | ||
| 469 | 420 | //////////////////////////////// |
|
| 470 | 421 | // Basic Block Representation // |
|
| 471 | 422 | //////////////////////////////// |
|
| 472 | 423 | ||
| 473 | 424 | // During lowering, we build a CFG of basic blocks. Each block accumulates |
| 479 | 430 | pub record BlockId { |
|
| 480 | 431 | /// Index into the function's block array. |
|
| 481 | 432 | n: u32, |
|
| 482 | 433 | } |
|
| 483 | 434 | ||
| 484 | - | /// A growable list of block parameters. |
|
| 485 | - | record BlockParams { |
|
| 486 | - | list: *mut [il::Param], |
|
| 487 | - | len: u32, |
|
| 488 | - | } |
|
| 489 | - | ||
| 490 | 435 | /// Internal block state during construction. |
|
| 491 | 436 | /// |
|
| 492 | 437 | /// The key invariants: |
|
| 493 | 438 | /// |
|
| 494 | 439 | /// - A block is "open" if it has no terminator; instructions can be added. |
| 499 | 444 | record BlockData { |
|
| 500 | 445 | /// Block label for debugging and IL printing. |
|
| 501 | 446 | label: *[u8], |
|
| 502 | 447 | /// Block parameters for merging values at control flow joins. These |
|
| 503 | 448 | /// receive values from predecessor edges when control flow merges. |
|
| 504 | - | params: BlockParams, |
|
| 449 | + | params: *mut [il::Param], |
|
| 505 | 450 | /// Variable ids corresponding to each parameter. Used to map block params |
|
| 506 | 451 | /// back to source variables when building argument lists for jumps. |
|
| 507 | - | paramVars: U32List, |
|
| 452 | + | paramVars: *mut [u32], |
|
| 508 | 453 | /// Instructions accumulated so far. The last instruction should eventually |
|
| 509 | 454 | /// be a terminator. |
|
| 510 | - | instrs: il::InstrList, |
|
| 511 | - | /// Debug source locations, one per instruction. Only populated when debug |
|
| 512 | - | /// info is enabled. |
|
| 455 | + | instrs: *mut [il::Instr], |
|
| 456 | + | /// Debug source locations, one per instruction. Only populated when |
|
| 457 | + | /// debug info is enabled. |
|
| 513 | 458 | locs: *mut [il::SrcLoc], |
|
| 514 | - | /// Number of debug locations stored. |
|
| 515 | - | locsLen: u32, |
|
| 516 | 459 | /// Predecessor block ids. Used for SSA construction to propagate values |
|
| 517 | 460 | /// from predecessors when a variable is used before being defined locally. |
|
| 518 | - | preds: U32List, |
|
| 461 | + | preds: *mut [u32], |
|
| 519 | 462 | /// The current SSA value of each variable in this block. Indexed by variable |
|
| 520 | 463 | /// id. A `nil` means the variable wasn't assigned in this block. Updated by |
|
| 521 | 464 | /// [`defVar`], queried by [`useVarInBlock`]. |
|
| 522 | 465 | vars: *mut [?il::Val], |
|
| 523 | 466 | /// Sealing state. Once sealed, all predecessors are known and we can resolve |
| 533 | 476 | /// During this time, variables used before being defined locally are tracked. |
|
| 534 | 477 | /// Once all predecessors are known, the block is sealed and those variables |
|
| 535 | 478 | /// are resolved via [`resolveBlockArgs`]. |
|
| 536 | 479 | union Sealed { |
|
| 537 | 480 | /// Block is unsealed; predecessors may still be added. |
|
| 538 | - | No { incompleteVars: U32List }, |
|
| 481 | + | No { incompleteVars: *mut [u32] }, |
|
| 539 | 482 | /// Block is sealed; all predecessors are known. |
|
| 540 | 483 | Yes, |
|
| 541 | 484 | } |
|
| 542 | 485 | ||
| 543 | 486 | /////////////////////////////////// |
| 664 | 607 | /// Per-function lowering state. Created fresh for each function and contains |
|
| 665 | 608 | /// all the mutable state needed during function body lowering. |
|
| 666 | 609 | record FnLowerer { |
|
| 667 | 610 | /// Reference to the module-level lowerer. |
|
| 668 | 611 | low: *mut Lowerer, |
|
| 612 | + | /// Allocator for IL allocations. |
|
| 613 | + | allocator: alloc::Allocator, |
|
| 669 | 614 | /// Type signature of the function being lowered. |
|
| 670 | 615 | fnType: *resolver::FnType, |
|
| 671 | 616 | /// Function name, used as prefix for generated data symbols. |
|
| 672 | 617 | fnName: *[u8], |
|
| 673 | 618 |
| 675 | 620 | ||
| 676 | 621 | /// Metadata (name, type, mutability) for each variable. Indexed by variable |
|
| 677 | 622 | /// id. Doesn't change after declaration. For the SSA value of a variable in |
|
| 678 | 623 | /// a specific block, see [`BlockData::vars`]. |
|
| 679 | 624 | vars: *mut [VarData], |
|
| 680 | - | /// Number of declared variables. |
|
| 681 | - | varsLen: u32, |
|
| 682 | - | ||
| 683 | - | // ~ Function parameters ~ // |
|
| 684 | - | ||
| 685 | 625 | /// Parameter-to-variable bindings, initialized in the entry block. |
|
| 686 | 626 | params: *mut [FnParamBinding], |
|
| 687 | - | /// Number of function parameters. |
|
| 688 | - | paramsLen: u32, |
|
| 689 | 627 | ||
| 690 | 628 | // ~ Basic block management ~ // |
|
| 691 | 629 | ||
| 692 | 630 | /// Block storage array, indexed by block id. |
|
| 693 | 631 | blockData: *mut [BlockData], |
|
| 694 | - | /// Number of blocks created so far. |
|
| 695 | - | blockCount: u32, |
|
| 696 | 632 | /// The entry block for this function. |
|
| 697 | 633 | entryBlock: ?BlockId, |
|
| 698 | 634 | /// The block currently receiving new instructions. |
|
| 699 | 635 | currentBlock: ?BlockId, |
|
| 700 | 636 |
| 742 | 678 | res: *resolver::Resolver, |
|
| 743 | 679 | root: *ast::Node, |
|
| 744 | 680 | pkgName: *[u8], |
|
| 745 | 681 | arena: *mut alloc::Arena |
|
| 746 | 682 | ) -> il::Program throws (LowerError) { |
|
| 747 | - | let data = try! alloc::allocSlice(arena, @sizeOf(il::Data), @alignOf(il::Data), INITIAL_DATA) as *mut [il::Data]; |
|
| 748 | - | let fns = try! alloc::allocSlice(arena, @sizeOf(*il::Fn), @alignOf(*il::Fn), INITIAL_FNS) as *mut [*il::Fn]; |
|
| 749 | - | let fnSyms = try! alloc::allocSlice(arena, @sizeOf(FnSymEntry), @alignOf(FnSymEntry), INITIAL_FNS) as *mut [FnSymEntry]; |
|
| 750 | - | let errTags = try! alloc::allocSlice(arena, @sizeOf(ErrTagEntry), @alignOf(ErrTagEntry), INITIAL_ERR_TAGS) as *mut [ErrTagEntry]; |
|
| 751 | 683 | let mut low = Lowerer { |
|
| 752 | 684 | arena: arena, |
|
| 685 | + | allocator: alloc::arenaAllocator(arena), |
|
| 753 | 686 | resolver: res, |
|
| 754 | 687 | moduleGraph: nil, |
|
| 755 | 688 | pkgName, |
|
| 756 | 689 | currentMod: nil, |
|
| 757 | - | data, |
|
| 758 | - | dataCount: 0, |
|
| 759 | - | fns, |
|
| 760 | - | fnCount: 0, |
|
| 761 | - | fnSyms, |
|
| 762 | - | fnSymsLen: 0, |
|
| 763 | - | errTags, |
|
| 764 | - | errTagsLen: 0, |
|
| 690 | + | data: &mut [], |
|
| 691 | + | fns: &mut [], |
|
| 692 | + | fnSyms: &mut [], |
|
| 693 | + | errTags: &mut [], |
|
| 765 | 694 | errTagCounter: 1, |
|
| 766 | 695 | options: LowerOptions { debug: false, buildTest: false }, |
|
| 767 | 696 | }; |
|
| 768 | 697 | let defaultFnIdx = try lowerDecls(&mut low, root, true); |
|
| 769 | 698 | ||
| 770 | 699 | return il::Program { |
|
| 771 | - | data: &low.data[..low.dataCount], |
|
| 772 | - | fns: &low.fns[..low.fnCount], |
|
| 700 | + | data: &low.data[..], |
|
| 701 | + | fns: &low.fns[..], |
|
| 773 | 702 | defaultFnIdx, |
|
| 774 | 703 | }; |
|
| 775 | 704 | } |
|
| 776 | 705 | ||
| 777 | 706 | ///////////////////////////////// |
| 784 | 713 | graph: *module::ModuleGraph, |
|
| 785 | 714 | pkgName: *[u8], |
|
| 786 | 715 | arena: *mut alloc::Arena, |
|
| 787 | 716 | options: LowerOptions |
|
| 788 | 717 | ) -> Lowerer { |
|
| 789 | - | let data = try! alloc::allocSlice( |
|
| 790 | - | arena, @sizeOf(il::Data), @alignOf(il::Data), INITIAL_DATA |
|
| 791 | - | ) as *mut [il::Data]; |
|
| 792 | - | ||
| 793 | - | let fns = try! alloc::allocSlice( |
|
| 794 | - | arena, @sizeOf(*il::Fn), @alignOf(*il::Fn), INITIAL_FNS |
|
| 795 | - | ) as *mut [*il::Fn]; |
|
| 796 | - | ||
| 797 | - | let fnSyms = try! alloc::allocSlice( |
|
| 798 | - | arena, @sizeOf(FnSymEntry), @alignOf(FnSymEntry), MAX_FN_SYMS |
|
| 799 | - | ) as *mut [FnSymEntry]; |
|
| 800 | - | ||
| 801 | - | let errTags = try! alloc::allocSlice( |
|
| 802 | - | arena, @sizeOf(ErrTagEntry), @alignOf(ErrTagEntry), INITIAL_ERR_TAGS |
|
| 803 | - | ) as *mut [ErrTagEntry]; |
|
| 804 | - | ||
| 805 | 718 | return Lowerer { |
|
| 806 | 719 | arena, |
|
| 720 | + | allocator: alloc::arenaAllocator(arena), |
|
| 807 | 721 | resolver: res, |
|
| 808 | 722 | moduleGraph: graph, |
|
| 809 | 723 | pkgName, |
|
| 810 | 724 | currentMod: nil, |
|
| 811 | - | data, |
|
| 812 | - | dataCount: 0, |
|
| 813 | - | fns, |
|
| 814 | - | fnCount: 0, |
|
| 815 | - | fnSyms, |
|
| 816 | - | fnSymsLen: 0, |
|
| 817 | - | errTags, |
|
| 818 | - | errTagsLen: 0, |
|
| 725 | + | data: &mut [], |
|
| 726 | + | fns: &mut [], |
|
| 727 | + | fnSyms: &mut [], |
|
| 728 | + | errTags: &mut [], |
|
| 819 | 729 | errTagCounter: 1, |
|
| 820 | 730 | options, |
|
| 821 | 731 | }; |
|
| 822 | 732 | } |
|
| 823 | 733 |
| 836 | 746 | /// Lower all top-level declarations in a block. |
|
| 837 | 747 | fn lowerDecls(low: *mut Lowerer, root: *ast::Node, isRoot: bool) -> ?u32 throws (LowerError) { |
|
| 838 | 748 | let case ast::NodeValue::Block(block) = root.value else { |
|
| 839 | 749 | throw LowerError::ExpectedBlock(root); |
|
| 840 | 750 | }; |
|
| 841 | - | let stmtsList = block.statements.list; |
|
| 842 | - | let stmtsLen = block.statements.len; |
|
| 751 | + | let stmtsList = block.statements; |
|
| 843 | 752 | let mut defaultFnIdx: ?u32 = nil; |
|
| 844 | 753 | ||
| 845 | - | for i in 0..stmtsLen { |
|
| 846 | - | let node = stmtsList[i]; |
|
| 754 | + | for node in stmtsList { |
|
| 847 | 755 | match node.value { |
|
| 848 | 756 | case ast::NodeValue::FnDecl(decl) => { |
|
| 849 | 757 | if let f = try lowerFnDecl(low, node, decl) { |
|
| 850 | 758 | if isRoot and checkAttr(decl.attrs, ast::Attribute::Default) { |
|
| 851 | - | defaultFnIdx = low.fnCount; |
|
| 759 | + | defaultFnIdx = low.fns.len; |
|
| 852 | 760 | } |
|
| 853 | 761 | try pushFn(low, f); |
|
| 854 | 762 | } |
|
| 855 | 763 | } |
|
| 856 | 764 | case ast::NodeValue::ConstDecl(decl) => { |
| 858 | 766 | } |
|
| 859 | 767 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 860 | 768 | try lowerDataDecl(low, node, decl.value, false); |
|
| 861 | 769 | } |
|
| 862 | 770 | case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 863 | - | try lowerInstanceDecl(low, node, traitName, targetType, &methods); |
|
| 771 | + | try lowerInstanceDecl(low, node, traitName, targetType, methods); |
|
| 864 | 772 | } |
|
| 865 | 773 | else => {}, |
|
| 866 | 774 | } |
|
| 867 | 775 | } |
|
| 868 | 776 | return defaultFnIdx; |
|
| 869 | 777 | } |
|
| 870 | 778 | ||
| 871 | 779 | /// Finalize lowering and return the unified IL program. |
|
| 872 | 780 | pub fn finalize(low: *Lowerer, defaultFnIdx: ?u32) -> il::Program { |
|
| 873 | 781 | return il::Program { |
|
| 874 | - | data: &low.data[..low.dataCount], |
|
| 875 | - | fns: &low.fns[..low.fnCount], |
|
| 782 | + | data: &low.data[..], |
|
| 783 | + | fns: &low.fns[..], |
|
| 876 | 784 | defaultFnIdx, |
|
| 877 | 785 | }; |
|
| 878 | 786 | } |
|
| 879 | 787 | ||
| 880 | 788 | ///////////////////////////////// |
| 909 | 817 | } |
|
| 910 | 818 | return il::formatQualifiedName(self.arena, path, name); |
|
| 911 | 819 | } |
|
| 912 | 820 | ||
| 913 | 821 | /// Register a function symbol with its qualified name. |
|
| 914 | - | /// Called when lowering function declarations, so cross-package calls can find the function |
|
| 915 | - | /// by name. |
|
| 822 | + | /// Called when lowering function declarations, so cross-package calls can find |
|
| 823 | + | /// the function by name. |
|
| 916 | 824 | fn registerFnSym(self: *mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) { |
|
| 917 | - | if self.fnSymsLen >= self.fnSyms.len { |
|
| 918 | - | panic "registerFnSym: symbol table full"; |
|
| 919 | - | } |
|
| 920 | - | self.fnSyms[self.fnSymsLen] = FnSymEntry { sym, qualName }; |
|
| 921 | - | self.fnSymsLen += 1; |
|
| 825 | + | self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator); |
|
| 922 | 826 | } |
|
| 923 | 827 | ||
| 924 | 828 | /// Look up a function's qualified name by its symbol. |
|
| 925 | 829 | /// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered). |
|
| 926 | 830 | // TODO: This is kind of dubious as an optimization, if it depends on the order |
|
| 927 | 831 | // in which modules are lowered. |
|
| 928 | 832 | // TODO: Use a hash table here? |
|
| 929 | 833 | fn lookupFnSym(self: *Lowerer, sym: *resolver::Symbol) -> ?*[u8] { |
|
| 930 | - | for i in 0..self.fnSymsLen { |
|
| 931 | - | if self.fnSyms[i].sym == sym { |
|
| 932 | - | return self.fnSyms[i].qualName; |
|
| 834 | + | for entry in self.fnSyms { |
|
| 835 | + | if entry.sym == sym { |
|
| 836 | + | return entry.qualName; |
|
| 933 | 837 | } |
|
| 934 | 838 | } |
|
| 935 | 839 | return nil; |
|
| 936 | 840 | } |
|
| 937 | 841 |
| 948 | 852 | self: *mut Lowerer, |
|
| 949 | 853 | node: *ast::Node, |
|
| 950 | 854 | fnType: *resolver::FnType, |
|
| 951 | 855 | qualName: *[u8] |
|
| 952 | 856 | ) -> FnLowerer { |
|
| 953 | - | let vars = try! alloc::allocSlice(self.arena, @sizeOf(VarData), @alignOf(VarData), fnType.localCount) as *mut [VarData]; |
|
| 954 | - | let params = try! alloc::allocSlice(self.arena, @sizeOf(FnParamBinding), @alignOf(FnParamBinding), fnType.paramTypesLen) as *mut [FnParamBinding]; |
|
| 955 | - | let blockData = try! alloc::allocSlice(self.arena, @sizeOf(BlockData), @alignOf(BlockData), INITIAL_BLOCKS) as *mut [BlockData]; |
|
| 956 | 857 | let loopStack = try! alloc::allocSlice(self.arena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx]; |
|
| 957 | 858 | ||
| 958 | 859 | let mut fnLow = FnLowerer { |
|
| 959 | 860 | low: self, |
|
| 861 | + | allocator: alloc::arenaAllocator(self.arena), |
|
| 960 | 862 | fnType: fnType, |
|
| 961 | 863 | fnName: qualName, |
|
| 962 | - | vars, |
|
| 963 | - | varsLen: 0, |
|
| 964 | - | params, |
|
| 965 | - | paramsLen: 0, |
|
| 966 | - | blockData, |
|
| 967 | - | blockCount: 0, |
|
| 864 | + | vars: &mut [], |
|
| 865 | + | params: &mut [], |
|
| 866 | + | blockData: &mut [], |
|
| 968 | 867 | entryBlock: nil, |
|
| 969 | 868 | currentBlock: nil, |
|
| 970 | 869 | loopStack, |
|
| 971 | 870 | loopDepth: 0, |
|
| 972 | 871 | labelCounter: 0, |
| 1034 | 933 | }; |
|
| 1035 | 934 | // Throwing functions return a result aggregate (word-sized pointer). |
|
| 1036 | 935 | // TODO: The resolver should set an appropriate type that takes into account |
|
| 1037 | 936 | // the throws list. It shouldn't set the return type to the "success" |
|
| 1038 | 937 | // value only. |
|
| 1039 | - | if fnType.throwListLen > 0 { |
|
| 938 | + | if fnType.throwList.len > 0 { |
|
| 1040 | 939 | func.returnType = il::Type::W64; |
|
| 1041 | 940 | } else { |
|
| 1042 | 941 | func.returnType = ilType(self, *fnType.returnType); |
|
| 1043 | 942 | } |
|
| 1044 | 943 | let body = decl.body else { |
| 1095 | 994 | fn lowerInstanceDecl( |
|
| 1096 | 995 | self: *mut Lowerer, |
|
| 1097 | 996 | node: *ast::Node, |
|
| 1098 | 997 | traitNameNode: *ast::Node, |
|
| 1099 | 998 | targetTypeNode: *ast::Node, |
|
| 1100 | - | methods: *ast::NodeList |
|
| 999 | + | methods: *mut [*ast::Node] |
|
| 1101 | 1000 | ) throws (LowerError) { |
|
| 1102 | 1001 | // Look up the trait and type from the resolver. |
|
| 1103 | 1002 | let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym |
|
| 1104 | 1003 | else throw LowerError::MissingSymbol(traitNameNode); |
|
| 1105 | 1004 | let case resolver::SymbolData::Trait(traitInfo) = traitSym.data |
| 1114 | 1013 | // Collect qualified names for the v-table. Empty entries are filled |
|
| 1115 | 1014 | // later from inherited supertrait methods. |
|
| 1116 | 1015 | let mut methodNames: [*[u8]; ast::MAX_TRAIT_METHODS] = undefined; |
|
| 1117 | 1016 | let mut methodNameSet: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
|
| 1118 | 1017 | ||
| 1119 | - | for i in 0..methods.len { |
|
| 1120 | - | let methodNode = methods.list[i]; |
|
| 1018 | + | for methodNode in methods { |
|
| 1121 | 1019 | let case ast::NodeValue::InstanceMethodDecl { |
|
| 1122 | 1020 | name, receiverName, receiverType, sig, body |
|
| 1123 | 1021 | } = methodNode.value else continue; |
|
| 1124 | 1022 | ||
| 1125 | 1023 | let case ast::NodeValue::Ident(mName) = name.value else { |
| 1151 | 1049 | params: lowParams, |
|
| 1152 | 1050 | returnType: ilType(self, *fnType.returnType), |
|
| 1153 | 1051 | isExtern: false, |
|
| 1154 | 1052 | blocks: &[], |
|
| 1155 | 1053 | }; |
|
| 1156 | - | if fnType.throwListLen > 0 { |
|
| 1054 | + | if fnType.throwList.len > 0 { |
|
| 1157 | 1055 | func.returnType = il::Type::W64; |
|
| 1158 | 1056 | } |
|
| 1159 | 1057 | func.blocks = try lowerFnBody(&mut fnLow, body); |
|
| 1160 | 1058 | try pushFn(self, func); |
|
| 1161 | 1059 |
| 1167 | 1065 | } |
|
| 1168 | 1066 | ||
| 1169 | 1067 | // Fill inherited method slots from supertraits. |
|
| 1170 | 1068 | // These methods were already lowered as part of the supertrait instance |
|
| 1171 | 1069 | // declarations and use the same `Type::method` qualified name. |
|
| 1172 | - | for i in 0..traitInfo.methodsLen { |
|
| 1070 | + | for i in 0..traitInfo.methods.len { |
|
| 1173 | 1071 | if not methodNameSet[i] { |
|
| 1174 | 1072 | let mName = traitInfo.methods[i].name; |
|
| 1175 | 1073 | methodNames[i] = instanceMethodName(self, nil, typeName, mName); |
|
| 1176 | 1074 | } |
|
| 1177 | 1075 | } |
|
| 1178 | 1076 | ||
| 1179 | 1077 | // Create v-table in data section, used for dynamic dispatch. |
|
| 1180 | 1078 | let vName = vtableName(self, nil, typeName, tName); |
|
| 1181 | 1079 | let values = try! alloc::allocSlice( |
|
| 1182 | - | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methodsLen |
|
| 1080 | + | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1183 | 1081 | ) as *mut [il::DataValue]; |
|
| 1184 | 1082 | ||
| 1185 | - | for i in 0..traitInfo.methodsLen { |
|
| 1083 | + | for i in 0..traitInfo.methods.len { |
|
| 1186 | 1084 | values[i] = il::DataValue { |
|
| 1187 | 1085 | item: il::DataItem::Fn(methodNames[i]), |
|
| 1188 | 1086 | count: 1, |
|
| 1189 | 1087 | }; |
|
| 1190 | 1088 | } |
|
| 1191 | 1089 | try pushData(self, il::Data { |
|
| 1192 | 1090 | name: vName, |
|
| 1193 | - | size: traitInfo.methodsLen * resolver::PTR_SIZE, |
|
| 1091 | + | size: traitInfo.methods.len as u32 * resolver::PTR_SIZE, |
|
| 1194 | 1092 | alignment: resolver::PTR_SIZE, |
|
| 1195 | 1093 | readOnly: true, |
|
| 1196 | 1094 | isUndefined: false, |
|
| 1197 | - | values: &values[..traitInfo.methodsLen], |
|
| 1095 | + | values: &values[..traitInfo.methods.len as u32], |
|
| 1198 | 1096 | }); |
|
| 1199 | 1097 | } |
|
| 1200 | 1098 | ||
| 1201 | 1099 | /// Check if a function should be lowered. |
|
| 1202 | 1100 | fn shouldLowerFn(decl: *ast::FnDecl, buildTest: bool) -> bool { |
| 1295 | 1193 | if data.ty == resolver::Type::Unknown { |
|
| 1296 | 1194 | throw LowerError::MissingType(node); |
|
| 1297 | 1195 | } |
|
| 1298 | 1196 | let layout = resolver::getTypeLayout(data.ty); |
|
| 1299 | 1197 | let qualName = qualifyName(self, nil, sym.name); |
|
| 1300 | - | let mut b = dataBuilder(self.arena); |
|
| 1198 | + | let mut b = dataBuilder(self.allocator); |
|
| 1301 | 1199 | try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b); |
|
| 1302 | 1200 | let result = dataBuilderFinish(&b); |
|
| 1303 | 1201 | ||
| 1304 | 1202 | try pushData(self, il::Data { |
|
| 1305 | 1203 | name: qualName, |
| 1346 | 1244 | let targetTy = resolver::typeFor(self.resolver, addr.target) |
|
| 1347 | 1245 | else throw LowerError::MissingType(addr.target); |
|
| 1348 | 1246 | let case resolver::Type::Array(arrInfo) = targetTy |
|
| 1349 | 1247 | else throw LowerError::ExpectedArray; |
|
| 1350 | 1248 | ||
| 1351 | - | let mut nested = dataBuilder(self.arena); |
|
| 1249 | + | let mut nested = dataBuilder(self.allocator); |
|
| 1352 | 1250 | let layout = resolver::getTypeLayout(targetTy); |
|
| 1353 | 1251 | try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested); |
|
| 1354 | 1252 | ||
| 1355 | 1253 | let backing = dataBuilderFinish(&nested); |
|
| 1356 | 1254 | let readOnly = not mutable; |
| 1466 | 1364 | } |
|
| 1467 | 1365 | ||
| 1468 | 1366 | /// Flatten a constant array literal `[a, b, c]` into a builder. |
|
| 1469 | 1367 | fn lowerConstArrayLitInto( |
|
| 1470 | 1368 | self: *mut Lowerer, |
|
| 1471 | - | elems: ast::NodeList, |
|
| 1369 | + | elems: *mut [*ast::Node], |
|
| 1472 | 1370 | ty: resolver::Type, |
|
| 1473 | 1371 | dataPrefix: *[u8], |
|
| 1474 | 1372 | b: *mut DataValueBuilder |
|
| 1475 | 1373 | ) throws (LowerError) { |
|
| 1476 | 1374 | let case resolver::Type::Array(arrInfo) = ty |
|
| 1477 | 1375 | else throw LowerError::ExpectedArray; |
|
| 1478 | 1376 | let elemTy = *arrInfo.item; |
|
| 1479 | 1377 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1480 | 1378 | ||
| 1481 | - | for i in 0..elems.len { |
|
| 1482 | - | let elem = elems.list[i]; |
|
| 1379 | + | for elem in elems { |
|
| 1483 | 1380 | try lowerConstDataInto(self, elem, elemTy, elemLayout.size, dataPrefix, b); |
|
| 1484 | 1381 | } |
|
| 1485 | 1382 | } |
|
| 1486 | 1383 | ||
| 1487 | 1384 | /// Build data values for a constant array repeat literal `[item; count]`. |
| 1546 | 1443 | } |
|
| 1547 | 1444 | ||
| 1548 | 1445 | /// Build data values for record constants. |
|
| 1549 | 1446 | fn lowerConstRecordCtorInto( |
|
| 1550 | 1447 | self: *mut Lowerer, |
|
| 1551 | - | args: ast::NodeList, |
|
| 1448 | + | args: *mut [*ast::Node], |
|
| 1552 | 1449 | recInfo: resolver::RecordType, |
|
| 1553 | 1450 | dataPrefix: *[u8], |
|
| 1554 | 1451 | b: *mut DataValueBuilder |
|
| 1555 | 1452 | ) throws (LowerError) { |
|
| 1556 | 1453 | let layout = recInfo.layout; |
|
| 1557 | 1454 | for i in 0..args.len { |
|
| 1558 | - | let argNode = args.list[i]; |
|
| 1455 | + | let argNode = args[i]; |
|
| 1559 | 1456 | let mut valueNode = argNode; |
|
| 1560 | 1457 | if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value { |
|
| 1561 | 1458 | valueNode = fieldLit.value; |
|
| 1562 | 1459 | } |
|
| 1563 | 1460 | let fieldInfo = recInfo.fields[i]; |
|
| 1564 | 1461 | let fieldOffset = fieldInfo.offset as u32; |
|
| 1565 | 1462 | ||
| 1566 | 1463 | // Slot extends to the next field's offset, |
|
| 1567 | 1464 | // or record size for the last field. |
|
| 1568 | - | let slotEnd = recInfo.fields[i + 1].offset as u32 if i + 1 < recInfo.fieldsLen else layout.size; |
|
| 1465 | + | let slotEnd = recInfo.fields[i + 1].offset as u32 if i + 1 < recInfo.fields.len else layout.size; |
|
| 1569 | 1466 | let slotSize = slotEnd - fieldOffset; |
|
| 1570 | 1467 | ||
| 1571 | 1468 | try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b); |
|
| 1572 | 1469 | } |
|
| 1573 | 1470 | } |
| 1576 | 1473 | fn lowerConstUnionVariantInto( |
|
| 1577 | 1474 | self: *mut Lowerer, |
|
| 1578 | 1475 | node: *ast::Node, |
|
| 1579 | 1476 | variantSym: *mut resolver::Symbol, |
|
| 1580 | 1477 | ty: resolver::Type, |
|
| 1581 | - | payloadArgs: ast::NodeList, |
|
| 1478 | + | payloadArgs: *mut [*ast::Node], |
|
| 1582 | 1479 | dataPrefix: *[u8], |
|
| 1583 | 1480 | b: *mut DataValueBuilder |
|
| 1584 | 1481 | ) throws (LowerError) { |
|
| 1585 | 1482 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data |
|
| 1586 | 1483 | else throw LowerError::UnexpectedNodeValue(node); |
| 1633 | 1530 | }); |
|
| 1634 | 1531 | } |
|
| 1635 | 1532 | } |
|
| 1636 | 1533 | ||
| 1637 | 1534 | /// Add a data item to the lowerer's data list. |
|
| 1535 | + | // XXX: Inline this. |
|
| 1638 | 1536 | fn pushData(self: *mut Lowerer, data: il::Data) throws (LowerError) { |
|
| 1639 | - | if self.dataCount >= self.data.len { |
|
| 1640 | - | self.data = try! alloc::growSlice( |
|
| 1641 | - | self.arena, self.data, self.dataCount, |
|
| 1642 | - | @sizeOf(il::Data), @alignOf(il::Data) |
|
| 1643 | - | ) as *mut [il::Data]; |
|
| 1644 | - | } |
|
| 1645 | - | self.data[self.dataCount] = data; |
|
| 1646 | - | self.dataCount += 1; |
|
| 1537 | + | self.data.append(data, self.allocator); |
|
| 1647 | 1538 | } |
|
| 1648 | 1539 | ||
| 1649 | 1540 | /// Add a function to the lowerer's function list. |
|
| 1541 | + | // XXX: Inline this. |
|
| 1650 | 1542 | fn pushFn(self: *mut Lowerer, f: *il::Fn) throws (LowerError) { |
|
| 1651 | - | if self.fnCount >= self.fns.len { |
|
| 1652 | - | self.fns = try! alloc::growSlice( |
|
| 1653 | - | self.arena, self.fns, self.fnCount, |
|
| 1654 | - | @sizeOf(*il::Fn), @alignOf(*il::Fn) |
|
| 1655 | - | ) as *mut [*il::Fn]; |
|
| 1656 | - | } |
|
| 1657 | - | self.fns[self.fnCount] = f; |
|
| 1658 | - | self.fnCount += 1; |
|
| 1543 | + | self.fns.append(f, self.allocator); |
|
| 1659 | 1544 | } |
|
| 1660 | 1545 | ||
| 1661 | 1546 | /// Find an existing string data entry with matching content. |
|
| 1662 | 1547 | // TODO: Optimize with hash table or remove? |
|
| 1663 | 1548 | fn findStringData(self: *Lowerer, s: *[u8]) -> ?*[u8] { |
|
| 1664 | - | for i in 0..self.dataCount { |
|
| 1665 | - | let data = self.data[i]; |
|
| 1666 | - | if data.values.len == 1 { |
|
| 1667 | - | if let case il::DataItem::Str(existing) = data.values[0].item { |
|
| 1549 | + | for d in self.data { |
|
| 1550 | + | if d.values.len == 1 { |
|
| 1551 | + | if let case il::DataItem::Str(existing) = d.values[0].item { |
|
| 1668 | 1552 | if mem::eq(existing, s) { |
|
| 1669 | - | return data.name; |
|
| 1553 | + | return d.name; |
|
| 1670 | 1554 | } |
|
| 1671 | 1555 | } |
|
| 1672 | 1556 | } |
|
| 1673 | 1557 | } |
|
| 1674 | 1558 | return nil; |
| 1696 | 1580 | alignment: u32, |
|
| 1697 | 1581 | readOnly: bool, |
|
| 1698 | 1582 | values: *[il::DataValue], |
|
| 1699 | 1583 | dataPrefix: *[u8] |
|
| 1700 | 1584 | ) -> *[u8] throws (LowerError) { |
|
| 1701 | - | let name = nextDeclDataName(self, dataPrefix, self.dataCount); |
|
| 1585 | + | let name = nextDeclDataName(self, dataPrefix, self.data.len); |
|
| 1702 | 1586 | try pushData(self, il::Data { name, size, alignment, readOnly, isUndefined: false, values }); |
|
| 1703 | 1587 | ||
| 1704 | 1588 | return name; |
|
| 1705 | 1589 | } |
|
| 1706 | 1590 |
| 1778 | 1662 | } |
|
| 1779 | 1663 | ||
| 1780 | 1664 | /// Find an existing read-only slice data entry with matching values. |
|
| 1781 | 1665 | // TODO: Optimize with hash table or remove? |
|
| 1782 | 1666 | fn findSliceData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1783 | - | for i in 0..self.dataCount { |
|
| 1784 | - | let data = self.data[i]; |
|
| 1785 | - | if data.alignment == alignment and data.readOnly and dataValuesEq(data.values, values) { |
|
| 1786 | - | return data.name; |
|
| 1667 | + | for d in self.data { |
|
| 1668 | + | if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) { |
|
| 1669 | + | return d.name; |
|
| 1787 | 1670 | } |
|
| 1788 | 1671 | } |
|
| 1789 | 1672 | return nil; |
|
| 1790 | 1673 | } |
|
| 1791 | 1674 |
| 1849 | 1732 | let reg = il::Reg { n: self.regCounter }; |
|
| 1850 | 1733 | self.regCounter += 1; |
|
| 1851 | 1734 | return reg; |
|
| 1852 | 1735 | } |
|
| 1853 | 1736 | ||
| 1854 | - | /// Initialize a growable parameter list. |
|
| 1855 | - | fn paramList(self: *mut FnLowerer, cap: u32) -> BlockParams throws (LowerError) { |
|
| 1856 | - | let list = try! alloc::allocSlice(self.low.arena, @sizeOf(il::Param), @alignOf(il::Param), cap) as *mut [il::Param]; |
|
| 1857 | - | return BlockParams { list, len: 0 }; |
|
| 1858 | - | } |
|
| 1859 | - | ||
| 1860 | - | /// Push a parameter onto a parameter list. |
|
| 1861 | - | fn paramListPush(self: *mut FnLowerer, list: *mut BlockParams, param: il::Param) throws (LowerError) { |
|
| 1862 | - | if list.len >= list.list.len { |
|
| 1863 | - | list.list = try! alloc::growSlice( |
|
| 1864 | - | self.low.arena, list.list, list.len, |
|
| 1865 | - | @sizeOf(il::Param), @alignOf(il::Param) |
|
| 1866 | - | ) as *mut [il::Param]; |
|
| 1867 | - | } |
|
| 1868 | - | list.list[list.len] = param; |
|
| 1869 | - | list.len += 1; |
|
| 1870 | - | } |
|
| 1871 | - | ||
| 1872 | 1737 | /// Remove the last block parameter and its associated variable. |
|
| 1873 | 1738 | /// Used when detecting a trivial phi that can be eliminated. |
|
| 1874 | 1739 | fn removeLastBlockParam(self: *mut FnLowerer, block: BlockId) { |
|
| 1875 | 1740 | let blk = getBlockMut(self, block); |
|
| 1876 | 1741 | if blk.params.len > 0 { |
|
| 1877 | - | blk.params.len -= 1; |
|
| 1742 | + | // TODO: Use `pop`? |
|
| 1743 | + | blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap); |
|
| 1878 | 1744 | } |
|
| 1879 | 1745 | if blk.paramVars.len > 0 { |
|
| 1880 | - | blk.paramVars.len -= 1; |
|
| 1746 | + | // TODO: Use `pop`? |
|
| 1747 | + | blk.paramVars = @sliceOf(blk.paramVars.ptr, blk.paramVars.len - 1, blk.paramVars.cap); |
|
| 1881 | 1748 | } |
|
| 1882 | 1749 | } |
|
| 1883 | 1750 | ||
| 1884 | 1751 | /// Rewrite cached SSA values for a variable across all blocks, and also |
|
| 1885 | 1752 | /// rewrite any terminator arguments that reference the provisional register. |
|
| 1886 | 1753 | /// The latter is necessary because recursive SSA resolution may have already |
|
| 1887 | 1754 | /// patched terminator arguments with the provisional value before it was |
|
| 1888 | 1755 | /// found to be trivial. |
|
| 1889 | 1756 | fn rewriteCachedVarValue(self: *mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1890 | - | for i in 0..self.blockCount { |
|
| 1757 | + | for i in 0..self.blockData.len { |
|
| 1891 | 1758 | let blk = getBlockMut(self, BlockId { n: i }); |
|
| 1892 | 1759 | if blk.vars[v.id] == from { |
|
| 1893 | 1760 | blk.vars[v.id] = to; |
|
| 1894 | 1761 | } |
|
| 1895 | 1762 | if blk.instrs.len > 0 { |
|
| 1896 | 1763 | let ix = blk.instrs.len - 1; |
|
| 1897 | - | match &mut blk.instrs.list[ix] { |
|
| 1764 | + | match &mut blk.instrs[ix] { |
|
| 1898 | 1765 | case il::Instr::Jmp { args, .. } => |
|
| 1899 | 1766 | rewriteValInSlice(*args, from, to), |
|
| 1900 | 1767 | case il::Instr::Br { thenArgs, elseArgs, .. } => { |
|
| 1901 | 1768 | rewriteValInSlice(*thenArgs, from, to); |
|
| 1902 | 1769 | rewriteValInSlice(*elseArgs, from, to); |
| 1920 | 1787 | args[i] = to; |
|
| 1921 | 1788 | } |
|
| 1922 | 1789 | } |
|
| 1923 | 1790 | } |
|
| 1924 | 1791 | ||
| 1925 | - | /// Initialize a growable u32 list. |
|
| 1926 | - | fn u32List(self: *mut FnLowerer, cap: u32) -> U32List throws (LowerError) { |
|
| 1927 | - | let list = try! alloc::allocSlice(self.low.arena, @sizeOf(u32), @alignOf(u32), cap) as *mut [u32]; |
|
| 1928 | - | return U32List { list, len: 0 }; |
|
| 1929 | - | } |
|
| 1930 | - | ||
| 1931 | - | /// Push a value onto a u32 list. |
|
| 1932 | - | fn u32ListPush(self: *mut FnLowerer, list: *mut U32List, value: u32) throws (LowerError) { |
|
| 1933 | - | if list.len >= list.list.len { |
|
| 1934 | - | list.list = try! alloc::growSlice( |
|
| 1935 | - | self.low.arena, list.list, list.len, |
|
| 1936 | - | @sizeOf(u32), @alignOf(u32) |
|
| 1937 | - | ) as *mut [u32]; |
|
| 1938 | - | } |
|
| 1939 | - | list.list[list.len] = value; |
|
| 1940 | - | list.len += 1; |
|
| 1941 | - | } |
|
| 1942 | - | ||
| 1943 | 1792 | //////////////////////////// |
|
| 1944 | 1793 | // Basic Block Management // |
|
| 1945 | 1794 | //////////////////////////// |
|
| 1946 | 1795 | ||
| 1947 | 1796 | // Basic blocks are the fundamental unit of the CFG. Each block contains a |
| 1960 | 1809 | /// |
|
| 1961 | 1810 | /// The block is initially unsealed (predecessors may be added later) and empty. |
|
| 1962 | 1811 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 1963 | 1812 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 1964 | 1813 | fn createBlock(self: *mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 1965 | - | if self.blockCount >= self.blockData.len { |
|
| 1966 | - | self.blockData = try! alloc::growSlice( |
|
| 1967 | - | self.low.arena, self.blockData, self.blockCount, |
|
| 1968 | - | @sizeOf(BlockData), @alignOf(BlockData) |
|
| 1969 | - | ) as *mut [BlockData]; |
|
| 1970 | - | } |
|
| 1971 | 1814 | let label = try nextLabel(self, labelBase); |
|
| 1972 | - | let id = BlockId { n: self.blockCount }; |
|
| 1973 | - | let instrs = try! il::instrList(self.low.arena, INITIAL_BLOCK_INSTRS); |
|
| 1974 | - | let params = try paramList(self, 0); |
|
| 1975 | - | let paramVars = try u32List(self, 0); |
|
| 1976 | - | let preds = try u32List(self, INITIAL_PREDS); |
|
| 1977 | - | let vars = try! alloc::allocSlice(self.low.arena, @sizeOf(?il::Val), @alignOf(?il::Val), self.vars.len) as *mut [?il::Val]; |
|
| 1978 | - | ||
| 1979 | - | for i in 0..self.vars.len { |
|
| 1815 | + | let id = BlockId { n: self.blockData.len }; |
|
| 1816 | + | let varCount = self.fnType.localCount; |
|
| 1817 | + | let vars = try! alloc::allocSlice(self.low.arena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val]; |
|
| 1818 | + | ||
| 1819 | + | for i in 0..varCount { |
|
| 1980 | 1820 | vars[i] = nil; |
|
| 1981 | 1821 | } |
|
| 1982 | - | // Allocate source location list if debug info is enabled. |
|
| 1983 | - | let mut locs: *mut [il::SrcLoc] = &mut []; |
|
| 1984 | - | if self.low.options.debug { |
|
| 1985 | - | locs = try! alloc::allocSlice( |
|
| 1986 | - | self.low.arena, @sizeOf(il::SrcLoc), @alignOf(il::SrcLoc), INITIAL_BLOCK_INSTRS |
|
| 1987 | - | ) as *mut [il::SrcLoc]; |
|
| 1988 | - | } |
|
| 1989 | - | self.blockData[self.blockCount] = BlockData { |
|
| 1822 | + | self.blockData.append(BlockData { |
|
| 1990 | 1823 | label, |
|
| 1991 | - | params, |
|
| 1992 | - | paramVars, |
|
| 1993 | - | instrs, |
|
| 1994 | - | locs, |
|
| 1995 | - | locsLen: 0, |
|
| 1996 | - | preds, |
|
| 1824 | + | params: &mut [], |
|
| 1825 | + | paramVars: &mut [], |
|
| 1826 | + | instrs: &mut [], |
|
| 1827 | + | locs: &mut [], |
|
| 1828 | + | preds: &mut [], |
|
| 1997 | 1829 | vars, |
|
| 1998 | - | sealState: Sealed::No { incompleteVars: try u32List(self, 0) }, |
|
| 1830 | + | sealState: Sealed::No { incompleteVars: &mut [] }, |
|
| 1999 | 1831 | loopDepth: self.loopDepth, |
|
| 2000 | - | }; |
|
| 2001 | - | self.blockCount += 1; |
|
| 1832 | + | }, self.allocator); |
|
| 2002 | 1833 | ||
| 2003 | 1834 | return id; |
|
| 2004 | 1835 | } |
|
| 2005 | 1836 | ||
| 2006 | 1837 | /// Create a new block with a single parameter. |
| 2009 | 1840 | labelBase: *[u8], |
|
| 2010 | 1841 | param: il::Param |
|
| 2011 | 1842 | ) -> BlockId throws (LowerError) { |
|
| 2012 | 1843 | let block = try createBlock(self, labelBase); |
|
| 2013 | 1844 | let blk = getBlockMut(self, block); |
|
| 2014 | - | try paramListPush(self, &mut blk.params, param); |
|
| 1845 | + | blk.params.append(param, self.allocator); |
|
| 2015 | 1846 | ||
| 2016 | 1847 | return block; |
|
| 2017 | 1848 | } |
|
| 2018 | 1849 | ||
| 2019 | 1850 | /// Switch to building a different block. |
| 2033 | 1864 | return; // Already sealed. |
|
| 2034 | 1865 | }; |
|
| 2035 | 1866 | blk.sealState = Sealed::Yes; |
|
| 2036 | 1867 | ||
| 2037 | 1868 | // Complete all incomplete block parameters. |
|
| 2038 | - | for i in 0..incompleteVars.len { |
|
| 2039 | - | let v = Var { id: incompleteVars.list[i] }; |
|
| 2040 | - | try resolveBlockArgs(self, block, v); |
|
| 1869 | + | for varId in incompleteVars { |
|
| 1870 | + | try resolveBlockArgs(self, block, Var { id: varId }); |
|
| 2041 | 1871 | } |
|
| 2042 | 1872 | } |
|
| 2043 | 1873 | ||
| 2044 | 1874 | /// Seal a block and switch to it. |
|
| 2045 | 1875 | fn switchToAndSeal(self: *mut FnLowerer, block: BlockId) throws (LowerError) { |
| 2070 | 1900 | ////////////////////////// |
|
| 2071 | 1901 | ||
| 2072 | 1902 | /// Emit an instruction to the current block. |
|
| 2073 | 1903 | fn emit(self: *mut FnLowerer, instr: il::Instr) { |
|
| 2074 | 1904 | let blk = self.currentBlock else panic; |
|
| 2075 | - | // TODO: Add language support for this. |
|
| 2076 | 1905 | let mut block = getBlockMut(self, blk); |
|
| 2077 | - | let instrs = &mut block.instrs; |
|
| 2078 | 1906 | ||
| 2079 | - | // Grow instruction list if needed. |
|
| 2080 | - | if instrs.len >= instrs.list.len { |
|
| 2081 | - | instrs.list = try! alloc::growSlice( |
|
| 2082 | - | self.low.arena, instrs.list, instrs.len, |
|
| 2083 | - | @sizeOf(il::Instr), @alignOf(il::Instr) |
|
| 2084 | - | ) as *mut [il::Instr]; |
|
| 2085 | - | } |
|
| 2086 | 1907 | // Record source location alongside instruction when enabled. |
|
| 2087 | 1908 | if self.low.options.debug { |
|
| 2088 | - | if block.locsLen >= block.locs.len { |
|
| 2089 | - | block.locs = try! alloc::growSlice( |
|
| 2090 | - | self.low.arena, block.locs, block.locsLen, |
|
| 2091 | - | @sizeOf(il::SrcLoc), @alignOf(il::SrcLoc) |
|
| 2092 | - | ) as *mut [il::SrcLoc]; |
|
| 2093 | - | } |
|
| 2094 | - | block.locs[block.locsLen] = self.srcLoc; |
|
| 2095 | - | block.locsLen += 1; |
|
| 1909 | + | block.locs.append(self.srcLoc, self.allocator); |
|
| 2096 | 1910 | } |
|
| 2097 | - | il::instrListPush(instrs, instr); |
|
| 1911 | + | block.instrs.append(instr, self.allocator); |
|
| 2098 | 1912 | } |
|
| 2099 | 1913 | ||
| 2100 | 1914 | /// Emit an unconditional jump to `target`. |
|
| 2101 | 1915 | fn emitJmp(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2102 | 1916 | emit(self, il::Instr::Jmp { target: target.n, args: &mut [] }); |
| 2432 | 2246 | /// causes a jump to the match block. If no patterns match, we jump to the |
|
| 2433 | 2247 | /// fallthrough block. |
|
| 2434 | 2248 | fn emitPatternMatches( |
|
| 2435 | 2249 | self: *mut FnLowerer, |
|
| 2436 | 2250 | subject: *MatchSubject, |
|
| 2437 | - | patterns: ast::NodeList, |
|
| 2251 | + | patterns: *mut [*ast::Node], |
|
| 2438 | 2252 | matchBlock: BlockId, |
|
| 2439 | 2253 | fallthrough: BlockId |
|
| 2440 | 2254 | ) throws (LowerError) { |
|
| 2441 | 2255 | assert patterns.len > 0; |
|
| 2442 | 2256 | ||
| 2443 | 2257 | for i in 0..(patterns.len - 1) { |
|
| 2444 | - | let pattern = patterns.list[i]; |
|
| 2258 | + | let pattern = patterns[i]; |
|
| 2445 | 2259 | let nextArm = try createBlock(self, "arm"); |
|
| 2446 | 2260 | try emitPatternMatch(self, subject, pattern, matchBlock, nextArm); |
|
| 2447 | 2261 | ||
| 2448 | 2262 | // Seal the intermediate arm block: all predecessor edges are known |
|
| 2449 | 2263 | // This ensures SSA construction can resolve variable uses through |
|
| 2450 | 2264 | // single-predecessor optimization instead of creating unresolved block |
|
| 2451 | 2265 | // parameters. |
|
| 2452 | 2266 | try switchToAndSeal(self, nextArm); |
|
| 2453 | 2267 | } |
|
| 2454 | 2268 | // Handle last pattern: go to fallthrough block on failure. |
|
| 2455 | - | let last = patterns.list[patterns.len - 1]; |
|
| 2269 | + | let last = patterns[patterns.len - 1]; |
|
| 2456 | 2270 | try emitPatternMatch(self, subject, last, matchBlock, fallthrough); |
|
| 2457 | 2271 | } |
|
| 2458 | 2272 | ||
| 2459 | 2273 | /// Emit a match binding pattern. |
|
| 2460 | 2274 | /// Binding patterns always match for regular values, but for optionals they |
| 2490 | 2304 | fn blockHasTerminator(self: *FnLowerer) -> bool { |
|
| 2491 | 2305 | let blk = getBlock(self, currentBlock(self)); |
|
| 2492 | 2306 | if blk.instrs.len == 0 { |
|
| 2493 | 2307 | return false; |
|
| 2494 | 2308 | } |
|
| 2495 | - | match blk.instrs.list[blk.instrs.len - 1] { |
|
| 2309 | + | match blk.instrs[blk.instrs.len - 1] { |
|
| 2496 | 2310 | case il::Instr::Ret { .. }, |
|
| 2497 | 2311 | il::Instr::Jmp { .. }, |
|
| 2498 | 2312 | il::Instr::Br { .. }, |
|
| 2499 | 2313 | il::Instr::Switch { .. }, |
|
| 2500 | 2314 | il::Instr::Unreachable => |
| 2540 | 2354 | if blk.sealState == Sealed::Yes { |
|
| 2541 | 2355 | panic "addPredecessor: adding predecessor to sealed block"; |
|
| 2542 | 2356 | } |
|
| 2543 | 2357 | let preds = &mut blk.preds; |
|
| 2544 | 2358 | for i in 0..preds.len { |
|
| 2545 | - | if preds.list[i] == pred.n { // Avoid duplicate predecessor entries. |
|
| 2359 | + | if preds[i] == pred.n { // Avoid duplicate predecessor entries. |
|
| 2546 | 2360 | return; |
|
| 2547 | 2361 | } |
|
| 2548 | 2362 | } |
|
| 2549 | - | try u32ListPush(self, preds, pred.n); |
|
| 2363 | + | preds.append(pred.n, self.allocator); |
|
| 2550 | 2364 | } |
|
| 2551 | 2365 | ||
| 2552 | 2366 | /// Finalize all blocks and return the block array. |
|
| 2553 | 2367 | fn finalizeBlocks(self: *mut FnLowerer) -> *[il::Block] throws (LowerError) { |
|
| 2554 | 2368 | let blocks = try! alloc::allocSlice( |
|
| 2555 | - | self.low.arena, @sizeOf(il::Block), @alignOf(il::Block), self.blockCount |
|
| 2369 | + | self.low.arena, @sizeOf(il::Block), @alignOf(il::Block), self.blockData.len |
|
| 2556 | 2370 | ) as *mut [il::Block]; |
|
| 2557 | - | let mut count: u32 = 0; |
|
| 2558 | 2371 | ||
| 2559 | - | for i in 0..self.blockCount { |
|
| 2372 | + | for i in 0..self.blockData.len { |
|
| 2560 | 2373 | let data = &self.blockData[i]; |
|
| 2561 | 2374 | ||
| 2562 | - | let params = &data.params.list[..data.params.len]; |
|
| 2563 | - | let preds = &data.preds.list[..data.preds.len]; |
|
| 2564 | - | blocks[count] = il::Block { |
|
| 2375 | + | blocks[i] = il::Block { |
|
| 2565 | 2376 | label: data.label, |
|
| 2566 | - | params, |
|
| 2377 | + | params: &data.params[..], |
|
| 2567 | 2378 | instrs: data.instrs, |
|
| 2568 | - | locs: &data.locs[..data.locsLen], |
|
| 2569 | - | preds, |
|
| 2379 | + | locs: &data.locs[..], |
|
| 2380 | + | preds: &data.preds[..], |
|
| 2570 | 2381 | loopDepth: data.loopDepth, |
|
| 2571 | 2382 | }; |
|
| 2572 | - | count += 1; |
|
| 2573 | 2383 | } |
|
| 2574 | - | return &blocks[..count]; |
|
| 2384 | + | return &blocks[..self.blockData.len]; |
|
| 2575 | 2385 | } |
|
| 2576 | 2386 | ||
| 2577 | 2387 | ///////////////////// |
|
| 2578 | 2388 | // Loop Management // |
|
| 2579 | 2389 | ///////////////////// |
| 2727 | 2537 | name: ?*[u8], |
|
| 2728 | 2538 | type: il::Type, |
|
| 2729 | 2539 | mutable: bool, |
|
| 2730 | 2540 | val: il::Val |
|
| 2731 | 2541 | ) -> Var { |
|
| 2732 | - | if self.varsLen >= self.vars.len { |
|
| 2733 | - | panic "newVar: out of capacity"; |
|
| 2734 | - | } |
|
| 2735 | - | let id = self.varsLen; |
|
| 2736 | - | self.vars[id] = VarData { name, type, mutable, addressTaken: false }; |
|
| 2737 | - | self.varsLen += 1; |
|
| 2542 | + | let id = self.vars.len; |
|
| 2543 | + | self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator); |
|
| 2738 | 2544 | ||
| 2739 | 2545 | let v = Var { id }; |
|
| 2740 | 2546 | if self.currentBlock != nil { |
|
| 2741 | 2547 | defVar(self, v, val); |
|
| 2742 | 2548 | } |
| 2746 | 2552 | /// Define (write) a variable. Record the SSA value of a variable in the |
|
| 2747 | 2553 | /// current block. Called when a variable is assigned or initialized (`let` |
|
| 2748 | 2554 | /// bindings, assignments, loop updates). When [`useVar`] is later called, |
|
| 2749 | 2555 | /// it will retrieve this value. |
|
| 2750 | 2556 | fn defVar(self: *mut FnLowerer, v: Var, val: il::Val) { |
|
| 2751 | - | assert v.id < self.varsLen; |
|
| 2557 | + | assert v.id < self.vars.len; |
|
| 2752 | 2558 | getBlockMut(self, currentBlock(self)).vars[v.id] = val; |
|
| 2753 | 2559 | } |
|
| 2754 | 2560 | ||
| 2755 | 2561 | /// Use (read) the current value of a variable in the current block. |
|
| 2756 | 2562 | /// May insert block parameters if the value must come from predecessors. |
| 2763 | 2569 | /// Given a variable and a block where it's used, this function finds the |
|
| 2764 | 2570 | /// correct [`il::Val`] that holds the variable's value at that program point. |
|
| 2765 | 2571 | /// When control flow merges from multiple predecessors with different |
|
| 2766 | 2572 | /// definitions, it creates a block parameter to unify them. |
|
| 2767 | 2573 | fn useVarInBlock(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2768 | - | assert v.id < self.varsLen; |
|
| 2574 | + | assert v.id < self.vars.len; |
|
| 2769 | 2575 | ||
| 2770 | 2576 | let blk = getBlockMut(self, block); |
|
| 2771 | 2577 | if let val = blk.vars[v.id] { |
|
| 2772 | 2578 | return val; |
|
| 2773 | 2579 | } |
| 2782 | 2588 | throw LowerError::InvalidUse; |
|
| 2783 | 2589 | } |
|
| 2784 | 2590 | // Single predecessor means no merge needed, variable is implicitly |
|
| 2785 | 2591 | // available without a block parameter. |
|
| 2786 | 2592 | if blk.preds.len == 1 { |
|
| 2787 | - | let pred = BlockId { n: blk.preds.list[0] }; |
|
| 2593 | + | let pred = BlockId { n: blk.preds[0] }; |
|
| 2788 | 2594 | if pred.n != block.n { |
|
| 2789 | 2595 | let val = try useVarInBlock(self, pred, v); |
|
| 2790 | 2596 | blk.vars[v.id] = val; // Cache. |
|
| 2791 | 2597 | return val; |
|
| 2792 | 2598 | } |
| 2798 | 2604 | } |
|
| 2799 | 2605 | ||
| 2800 | 2606 | /// Look up a variable by name in the current scope. |
|
| 2801 | 2607 | /// Searches from most recently declared to first, enabling shadowing. |
|
| 2802 | 2608 | fn lookupVarByName(self: *FnLowerer, name: *[u8]) -> ?Var { |
|
| 2803 | - | let mut id = self.varsLen; |
|
| 2609 | + | let mut id = self.vars.len; |
|
| 2804 | 2610 | while id > 0 { |
|
| 2805 | 2611 | id -= 1; |
|
| 2806 | 2612 | if let varName = self.vars[id].name { |
|
| 2807 | 2613 | if mem::eq(varName, name) { |
|
| 2808 | 2614 | return Var { id }; |
| 2820 | 2626 | return lookupVarByName(self, name); |
|
| 2821 | 2627 | } |
|
| 2822 | 2628 | ||
| 2823 | 2629 | /// Save current lexical variable scope depth. |
|
| 2824 | 2630 | fn enterVarScope(self: *FnLowerer) -> u32 { |
|
| 2825 | - | return self.varsLen; |
|
| 2631 | + | return self.vars.len; |
|
| 2826 | 2632 | } |
|
| 2827 | 2633 | ||
| 2828 | 2634 | /// Restore lexical variable scope depth. |
|
| 2829 | 2635 | fn exitVarScope(self: *mut FnLowerer, savedVarsLen: u32) { |
|
| 2830 | - | self.varsLen = savedVarsLen; |
|
| 2636 | + | self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2831 | 2637 | } |
|
| 2832 | 2638 | ||
| 2833 | 2639 | /// Get the metadata for a variable. |
|
| 2834 | 2640 | fn getVar(self: *FnLowerer, v: Var) -> *VarData { |
|
| 2835 | - | assert v.id < self.varsLen; |
|
| 2641 | + | assert v.id < self.vars.len; |
|
| 2836 | 2642 | return &self.vars[v.id]; |
|
| 2837 | 2643 | } |
|
| 2838 | 2644 | ||
| 2839 | 2645 | /// Create a block parameter to merge a variable's value from multiple |
|
| 2840 | 2646 | /// control flow paths. |
| 2862 | 2668 | let type = getVar(self, v).type; |
|
| 2863 | 2669 | ||
| 2864 | 2670 | // Create block parameter and add it to the block. |
|
| 2865 | 2671 | let param = il::Param { value: reg, type }; |
|
| 2866 | 2672 | let blk = getBlockMut(self, block); |
|
| 2867 | - | try paramListPush(self, &mut blk.params, param); |
|
| 2868 | - | try u32ListPush(self, &mut blk.paramVars, v.id); // Associate variable with parameter. |
|
| 2673 | + | blk.params.append(param, self.allocator); |
|
| 2674 | + | blk.paramVars.append(v.id, self.allocator); // Associate variable with parameter. |
|
| 2869 | 2675 | ||
| 2870 | 2676 | // Record that this variable's value in this block is now the parameter register. |
|
| 2871 | 2677 | // This must happen before the predecessor loop to handle self-referential loops. |
|
| 2872 | 2678 | blk.vars[v.id] = il::Val::Reg(reg); |
|
| 2873 | 2679 | ||
| 2874 | 2680 | match &mut blk.sealState { |
|
| 2875 | 2681 | case Sealed::No { incompleteVars } => { |
|
| 2876 | 2682 | // Block unsealed: defer until sealing. |
|
| 2877 | - | try u32ListPush(self, incompleteVars, v.id); |
|
| 2683 | + | incompleteVars.append(v.id, self.allocator); |
|
| 2878 | 2684 | }, |
|
| 2879 | 2685 | case Sealed::Yes => { |
|
| 2880 | 2686 | // Block sealed: check for trivial phi before committing. If all |
|
| 2881 | 2687 | // predecessors provide the same value, we can remove the param we |
|
| 2882 | 2688 | // just created and use that value directly. |
| 2910 | 2716 | ||
| 2911 | 2717 | // Find the parameter index corresponding to this variable. |
|
| 2912 | 2718 | // Each variable that needs merging gets its own block parameter slot. |
|
| 2913 | 2719 | let mut paramIdx: u32 = 0; |
|
| 2914 | 2720 | for i in 0..blk.paramVars.len { |
|
| 2915 | - | if blk.paramVars.list[i] == v.id { |
|
| 2721 | + | if blk.paramVars[i] == v.id { |
|
| 2916 | 2722 | paramIdx = i; |
|
| 2917 | 2723 | break; |
|
| 2918 | 2724 | } |
|
| 2919 | 2725 | } |
|
| 2920 | 2726 | ||
| 2921 | 2727 | // For each predecessor, recursively look up the variable's reaching definition |
|
| 2922 | 2728 | // in that block, then patch the predecessor's terminator to pass that value |
|
| 2923 | 2729 | // as an argument to this block's parameter. |
|
| 2924 | - | for i in 0..blk.preds.len { |
|
| 2925 | - | let pred = BlockId { n: blk.preds.list[i] }; |
|
| 2730 | + | for predId in blk.preds { |
|
| 2731 | + | let pred = BlockId { n: predId }; |
|
| 2926 | 2732 | // This may recursively trigger more block arg resolution if the |
|
| 2927 | 2733 | // predecessor also needs to look up the variable from its predecessors. |
|
| 2928 | 2734 | let val = try useVarInBlock(self, pred, v); |
|
| 2929 | 2735 | if val == il::Val::Undef { |
|
| 2930 | 2736 | panic "createBlockParam: predecessor provides undef value for block parameter"; |
| 2940 | 2746 | // Get the block parameter register. |
|
| 2941 | 2747 | let paramReg = blk.vars[v.id]; |
|
| 2942 | 2748 | // Check if all predecessors provide the same value. |
|
| 2943 | 2749 | let mut sameVal: ?il::Val = nil; |
|
| 2944 | 2750 | ||
| 2945 | - | for i in 0..blk.preds.len { |
|
| 2946 | - | let pred = BlockId { n: blk.preds.list[i] }; |
|
| 2751 | + | for predId in blk.preds { |
|
| 2752 | + | let pred = BlockId { n: predId }; |
|
| 2947 | 2753 | let val = try useVarInBlock(self, pred, v); |
|
| 2948 | 2754 | ||
| 2949 | 2755 | // Check if this is a self-reference. |
|
| 2950 | 2756 | // This happens in cycles where the loop back-edge passes the phi to |
|
| 2951 | 2757 | // itself. We skip self-references when checking for trivial phis. |
| 2991 | 2797 | let data = getBlockMut(self, from); |
|
| 2992 | 2798 | let ix = data.instrs.len - 1; // The terminator is always the last instruction. |
|
| 2993 | 2799 | ||
| 2994 | 2800 | // TODO: We shouldn't need to use a mutable subscript here, given that the |
|
| 2995 | 2801 | // fields are already mutable. |
|
| 2996 | - | match &mut data.instrs.list[ix] { |
|
| 2802 | + | match &mut data.instrs[ix] { |
|
| 2997 | 2803 | case il::Instr::Jmp { args, .. } => { |
|
| 2998 | 2804 | *args = growArgs(self, *args, paramIdx + 1); |
|
| 2999 | 2805 | args[paramIdx] = val; |
|
| 3000 | 2806 | } |
|
| 3001 | 2807 | case il::Instr::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => { |
| 3054 | 2860 | /// Lower function parameters. Declares variables for each parameter. |
|
| 3055 | 2861 | /// When a receiver name is passed, we're handling a trait method. |
|
| 3056 | 2862 | fn lowerParams( |
|
| 3057 | 2863 | self: *mut FnLowerer, |
|
| 3058 | 2864 | fnType: resolver::FnType, |
|
| 3059 | - | astParams: ast::NodeList, |
|
| 2865 | + | astParams: *mut [*ast::Node], |
|
| 3060 | 2866 | receiverName: ?*ast::Node |
|
| 3061 | 2867 | ) -> *[il::Param] throws (LowerError) { |
|
| 3062 | 2868 | let offset: u32 = 1 if self.returnReg != nil else 0; |
|
| 3063 | - | let totalLen = fnType.paramTypesLen + offset; |
|
| 2869 | + | let totalLen = fnType.paramTypes.len as u32 + offset; |
|
| 3064 | 2870 | if totalLen == 0 { |
|
| 3065 | 2871 | return &[]; |
|
| 3066 | 2872 | } |
|
| 3067 | - | assert fnType.paramTypesLen <= resolver::MAX_FN_PARAMS; |
|
| 2873 | + | assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS; |
|
| 3068 | 2874 | ||
| 3069 | 2875 | let params = try! alloc::allocSlice( |
|
| 3070 | 2876 | self.low.arena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3071 | 2877 | ) as *mut [il::Param]; |
|
| 3072 | 2878 | ||
| 3073 | 2879 | if let reg = self.returnReg { |
|
| 3074 | 2880 | params[0] = il::Param { value: reg, type: il::Type::W64 }; |
|
| 3075 | 2881 | } |
|
| 3076 | - | for i in 0..fnType.paramTypesLen { |
|
| 2882 | + | for i in 0..fnType.paramTypes.len as u32 { |
|
| 3077 | 2883 | let type = ilType(self.low, *fnType.paramTypes[i]); |
|
| 3078 | 2884 | let reg = nextReg(self); |
|
| 3079 | 2885 | ||
| 3080 | 2886 | params[i + offset] = il::Param { value: reg, type }; |
|
| 3081 | 2887 |
| 3088 | 2894 | let case ast::NodeValue::Ident(recName) = recNode.value else { |
|
| 3089 | 2895 | throw LowerError::ExpectedIdentifier; |
|
| 3090 | 2896 | }; |
|
| 3091 | 2897 | name = recName; |
|
| 3092 | 2898 | } else { |
|
| 3093 | - | name = try paramName(&astParams.list[i - 1].value); |
|
| 2899 | + | name = try paramName(&astParams[i - 1].value); |
|
| 3094 | 2900 | } |
|
| 3095 | 2901 | } else { |
|
| 3096 | - | name = try paramName(&astParams.list[i].value); |
|
| 2902 | + | name = try paramName(&astParams[i].value); |
|
| 3097 | 2903 | } |
|
| 3098 | 2904 | let v = newVar(self, name, type, false, il::Val::Undef); |
|
| 3099 | 2905 | ||
| 3100 | - | self.params[self.paramsLen] = FnParamBinding { var: v, reg }; |
|
| 3101 | - | self.paramsLen += 1; |
|
| 2906 | + | self.params.append(FnParamBinding { var: v, reg }, self.allocator); |
|
| 3102 | 2907 | } |
|
| 3103 | 2908 | return params; |
|
| 3104 | 2909 | } |
|
| 3105 | 2910 | ||
| 3106 | 2911 | /// Resolve match subject. |
| 3249 | 3054 | // Declare the variable in the current block's scope. |
|
| 3250 | 3055 | return newVar(self, name, ilType(self.low, subject.bindType), false, subject.val); |
|
| 3251 | 3056 | } |
|
| 3252 | 3057 | ||
| 3253 | 3058 | /// Bind variables from inside case patterns (union variants, records, slices). |
|
| 3254 | - | fn bindPatternVariables(self: *mut FnLowerer, subject: *MatchSubject, patterns: ast::NodeList) throws (LowerError) { |
|
| 3255 | - | for i in 0..patterns.len { |
|
| 3256 | - | let pattern = patterns.list[i]; |
|
| 3059 | + | fn bindPatternVariables(self: *mut FnLowerer, subject: *MatchSubject, patterns: *mut [*ast::Node]) throws (LowerError) { |
|
| 3060 | + | for pattern in patterns { |
|
| 3257 | 3061 | ||
| 3258 | 3062 | // Handle simple variant patterns like `Variant(x)`. |
|
| 3259 | 3063 | // TODO: Why is this handled differently from the cases below? |
|
| 3260 | 3064 | if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) { |
|
| 3261 | 3065 | if let case ast::NodeValue::Ident(name) = arg.value { |
| 3307 | 3111 | let base = try emitValToReg(self, subject.val); |
|
| 3308 | 3112 | let valOffset = unionInfo.valOffset as i32; |
|
| 3309 | 3113 | let payloadBase = emitPtrOffset(self, base, valOffset); |
|
| 3310 | 3114 | ||
| 3311 | 3115 | // Bind each field. |
|
| 3312 | - | for i in 0..lit.fields.len { |
|
| 3313 | - | let fieldNode = lit.fields.list[i]; |
|
| 3116 | + | for fieldNode in lit.fields { |
|
| 3314 | 3117 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 3315 | 3118 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 3316 | 3119 | }; |
|
| 3317 | 3120 | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) |
|
| 3318 | 3121 | else throw LowerError::MissingMetadata; |
|
| 3319 | - | if fieldIdx >= recInfo.fieldsLen { |
|
| 3122 | + | if fieldIdx >= recInfo.fields.len { |
|
| 3320 | 3123 | throw LowerError::MissingMetadata; |
|
| 3321 | 3124 | } |
|
| 3322 | 3125 | let fieldInfo = recInfo.fields[fieldIdx]; |
|
| 3323 | 3126 | ||
| 3324 | 3127 | try bindFieldVariable(self, field.value, payloadBase, fieldInfo, subject.by); |
| 3354 | 3157 | let entry = try createBlock(self, "entry"); |
|
| 3355 | 3158 | self.entryBlock = entry; |
|
| 3356 | 3159 | switchToBlock(self, entry); |
|
| 3357 | 3160 | ||
| 3358 | 3161 | /// Bind parameter registers to variables in the entry block. |
|
| 3359 | - | for i in 0..self.paramsLen { |
|
| 3360 | - | let def = self.params[i]; |
|
| 3162 | + | for def in self.params { |
|
| 3361 | 3163 | defVar(self, def.var, il::Val::Reg(def.reg)); |
|
| 3362 | 3164 | } |
|
| 3363 | 3165 | /// Lower function body. |
|
| 3364 | 3166 | try lowerBlock(self, body); |
|
| 3365 | 3167 | ||
| 3366 | 3168 | // Add implicit return if body doesn't diverge. |
|
| 3367 | 3169 | if not blockHasTerminator(self) { |
|
| 3368 | - | if self.fnType.throwListLen > 0 { |
|
| 3170 | + | if self.fnType.throwList.len > 0 { |
|
| 3369 | 3171 | if *self.fnType.returnType == resolver::Type::Void { |
|
| 3370 | 3172 | // Implicit `void` return in throwing function: wrap in result success. |
|
| 3371 | 3173 | let val = try buildResult(self, 0, nil, resolver::Type::Void); |
|
| 3372 | 3174 | try emitRetVal(self, val); |
|
| 3373 | 3175 | } else { |
| 3381 | 3183 | } |
|
| 3382 | 3184 | return try finalizeBlocks(self); |
|
| 3383 | 3185 | } |
|
| 3384 | 3186 | ||
| 3385 | 3187 | /// Lower a scalar match as a switch instruction. |
|
| 3386 | - | fn lowerMatchSwitch(self: *mut FnLowerer, prongs: *ast::NodeList, subject: *MatchSubject, mergeBlock: *mut ?BlockId) throws (LowerError) { |
|
| 3188 | + | fn lowerMatchSwitch(self: *mut FnLowerer, prongs: *mut [*ast::Node], subject: *MatchSubject, mergeBlock: *mut ?BlockId) throws (LowerError) { |
|
| 3387 | 3189 | let mut blocks: [BlockId; 32] = undefined; |
|
| 3388 | 3190 | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 3389 | - | let mut caseIdx: u32 = 0; |
|
| 3390 | 3191 | let mut defaultIdx: u32 = 0; |
|
| 3391 | 3192 | let entry = currentBlock(self); |
|
| 3392 | 3193 | ||
| 3393 | 3194 | for i in 0..prongs.len { |
|
| 3394 | - | let p = prongs.list[i]; |
|
| 3195 | + | let p = prongs[i]; |
|
| 3395 | 3196 | let case ast::NodeValue::MatchProng(prong) = p.value |
|
| 3396 | 3197 | else throw LowerError::UnexpectedNodeValue(p); |
|
| 3397 | 3198 | ||
| 3398 | 3199 | match prong.arm { |
|
| 3399 | 3200 | case ast::ProngArm::Binding(_), ast::ProngArm::Else => { |
|
| 3400 | 3201 | blocks[i] = try createBlock(self, "default"); |
|
| 3401 | 3202 | defaultIdx = i; |
|
| 3402 | 3203 | } |
|
| 3403 | 3204 | case ast::ProngArm::Case(pats) => { |
|
| 3404 | 3205 | blocks[i] = try createBlock(self, "case"); |
|
| 3405 | - | for j in 0..pats.len { |
|
| 3406 | - | if caseIdx >= cases.len { |
|
| 3407 | - | cases = try! alloc::growSlice( |
|
| 3408 | - | self.low.arena, |
|
| 3409 | - | cases as *mut [opaque], |
|
| 3410 | - | caseIdx, |
|
| 3411 | - | @sizeOf(il::SwitchCase), |
|
| 3412 | - | @alignOf(il::SwitchCase) |
|
| 3413 | - | ) as *mut [il::SwitchCase]; |
|
| 3414 | - | } |
|
| 3415 | - | let cv = resolver::constValueFor(self.low.resolver, pats.list[j]) |
|
| 3416 | - | else throw LowerError::MissingConst(pats.list[j]); |
|
| 3206 | + | for pat in pats { |
|
| 3207 | + | let cv = resolver::constValueFor(self.low.resolver, pat) |
|
| 3208 | + | else throw LowerError::MissingConst(pat); |
|
| 3417 | 3209 | ||
| 3418 | - | cases[caseIdx] = il::SwitchCase { |
|
| 3210 | + | cases.append(il::SwitchCase { |
|
| 3419 | 3211 | value: constToScalar(cv), |
|
| 3420 | 3212 | target: blocks[i].n, |
|
| 3421 | 3213 | args: &mut [] |
|
| 3422 | - | }; |
|
| 3423 | - | caseIdx += 1; |
|
| 3214 | + | }, self.allocator); |
|
| 3424 | 3215 | } |
|
| 3425 | 3216 | } |
|
| 3426 | 3217 | } |
|
| 3427 | 3218 | try addPredecessor(self, blocks[i], entry); |
|
| 3428 | 3219 | } |
|
| 3429 | 3220 | emit(self, il::Instr::Switch { |
|
| 3430 | 3221 | val: subject.val, |
|
| 3431 | 3222 | defaultTarget: blocks[defaultIdx].n, |
|
| 3432 | 3223 | defaultArgs: &mut [], |
|
| 3433 | - | cases: &mut cases[..caseIdx] |
|
| 3224 | + | cases: &mut cases[..] |
|
| 3434 | 3225 | }); |
|
| 3435 | 3226 | ||
| 3436 | 3227 | for i in 0..prongs.len { |
|
| 3437 | - | let p = prongs.list[i]; |
|
| 3228 | + | let p = prongs[i]; |
|
| 3438 | 3229 | let case ast::NodeValue::MatchProng(prong) = p.value |
|
| 3439 | 3230 | else throw LowerError::UnexpectedNodeValue(p); |
|
| 3440 | 3231 | ||
| 3441 | 3232 | try switchToAndSeal(self, blocks[i]); |
|
| 3442 | 3233 | try lowerNode(self, prong.body); |
| 3500 | 3291 | /// ret 0; // `else` body |
|
| 3501 | 3292 | /// |
|
| 3502 | 3293 | fn lowerMatch(self: *mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) { |
|
| 3503 | 3294 | assert m.prongs.len > 0; |
|
| 3504 | 3295 | ||
| 3505 | - | let prongs = &m.prongs; |
|
| 3296 | + | let prongs = m.prongs; |
|
| 3506 | 3297 | // Lower the subject expression once; reused across all arms. |
|
| 3507 | 3298 | let subject = try lowerMatchSubject(self, m.subject); |
|
| 3508 | 3299 | // Merge block created lazily if any arm needs it (i.e., doesn't diverge). |
|
| 3509 | 3300 | let mut mergeBlock: ?BlockId = nil; |
|
| 3510 | 3301 |
| 3518 | 3309 | try emitJmp(self, firstArm); |
|
| 3519 | 3310 | try switchToAndSeal(self, firstArm); |
|
| 3520 | 3311 | ||
| 3521 | 3312 | for i in 0..prongs.len { |
|
| 3522 | 3313 | let prongScope = enterVarScope(self); |
|
| 3523 | - | let prongNode = prongs.list[i]; |
|
| 3314 | + | let prongNode = prongs[i]; |
|
| 3524 | 3315 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 3525 | 3316 | else panic "lowerMatch: expected match prong"; |
|
| 3526 | 3317 | ||
| 3527 | 3318 | let isLastArm = i + 1 == prongs.len; |
|
| 3528 | 3319 | let hasGuard = prong.guard != nil; |
| 3650 | 3441 | } else { |
|
| 3651 | 3442 | targetBlock = *successBlock; |
|
| 3652 | 3443 | } |
|
| 3653 | 3444 | match pat.kind { |
|
| 3654 | 3445 | case ast::PatternKind::Case => { |
|
| 3655 | - | let patterns = ast::NodeList { list: &mut [pat.pattern], len: 1 }; |
|
| 3446 | + | let patterns: *mut [*ast::Node] = &mut [pat.pattern]; |
|
| 3656 | 3447 | // Jump to `targetBlock` if the pattern matches, `failBlock` otherwise. |
|
| 3657 | 3448 | try emitPatternMatches(self, subject, patterns, targetBlock, failBlock); |
|
| 3658 | 3449 | try switchToAndSeal(self, targetBlock); |
|
| 3659 | 3450 | // Bind any variables inside the pattern. |
|
| 3660 | 3451 | try bindPatternVariables(self, subject, patterns); |
| 3819 | 3610 | fn lowerBlock(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 3820 | 3611 | let case ast::NodeValue::Block(blk) = node.value else { |
|
| 3821 | 3612 | throw LowerError::ExpectedBlock(node); |
|
| 3822 | 3613 | }; |
|
| 3823 | 3614 | let savedVarsLen = enterVarScope(self); |
|
| 3824 | - | for i in 0..blk.statements.len { |
|
| 3825 | - | let stmt = blk.statements.list[i]; |
|
| 3615 | + | for stmt in blk.statements { |
|
| 3826 | 3616 | try lowerNode(self, stmt); |
|
| 3827 | 3617 | ||
| 3828 | 3618 | // If the statement diverges, further statements are unreachable. |
|
| 3829 | 3619 | if blockHasTerminator(self) { |
|
| 3830 | 3620 | exitVarScope(self, savedVarsLen); |
| 3920 | 3710 | /// |
|
| 3921 | 3711 | /// This is the case for throwing functions, which return a result aggregate, |
|
| 3922 | 3712 | /// and for functions returning large aggregates that cannot be passed in |
|
| 3923 | 3713 | /// registers. |
|
| 3924 | 3714 | fn requiresReturnParam(fnType: *resolver::FnType) -> bool { |
|
| 3925 | - | return fnType.throwListLen > 0 |
|
| 3715 | + | return fnType.throwList.len > 0 |
|
| 3926 | 3716 | or (isAggregateType(*fnType.returnType) |
|
| 3927 | 3717 | and not isSmallAggregate(*fnType.returnType)); |
|
| 3928 | 3718 | } |
|
| 3929 | 3719 | ||
| 3930 | 3720 | /// Check if a node is a void union variant literal (e.g. `Color::Red`). |
| 4082 | 3872 | payload: ?il::Val, |
|
| 4083 | 3873 | payloadType: resolver::Type |
|
| 4084 | 3874 | ) -> il::Val throws (LowerError) { |
|
| 4085 | 3875 | let successType = *self.fnType.returnType; |
|
| 4086 | 3876 | let layout = resolver::getResultLayout( |
|
| 4087 | - | successType, &self.fnType.throwList[..self.fnType.throwListLen] |
|
| 3877 | + | successType, self.fnType.throwList |
|
| 4088 | 3878 | ); |
|
| 4089 | 3879 | return try buildTagged(self, layout, tag, payload, payloadType, 8, RESULT_VAL_OFFSET); |
|
| 4090 | 3880 | } |
|
| 4091 | 3881 | ||
| 4092 | 3882 | /// Build a slice aggregate from a data pointer, length and capacity. |
| 4252 | 4042 | b: il::Reg, |
|
| 4253 | 4043 | offset: i32 |
|
| 4254 | 4044 | ) -> il::Val throws (LowerError) { |
|
| 4255 | 4045 | let mut result: ?il::Val = nil; |
|
| 4256 | 4046 | ||
| 4257 | - | for i in 0..recInfo.fieldsLen { |
|
| 4258 | - | let field = recInfo.fields[i]; |
|
| 4047 | + | for field in recInfo.fields { |
|
| 4259 | 4048 | let cmp = try emitEqAtOffset(self, a, b, offset + field.offset, field.fieldType); |
|
| 4260 | 4049 | ||
| 4261 | 4050 | result = emitLogicalAnd(self, result, cmp); |
|
| 4262 | 4051 | } |
|
| 4263 | 4052 | if let r = result { |
| 4434 | 4223 | ||
| 4435 | 4224 | // Create comparison blocks for each non-void variant and build switch cases. |
|
| 4436 | 4225 | // Void variants jump directly to merge with `true`. |
|
| 4437 | 4226 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4438 | 4227 | let cases = try! alloc::allocSlice( |
|
| 4439 | - | self.low.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variantsLen |
|
| 4228 | + | self.low.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4440 | 4229 | ) as *mut [il::SwitchCase]; |
|
| 4441 | 4230 | ||
| 4442 | 4231 | let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined; |
|
| 4443 | - | for i in 0..unionInfo.variantsLen { |
|
| 4232 | + | for i in 0..unionInfo.variants.len { |
|
| 4444 | 4233 | if unionInfo.variants[i].valueType == resolver::Type::Void { |
|
| 4445 | 4234 | cases[i] = il::SwitchCase { |
|
| 4446 | 4235 | value: i as i64, |
|
| 4447 | 4236 | target: mergeBlock.n, |
|
| 4448 | 4237 | args: trueArgs |
| 4469 | 4258 | cases |
|
| 4470 | 4259 | }); |
|
| 4471 | 4260 | ||
| 4472 | 4261 | // Add predecessor edges for switch targets. |
|
| 4473 | 4262 | try addPredecessor(self, unreachableBlock, tagBlock); |
|
| 4474 | - | for i in 0..unionInfo.variantsLen { |
|
| 4263 | + | for i in 0..unionInfo.variants.len { |
|
| 4475 | 4264 | if let caseBlock = caseBlocks[i] { |
|
| 4476 | 4265 | try addPredecessor(self, caseBlock, tagBlock); |
|
| 4477 | 4266 | } else { |
|
| 4478 | 4267 | try addPredecessor(self, mergeBlock, tagBlock); |
|
| 4479 | 4268 | } |
|
| 4480 | 4269 | } |
|
| 4481 | 4270 | let valOffset = unionInfo.valOffset as i32; |
|
| 4482 | 4271 | ||
| 4483 | 4272 | // Emit payload comparison blocks for non-void variants. |
|
| 4484 | - | for i in 0..unionInfo.variantsLen { |
|
| 4273 | + | for i in 0..unionInfo.variants.len { |
|
| 4485 | 4274 | if let caseBlock = caseBlocks[i] { |
|
| 4486 | 4275 | try switchToAndSeal(self, caseBlock); |
|
| 4487 | 4276 | let payloadEq = try emitEqAtOffset( |
|
| 4488 | 4277 | self, a, b, offset + valOffset, unionInfo.variants[i].valueType |
|
| 4489 | 4278 | ); |
| 4615 | 4404 | /// The `offset` is added to each field's offset when storing. |
|
| 4616 | 4405 | fn lowerRecordFields( |
|
| 4617 | 4406 | self: *mut FnLowerer, |
|
| 4618 | 4407 | dst: il::Reg, |
|
| 4619 | 4408 | recInfo: *resolver::RecordType, |
|
| 4620 | - | fields: ast::NodeList, |
|
| 4409 | + | fields: *mut [*ast::Node], |
|
| 4621 | 4410 | offset: i32 |
|
| 4622 | 4411 | ) throws (LowerError) { |
|
| 4623 | 4412 | for i in 0..fields.len { |
|
| 4624 | - | let fieldNode = fields.list[i]; |
|
| 4413 | + | let fieldNode = fields[i]; |
|
| 4625 | 4414 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 4626 | 4415 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 4627 | 4416 | }; |
|
| 4628 | 4417 | let mut fieldIdx: u32 = i; |
|
| 4629 | 4418 | if recInfo.labeled { |
| 4642 | 4431 | } |
|
| 4643 | 4432 | } |
|
| 4644 | 4433 | } |
|
| 4645 | 4434 | ||
| 4646 | 4435 | /// Lower an unlabeled record constructor call. |
|
| 4647 | - | fn lowerRecordCtor(self: *mut FnLowerer, nominal: *resolver::NominalType, args: ast::NodeList) -> il::Val throws (LowerError) { |
|
| 4436 | + | fn lowerRecordCtor(self: *mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4648 | 4437 | let case resolver::NominalType::Record(recInfo) = *nominal else { |
|
| 4649 | 4438 | throw LowerError::ExpectedRecord; |
|
| 4650 | 4439 | }; |
|
| 4651 | 4440 | let typ = resolver::Type::Nominal(nominal); |
|
| 4652 | 4441 | let dst = try emitReserve(self, typ); |
|
| 4653 | 4442 | ||
| 4654 | 4443 | for i in 0..args.len { |
|
| 4655 | - | let argNode = args.list[i]; |
|
| 4444 | + | let argNode = args[i]; |
|
| 4656 | 4445 | // Skip `undefined` arguments. |
|
| 4657 | 4446 | if not isUndef(argNode) { |
|
| 4658 | 4447 | let fieldTy = recInfo.fields[i].fieldType; |
|
| 4659 | 4448 | let argVal = try lowerExpr(self, argNode); |
|
| 4660 | 4449 | try emitStore(self, dst, recInfo.fields[i].offset, fieldTy, argVal); |
| 4662 | 4451 | } |
|
| 4663 | 4452 | return il::Val::Reg(dst); |
|
| 4664 | 4453 | } |
|
| 4665 | 4454 | ||
| 4666 | 4455 | /// Lower an array literal expression like `[1, 2, 3]`. |
|
| 4667 | - | fn lowerArrayLit(self: *mut FnLowerer, node: *ast::Node, elements: ast::NodeList) -> il::Val |
|
| 4456 | + | fn lowerArrayLit(self: *mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val |
|
| 4668 | 4457 | throws (LowerError) |
|
| 4669 | 4458 | { |
|
| 4670 | 4459 | let typ = resolver::typeFor(self.low.resolver, node) else { |
|
| 4671 | 4460 | throw LowerError::MissingType(node); |
|
| 4672 | 4461 | }; |
| 4676 | 4465 | let elemTy = *arrInfo.item; |
|
| 4677 | 4466 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 4678 | 4467 | let dst = try emitReserve(self, typ); |
|
| 4679 | 4468 | ||
| 4680 | 4469 | for i in 0..elements.len { |
|
| 4681 | - | let elemNode = elements.list[i]; |
|
| 4470 | + | let elemNode = elements[i]; |
|
| 4682 | 4471 | let elemVal = try lowerExpr(self, elemNode); |
|
| 4683 | 4472 | let offset = i * elemLayout.size; |
|
| 4684 | 4473 | ||
| 4685 | 4474 | try emitStore(self, dst, offset as i32, elemTy, elemVal); |
|
| 4686 | 4475 | } |
| 4896 | 4685 | /// stack space and stores elements at runtime. |
|
| 4897 | 4686 | fn lowerSliceLiteral( |
|
| 4898 | 4687 | self: *mut FnLowerer, |
|
| 4899 | 4688 | sliceNode: *ast::Node, |
|
| 4900 | 4689 | arrayNode: *ast::Node, |
|
| 4901 | - | elements: ast::NodeList |
|
| 4690 | + | elements: *mut [*ast::Node] |
|
| 4902 | 4691 | ) -> il::Val throws (LowerError) { |
|
| 4903 | 4692 | // Get the slice type from the address-of expression. |
|
| 4904 | 4693 | let sliceTy = resolver::typeFor(self.low.resolver, sliceNode) else { |
|
| 4905 | 4694 | throw LowerError::MissingType(sliceNode); |
|
| 4906 | 4695 | }; |
| 4921 | 4710 | /// Lower a slice literal with all constant elements to static data. |
|
| 4922 | 4711 | fn lowerConstSliceLiteral( |
|
| 4923 | 4712 | self: *mut FnLowerer, |
|
| 4924 | 4713 | elemTy: *resolver::Type, |
|
| 4925 | 4714 | mutable: bool, |
|
| 4926 | - | elements: ast::NodeList, |
|
| 4715 | + | elements: *mut [*ast::Node], |
|
| 4927 | 4716 | elemLayout: resolver::Layout |
|
| 4928 | 4717 | ) -> il::Val throws (LowerError) { |
|
| 4929 | 4718 | // Build data values for all elements using standard data lowering. |
|
| 4930 | - | let mut b = dataBuilder(self.low.arena); |
|
| 4931 | - | for i in 0..elements.len { |
|
| 4932 | - | let elem = elements.list[i]; |
|
| 4719 | + | let mut b = dataBuilder(self.low.allocator); |
|
| 4720 | + | for elem in elements { |
|
| 4933 | 4721 | try lowerConstDataInto(self.low, elem, *elemTy, elemLayout.size, self.fnName, &mut b); |
|
| 4934 | 4722 | } |
|
| 4935 | 4723 | let result = dataBuilderFinish(&b); |
|
| 4936 | 4724 | let readOnly = not mutable; |
|
| 4937 | 4725 |
| 4941 | 4729 | /// Lower a slice literal with non-constant elements. |
|
| 4942 | 4730 | fn lowerRuntimeSliceLiteral( |
|
| 4943 | 4731 | self: *mut FnLowerer, |
|
| 4944 | 4732 | elemTy: *resolver::Type, |
|
| 4945 | 4733 | mutable: bool, |
|
| 4946 | - | elements: ast::NodeList |
|
| 4734 | + | elements: *mut [*ast::Node] |
|
| 4947 | 4735 | ) -> il::Val throws (LowerError) { |
|
| 4948 | 4736 | let elemLayout = resolver::getTypeLayout(*elemTy); |
|
| 4949 | 4737 | let arraySize = elements.len * elemLayout.size; |
|
| 4950 | 4738 | let arrayReg = nextReg(self); |
|
| 4951 | 4739 |
| 4955 | 4743 | size: il::Val::Imm(arraySize as i64), |
|
| 4956 | 4744 | alignment: elemLayout.alignment |
|
| 4957 | 4745 | }); |
|
| 4958 | 4746 | // Store each element. |
|
| 4959 | 4747 | for i in 0..elements.len { |
|
| 4960 | - | let elemNode = elements.list[i]; |
|
| 4748 | + | let elemNode = elements[i]; |
|
| 4961 | 4749 | let elemVal = try lowerExpr(self, elemNode); |
|
| 4962 | 4750 | let offset = i * elemLayout.size; |
|
| 4963 | 4751 | ||
| 4964 | 4752 | try emitStore(self, arrayReg, offset as i32, *elemTy, elemVal); |
|
| 4965 | 4753 | } |
| 5467 | 5255 | } |
|
| 5468 | 5256 | } |
|
| 5469 | 5257 | ||
| 5470 | 5258 | /// Compute the size of the return buffer for the current function. |
|
| 5471 | 5259 | fn retBufSize(self: *mut FnLowerer) -> u32 { |
|
| 5472 | - | if self.fnType.throwListLen > 0 { |
|
| 5260 | + | if self.fnType.throwList.len > 0 { |
|
| 5473 | 5261 | let successType = *self.fnType.returnType; |
|
| 5474 | 5262 | ||
| 5475 | - | return resolver::getResultLayout(successType, &self.fnType.throwList[..self.fnType.throwListLen]).size; |
|
| 5263 | + | return resolver::getResultLayout(successType, self.fnType.throwList).size; |
|
| 5476 | 5264 | } |
|
| 5477 | 5265 | return resolver::getTypeLayout(*self.fnType.returnType).size; |
|
| 5478 | 5266 | } |
|
| 5479 | 5267 | ||
| 5480 | 5268 | /// Lower a return statement. |
| 5487 | 5275 | try emitRetVal(self, val); |
|
| 5488 | 5276 | } |
|
| 5489 | 5277 | ||
| 5490 | 5278 | /// Lower a throw statement. |
|
| 5491 | 5279 | fn lowerThrowStmt(self: *mut FnLowerer, expr: *ast::Node) throws (LowerError) { |
|
| 5492 | - | assert self.fnType.throwListLen > 0; |
|
| 5280 | + | assert self.fnType.throwList.len > 0; |
|
| 5493 | 5281 | ||
| 5494 | 5282 | let errType = resolver::typeFor(self.low.resolver, expr) else { |
|
| 5495 | 5283 | throw LowerError::MissingType(expr); |
|
| 5496 | 5284 | }; |
|
| 5497 | 5285 | let tag = getOrAssignErrorTag(self.low, errType) as i64; |
| 5920 | 5708 | ||
| 5921 | 5709 | return try lowerConstDataAsSlice(self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len); |
|
| 5922 | 5710 | } |
|
| 5923 | 5711 | ||
| 5924 | 5712 | /// Lower a builtin call expression. |
|
| 5925 | - | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: ast::NodeList) -> il::Val throws (LowerError) { |
|
| 5713 | + | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 5926 | 5714 | match kind { |
|
| 5927 | 5715 | case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args), |
|
| 5928 | 5716 | case ast::Builtin::SizeOf, ast::Builtin::AlignOf => { |
|
| 5929 | 5717 | let constVal = resolver::constValueFor(self.low.resolver, node) else { |
|
| 5930 | 5718 | throw LowerError::MissingConst(node); |
| 5933 | 5721 | } |
|
| 5934 | 5722 | } |
|
| 5935 | 5723 | } |
|
| 5936 | 5724 | ||
| 5937 | 5725 | /// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call. |
|
| 5938 | - | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: ast::NodeList) -> il::Val throws (LowerError) { |
|
| 5726 | + | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 5939 | 5727 | if args.len != 2 and args.len != 3 { |
|
| 5940 | 5728 | throw LowerError::InvalidArgCount; |
|
| 5941 | 5729 | } |
|
| 5942 | 5730 | let sliceTy = resolver::typeFor(self.low.resolver, node) else { |
|
| 5943 | 5731 | throw LowerError::MissingType(node); |
|
| 5944 | 5732 | }; |
|
| 5945 | 5733 | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 5946 | 5734 | throw LowerError::ExpectedSliceOrArray; |
|
| 5947 | 5735 | }; |
|
| 5948 | - | let ptrVal = try lowerExpr(self, args.list[0]); |
|
| 5949 | - | let lenVal = try lowerExpr(self, args.list[1]); |
|
| 5736 | + | let ptrVal = try lowerExpr(self, args[0]); |
|
| 5737 | + | let lenVal = try lowerExpr(self, args[1]); |
|
| 5950 | 5738 | let mut capVal = lenVal; |
|
| 5951 | 5739 | if args.len == 3 { |
|
| 5952 | - | capVal = try lowerExpr(self, args.list[2]); |
|
| 5740 | + | capVal = try lowerExpr(self, args[2]); |
|
| 5953 | 5741 | } |
|
| 5954 | 5742 | return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal); |
|
| 5955 | 5743 | } |
|
| 5956 | 5744 | ||
| 5957 | 5745 | /// Lower a `try` expression. |
| 6031 | 5819 | try emitStore(self, slot, 0, tryExprTy, errVal); |
|
| 6032 | 5820 | } |
|
| 6033 | 5821 | try emitMergeIfUnterminated(self, &mut mergeBlock); |
|
| 6034 | 5822 | } else if t.catches.len > 0 { |
|
| 6035 | 5823 | // `try ... catch` -- handle the error. |
|
| 6036 | - | let firstNode = t.catches.list[0]; |
|
| 5824 | + | let firstNode = t.catches[0]; |
|
| 6037 | 5825 | let case ast::NodeValue::CatchClause(first) = firstNode.value |
|
| 6038 | 5826 | else panic "lowerTry: expected CatchClause"; |
|
| 6039 | 5827 | ||
| 6040 | 5828 | if first.typeNode != nil or t.catches.len > 1 { |
|
| 6041 | 5829 | // Typed multi-catch: switch on global error tag. |
| 6061 | 5849 | emit(self, il::Instr::Unreachable); |
|
| 6062 | 5850 | } else { |
|
| 6063 | 5851 | // Plain `try` -- propagate the error to the caller by returning early. |
|
| 6064 | 5852 | // Forward the callee's global error tag and payload directly. |
|
| 6065 | 5853 | let callerLayout = resolver::getResultLayout( |
|
| 6066 | - | *self.fnType.returnType, &self.fnType.throwList[..self.fnType.throwListLen] |
|
| 5854 | + | *self.fnType.returnType, self.fnType.throwList |
|
| 6067 | 5855 | ); |
|
| 6068 | - | let calleeErrSize = maxErrSize(&calleeInfo.throwList[..calleeInfo.throwListLen]); |
|
| 5856 | + | let calleeErrSize = maxErrSize(calleeInfo.throwList); |
|
| 6069 | 5857 | let dst = try emitReserveLayout(self, callerLayout); |
|
| 6070 | 5858 | ||
| 6071 | 5859 | emitStoreW64At(self, il::Val::Reg(tagReg), dst, TVAL_TAG_OFFSET); |
|
| 6072 | 5860 | let srcPayload = emitPtrOffset(self, base, RESULT_VAL_OFFSET); |
|
| 6073 | 5861 | let dstPayload = emitPtrOffset(self, dst, RESULT_VAL_OFFSET); |
| 6100 | 5888 | /// Emits a switch on the global error tag to dispatch to the correct catch |
|
| 6101 | 5889 | /// clause. Each typed clause extracts the error payload for its specific type |
|
| 6102 | 5890 | /// and binds it to the clause's identifier. |
|
| 6103 | 5891 | fn lowerMultiCatch( |
|
| 6104 | 5892 | self: *mut FnLowerer, |
|
| 6105 | - | catches: ast::NodeList, |
|
| 5893 | + | catches: *mut [*ast::Node], |
|
| 6106 | 5894 | calleeInfo: *resolver::FnType, |
|
| 6107 | 5895 | base: il::Reg, |
|
| 6108 | 5896 | tagReg: il::Reg, |
|
| 6109 | 5897 | mergeBlock: *mut ?BlockId |
|
| 6110 | 5898 | ) throws (LowerError) { |
|
| 6111 | 5899 | let entry = currentBlock(self); |
|
| 6112 | 5900 | ||
| 6113 | 5901 | // First pass: create blocks, resolve error types, and build switch cases. |
|
| 6114 | 5902 | let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined; |
|
| 6115 | 5903 | let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined; |
|
| 6116 | - | let cases = try! alloc::allocSlice( |
|
| 6117 | - | self.low.arena, |
|
| 6118 | - | @sizeOf(il::SwitchCase), |
|
| 6119 | - | @alignOf(il::SwitchCase), |
|
| 6120 | - | catches.len |
|
| 6121 | - | ) as *mut [il::SwitchCase]; |
|
| 6122 | - | let mut caseIdx: u32 = 0; |
|
| 5904 | + | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 6123 | 5905 | let mut defaultIdx: ?u32 = nil; |
|
| 6124 | 5906 | ||
| 6125 | 5907 | for i in 0..catches.len { |
|
| 6126 | - | let clauseNode = catches.list[i]; |
|
| 5908 | + | let clauseNode = catches[i]; |
|
| 6127 | 5909 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value |
|
| 6128 | 5910 | else panic "lowerMultiCatch: expected CatchClause"; |
|
| 6129 | 5911 | ||
| 6130 | 5912 | blocks[i] = try createBlock(self, "catch"); |
|
| 6131 | 5913 | try addPredecessor(self, blocks[i], entry); |
| 6134 | 5916 | let errTy = resolver::typeFor(self.low.resolver, typeNode) else { |
|
| 6135 | 5917 | throw LowerError::MissingType(typeNode); |
|
| 6136 | 5918 | }; |
|
| 6137 | 5919 | errTypes[i] = errTy; |
|
| 6138 | 5920 | ||
| 6139 | - | cases[caseIdx] = il::SwitchCase { |
|
| 5921 | + | cases.append(il::SwitchCase { |
|
| 6140 | 5922 | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6141 | 5923 | target: blocks[i].n, |
|
| 6142 | 5924 | args: &mut [] |
|
| 6143 | - | }; |
|
| 6144 | - | caseIdx += 1; |
|
| 5925 | + | }, self.allocator); |
|
| 6145 | 5926 | } else { |
|
| 6146 | 5927 | errTypes[i] = nil; |
|
| 6147 | 5928 | defaultIdx = i; |
|
| 6148 | 5929 | } |
|
| 6149 | 5930 | } |
| 6158 | 5939 | } |
|
| 6159 | 5940 | emit(self, il::Instr::Switch { |
|
| 6160 | 5941 | val: il::Val::Reg(tagReg), |
|
| 6161 | 5942 | defaultTarget: defaultTarget.n, |
|
| 6162 | 5943 | defaultArgs: &mut [], |
|
| 6163 | - | cases: &mut cases[..caseIdx] |
|
| 5944 | + | cases |
|
| 6164 | 5945 | }); |
|
| 6165 | 5946 | ||
| 6166 | 5947 | // Second pass: emit each catch clause body. |
|
| 6167 | 5948 | for i in 0..catches.len { |
|
| 6168 | - | let clauseNode = catches.list[i]; |
|
| 5949 | + | let clauseNode = catches[i]; |
|
| 6169 | 5950 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value |
|
| 6170 | 5951 | else panic "lowerMultiCatch: expected CatchClause"; |
|
| 6171 | 5952 | ||
| 6172 | 5953 | try switchToAndSeal(self, blocks[i]); |
|
| 6173 | 5954 | let savedVarsLen = enterVarScope(self); |
| 6265 | 6046 | // Get the address of the slice header. |
|
| 6266 | 6047 | let sliceVal = try lowerExpr(self, access.parent); |
|
| 6267 | 6048 | let sliceReg = try emitValToReg(self, sliceVal); |
|
| 6268 | 6049 | ||
| 6269 | 6050 | // Lower the value to append and the allocator. |
|
| 6270 | - | let elemVal = try lowerExpr(self, call.args.list[0]); |
|
| 6271 | - | let allocVal = try lowerExpr(self, call.args.list[1]); |
|
| 6051 | + | let elemVal = try lowerExpr(self, call.args[0]); |
|
| 6052 | + | let allocVal = try lowerExpr(self, call.args[1]); |
|
| 6272 | 6053 | let allocReg = try emitValToReg(self, allocVal); |
|
| 6273 | 6054 | ||
| 6274 | 6055 | let elemLayout = resolver::getTypeLayout(*elemType); |
|
| 6275 | 6056 | let stride = elemLayout.size; |
|
| 6276 | 6057 | let alignment = elemLayout.alignment; |
| 6352 | 6133 | // Get slice header address. |
|
| 6353 | 6134 | let sliceVal = try lowerExpr(self, access.parent); |
|
| 6354 | 6135 | let sliceReg = try emitValToReg(self, sliceVal); |
|
| 6355 | 6136 | ||
| 6356 | 6137 | // Lower the index argument. |
|
| 6357 | - | let indexVal = try lowerExpr(self, call.args.list[0]); |
|
| 6138 | + | let indexVal = try lowerExpr(self, call.args[0]); |
|
| 6358 | 6139 | ||
| 6359 | 6140 | // Load len and bounds-check: index must be smaller than length. |
|
| 6360 | 6141 | let lenVal = loadSliceLen(self, sliceReg); |
|
| 6361 | 6142 | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, lenVal); |
|
| 6362 | 6143 |
| 6477 | 6258 | // Data pointer is the receiver (first argument after hidden return param). |
|
| 6478 | 6259 | args[argOffset] = il::Val::Reg(dataReg); |
|
| 6479 | 6260 | ||
| 6480 | 6261 | // Lower user arguments. |
|
| 6481 | 6262 | for i in 0..call.args.len { |
|
| 6482 | - | args[i + 1 + argOffset] = try lowerExpr(self, call.args.list[i]); |
|
| 6263 | + | args[i + 1 + argOffset] = try lowerExpr(self, call.args[i]); |
|
| 6483 | 6264 | } |
|
| 6484 | 6265 | ||
| 6485 | 6266 | // Allocate the return buffer when needed. |
|
| 6486 | 6267 | if returnParam { |
|
| 6487 | - | if methodFnType.throwListLen > 0 { |
|
| 6268 | + | if methodFnType.throwList.len > 0 { |
|
| 6488 | 6269 | let successType = *methodFnType.returnType; |
|
| 6489 | 6270 | let layout = resolver::getResultLayout( |
|
| 6490 | - | successType, &methodFnType.throwList[..methodFnType.throwListLen]); |
|
| 6271 | + | successType, methodFnType.throwList); |
|
| 6491 | 6272 | ||
| 6492 | 6273 | args[0] = il::Val::Reg(try emitReserveLayout(self, layout)); |
|
| 6493 | 6274 | } else { |
|
| 6494 | 6275 | args[0] = il::Val::Reg(try emitReserve(self, retTy)); |
|
| 6495 | 6276 | } |
| 6561 | 6342 | /// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`. |
|
| 6562 | 6343 | fn lowerEcall(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6563 | 6344 | if call.args.len != 5 { |
|
| 6564 | 6345 | throw LowerError::InvalidArgCount; |
|
| 6565 | 6346 | } |
|
| 6566 | - | let num = try lowerExpr(self, call.args.list[0]); |
|
| 6567 | - | let a0 = try lowerExpr(self, call.args.list[1]); |
|
| 6568 | - | let a1 = try lowerExpr(self, call.args.list[2]); |
|
| 6569 | - | let a2 = try lowerExpr(self, call.args.list[3]); |
|
| 6570 | - | let a3 = try lowerExpr(self, call.args.list[4]); |
|
| 6347 | + | let num = try lowerExpr(self, call.args[0]); |
|
| 6348 | + | let a0 = try lowerExpr(self, call.args[1]); |
|
| 6349 | + | let a1 = try lowerExpr(self, call.args[2]); |
|
| 6350 | + | let a2 = try lowerExpr(self, call.args[3]); |
|
| 6351 | + | let a3 = try lowerExpr(self, call.args[4]); |
|
| 6571 | 6352 | let dst = nextReg(self); |
|
| 6572 | 6353 | ||
| 6573 | 6354 | emit(self, il::Instr::Ecall { dst, num, a0, a1, a2, a3 }); |
|
| 6574 | 6355 | ||
| 6575 | 6356 | return il::Val::Reg(dst); |
| 6629 | 6410 | // hidden return buffer when needed. |
|
| 6630 | 6411 | let callee = try lowerCallee(self, call.callee); |
|
| 6631 | 6412 | let offset: u32 = 1 if returnParam else 0; |
|
| 6632 | 6413 | let args = try allocVals(self, call.args.len + offset); |
|
| 6633 | 6414 | for i in 0..call.args.len { |
|
| 6634 | - | args[i + offset] = try lowerExpr(self, call.args.list[i]); |
|
| 6415 | + | args[i + offset] = try lowerExpr(self, call.args[i]); |
|
| 6635 | 6416 | } |
|
| 6636 | 6417 | ||
| 6637 | 6418 | // Allocate the return buffer when needed. |
|
| 6638 | 6419 | if returnParam { |
|
| 6639 | - | if fnInfo.throwListLen > 0 { |
|
| 6420 | + | if fnInfo.throwList.len > 0 { |
|
| 6640 | 6421 | let successType = *fnInfo.returnType; |
|
| 6641 | - | let layout = resolver::getResultLayout(successType, &fnInfo.throwList[..fnInfo.throwListLen]); |
|
| 6422 | + | let layout = resolver::getResultLayout(successType, fnInfo.throwList); |
|
| 6642 | 6423 | ||
| 6643 | 6424 | args[0] = il::Val::Reg(try emitReserveLayout(self, layout)); |
|
| 6644 | 6425 | } else { |
|
| 6645 | 6426 | args[0] = il::Val::Reg(try emitReserve(self, retTy)); |
|
| 6646 | 6427 | } |
lib/std/lang/parser.rad
+48 -50
| 138 | 138 | previous: scanner::Token, |
|
| 139 | 139 | /// Collection of errors encountered during parsing. |
|
| 140 | 140 | errors: ErrorList, |
|
| 141 | 141 | /// Arena for all node allocations. |
|
| 142 | 142 | arena: *mut ast::NodeArena, |
|
| 143 | + | /// Allocator backed by the node arena. |
|
| 144 | + | allocator: alloc::Allocator, |
|
| 143 | 145 | /// Current parsing context (normal or conditional). |
|
| 144 | 146 | context: Context, |
|
| 145 | 147 | } |
|
| 146 | 148 | ||
| 147 | 149 | /// Create a new parser initialized with the given source and node arena. |
| 150 | 152 | scanner: scanner::scanner("", source, pool), |
|
| 151 | 153 | current: scanner::invalid(0, ""), |
|
| 152 | 154 | previous: scanner::invalid(0, ""), |
|
| 153 | 155 | errors: ErrorList { list: undefined, count: 0 }, |
|
| 154 | 156 | arena, |
|
| 157 | + | allocator: ast::nodeAllocator(arena), |
|
| 155 | 158 | context: Context::Normal, |
|
| 156 | 159 | }; |
|
| 157 | 160 | } |
|
| 158 | 161 | ||
| 159 | 162 | /// Emit a `true` or `false` literal node. |
| 350 | 353 | fn parseArrayLiteral(p: *mut Parser) -> *ast::Node |
|
| 351 | 354 | throws (ParseError) |
|
| 352 | 355 | { |
|
| 353 | 356 | try expect(p, scanner::TokenKind::LBracket, "expected `[`"); |
|
| 354 | 357 | if consume(p, scanner::TokenKind::RBracket) { // Empty array: `[]`. |
|
| 355 | - | return node(p, ast::NodeValue::ArrayLit(ast::nodeList(p.arena, 0))); |
|
| 358 | + | let empty: *mut [*ast::Node] = &mut []; |
|
| 359 | + | return node(p, ast::NodeValue::ArrayLit(empty)); |
|
| 356 | 360 | } |
|
| 357 | 361 | let firstExpr = try parseExpr(p); |
|
| 358 | 362 | ||
| 359 | 363 | if consume(p, scanner::TokenKind::Semicolon) { |
|
| 360 | 364 | // Array repeat literal: `[item; count]`. |
| 364 | 368 | return node(p, ast::NodeValue::ArrayRepeatLit( |
|
| 365 | 369 | ast::ArrayRepeatLit { item: firstExpr, count } |
|
| 366 | 370 | )); |
|
| 367 | 371 | } |
|
| 368 | 372 | // Regular array literal: `[a, b, ...]`. |
|
| 369 | - | let mut items = ast::nodeList(p.arena, 64); |
|
| 370 | - | ast::nodeListPush(&mut items, firstExpr); |
|
| 373 | + | let mut items = ast::nodeSlice(p.arena, 64); |
|
| 374 | + | items.append(firstExpr, p.allocator); |
|
| 371 | 375 | ||
| 372 | 376 | while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) { |
|
| 373 | 377 | let elem = try parseExpr(p); |
|
| 374 | - | ast::nodeListPush(&mut items, elem); |
|
| 378 | + | items.append(elem, p.allocator); |
|
| 375 | 379 | } |
|
| 376 | 380 | try expect(p, scanner::TokenKind::RBracket, "expected `]` after array elements"); |
|
| 377 | 381 | ||
| 378 | 382 | return node(p, ast::NodeValue::ArrayLit(items)); |
|
| 379 | 383 | } |
| 807 | 811 | } |
|
| 808 | 812 | try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name"); |
|
| 809 | 813 | ||
| 810 | 814 | // Parse arguments into a list. Use capacity 4 to handle any valid argument count |
|
| 811 | 815 | // plus some extra for error recovery. |
|
| 812 | - | let mut args = ast::nodeList(p.arena, 4); |
|
| 816 | + | let mut args = ast::nodeSlice(p.arena, 4); |
|
| 813 | 817 | ||
| 814 | 818 | if kind == ast::Builtin::SliceOf { |
|
| 815 | 819 | // Parse comma-separated expressions until closing paren. |
|
| 816 | 820 | // Argument count validation is done in semantic analysis. |
|
| 817 | 821 | if p.current.kind != scanner::TokenKind::RParen { |
|
| 818 | - | ast::nodeListPush(&mut args, try parseExpr(p)); |
|
| 822 | + | args.append(try parseExpr(p), p.allocator); |
|
| 819 | 823 | while p.current.kind == scanner::TokenKind::Comma { |
|
| 820 | 824 | advance(p); |
|
| 821 | - | ast::nodeListPush(&mut args, try parseExpr(p)); |
|
| 825 | + | args.append(try parseExpr(p), p.allocator); |
|
| 822 | 826 | } |
|
| 823 | 827 | } |
|
| 824 | 828 | } else { |
|
| 825 | - | ast::nodeListPush(&mut args, try parseType(p)); |
|
| 829 | + | args.append(try parseType(p), p.allocator); |
|
| 826 | 830 | } |
|
| 827 | 831 | try expect(p, scanner::TokenKind::RParen, "expected `)` after builtin argument"); |
|
| 828 | 832 | ||
| 829 | 833 | return node(p, ast::NodeValue::BuiltinCall { kind, args }); |
|
| 830 | 834 | } |
| 894 | 898 | return node(p, ast::NodeValue::ExprStmt(expr)); |
|
| 895 | 899 | } |
|
| 896 | 900 | ||
| 897 | 901 | /// Parse leading attributes attached to the next declaration statement. |
|
| 898 | 902 | fn parseAttributes(p: *mut Parser) -> ?ast::Attributes { |
|
| 899 | - | let mut attrs = ast::nodeList(p.arena, 4); |
|
| 903 | + | let mut attrs = ast::nodeSlice(p.arena, 4); |
|
| 900 | 904 | ||
| 901 | 905 | if let attr = tryParseAnnotation(p) { |
|
| 902 | - | ast::nodeListPush(&mut attrs, attr); |
|
| 906 | + | attrs.append(attr, p.allocator); |
|
| 903 | 907 | } |
|
| 904 | 908 | if consume(p, scanner::TokenKind::Pub) { |
|
| 905 | 909 | let attrNode = nodeAttribute(p, ast::Attribute::Pub); |
|
| 906 | - | ast::nodeListPush(&mut attrs, attrNode); |
|
| 910 | + | attrs.append(attrNode, p.allocator); |
|
| 907 | 911 | } |
|
| 908 | 912 | if consume(p, scanner::TokenKind::Extern) { |
|
| 909 | 913 | let attrNode = nodeAttribute(p, ast::Attribute::Extern); |
|
| 910 | - | ast::nodeListPush(&mut attrs, attrNode); |
|
| 914 | + | attrs.append(attrNode, p.allocator); |
|
| 911 | 915 | } |
|
| 912 | 916 | if attrs.len > 0 { |
|
| 913 | 917 | return ast::Attributes { list: attrs }; |
|
| 914 | 918 | } |
|
| 915 | 919 | return nil; |
| 1054 | 1058 | pub fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block) |
|
| 1055 | 1059 | throws (ParseError) |
|
| 1056 | 1060 | { |
|
| 1057 | 1061 | while not check(p, end) { |
|
| 1058 | 1062 | let stmt = try parseStmt(p); |
|
| 1059 | - | ast::nodeListPush(&mut blk.statements, stmt); |
|
| 1063 | + | blk.statements.append(stmt, p.allocator); |
|
| 1060 | 1064 | ||
| 1061 | 1065 | if check(p, end) or check(p, scanner::TokenKind::Eof) { |
|
| 1062 | 1066 | break; |
|
| 1063 | 1067 | } |
|
| 1064 | 1068 | if not consume(p, scanner::TokenKind::Semicolon) { |
| 1086 | 1090 | return node(p, ast::NodeValue::Block(blk)); |
|
| 1087 | 1091 | } |
|
| 1088 | 1092 | ||
| 1089 | 1093 | /// Create an empty block with no statements. |
|
| 1090 | 1094 | fn mkBlock(p: *mut Parser, cap: u32) -> ast::Block { |
|
| 1091 | - | return ast::Block { statements: ast::nodeList(p.arena, cap) }; |
|
| 1095 | + | return ast::Block { statements: ast::nodeSlice(p.arena, cap) }; |
|
| 1092 | 1096 | } |
|
| 1093 | 1097 | ||
| 1094 | 1098 | /// Create a block containing a single statement node. |
|
| 1095 | 1099 | fn mkBlockWith(p: *mut Parser, node: *ast::Node) -> ast::Block { |
|
| 1096 | - | let mut stmts = ast::nodeList(p.arena, 1); |
|
| 1097 | - | ast::nodeListPush(&mut stmts, node); |
|
| 1100 | + | let mut stmts = ast::nodeSlice(p.arena, 1); |
|
| 1101 | + | stmts.append(node, p.allocator); |
|
| 1098 | 1102 | ||
| 1099 | 1103 | return ast::Block { statements: stmts }; |
|
| 1100 | 1104 | } |
|
| 1101 | 1105 | ||
| 1102 | 1106 | /// Parse the branch that follows `else` in let-else style constructs. |
| 1415 | 1419 | try expect(p, scanner::TokenKind::Try, "expected `try`"); |
|
| 1416 | 1420 | ||
| 1417 | 1421 | let shouldPanic = consume(p, scanner::TokenKind::Bang); |
|
| 1418 | 1422 | let returnsOptional = consume(p, scanner::TokenKind::Question); |
|
| 1419 | 1423 | let expr = try parsePrimary(p); |
|
| 1420 | - | let mut catches = ast::nodeList(p.arena, 4); |
|
| 1424 | + | let mut catches = ast::nodeSlice(p.arena, 4); |
|
| 1421 | 1425 | ||
| 1422 | 1426 | while consume(p, scanner::TokenKind::Catch) { |
|
| 1423 | 1427 | let mut binding: ?*ast::Node = nil; |
|
| 1424 | 1428 | let mut typeNode: ?*ast::Node = nil; |
|
| 1425 | 1429 |
| 1436 | 1440 | } |
|
| 1437 | 1441 | let body = try parseBlock(p); |
|
| 1438 | 1442 | let clause = node(p, ast::NodeValue::CatchClause( |
|
| 1439 | 1443 | ast::CatchClause { binding, typeNode, body } |
|
| 1440 | 1444 | )); |
|
| 1441 | - | ast::nodeListPush(&mut catches, clause); |
|
| 1445 | + | catches.append(clause, p.allocator); |
|
| 1442 | 1446 | } |
|
| 1443 | 1447 | return node(p, ast::NodeValue::Try( |
|
| 1444 | 1448 | ast::Try { expr, catches, shouldPanic, returnsOptional } |
|
| 1445 | 1449 | )); |
|
| 1446 | 1450 | } |
| 1518 | 1522 | try expect(p, scanner::TokenKind::Match, "expected `match`"); |
|
| 1519 | 1523 | ||
| 1520 | 1524 | let subject = try parseCond(p); |
|
| 1521 | 1525 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs"); |
|
| 1522 | 1526 | ||
| 1523 | - | let mut prongs = ast::nodeList(p.arena, 128); |
|
| 1527 | + | let mut prongs = ast::nodeSlice(p.arena, 128); |
|
| 1524 | 1528 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 1525 | 1529 | not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF. |
|
| 1526 | 1530 | { |
|
| 1527 | 1531 | let prongNode = try parseMatchProng(p); |
|
| 1528 | - | ast::nodeListPush(&mut prongs, prongNode); |
|
| 1532 | + | prongs.append(prongNode, p.allocator); |
|
| 1529 | 1533 | consume(p, scanner::TokenKind::Comma); |
|
| 1530 | 1534 | } |
|
| 1531 | 1535 | try expect(p, scanner::TokenKind::RBrace, "expected `}` after match prongs"); |
|
| 1532 | 1536 | ||
| 1533 | 1537 | return node(p, ast::NodeValue::Match( |
| 1541 | 1545 | { |
|
| 1542 | 1546 | let mut guard: ?*ast::Node = nil; |
|
| 1543 | 1547 | ||
| 1544 | 1548 | // Case prong: `case <pattern>, ... if <guard> => <body>`. |
|
| 1545 | 1549 | if consume(p, scanner::TokenKind::Case) { |
|
| 1546 | - | let mut patterns = ast::nodeList(p.arena, 16); |
|
| 1550 | + | let mut patterns = ast::nodeSlice(p.arena, 16); |
|
| 1547 | 1551 | loop { |
|
| 1548 | 1552 | let pattern = try parseMatchPattern(p); |
|
| 1549 | - | ast::nodeListPush(&mut patterns, pattern); |
|
| 1553 | + | patterns.append(pattern, p.allocator); |
|
| 1550 | 1554 | ||
| 1551 | 1555 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1552 | 1556 | break; |
|
| 1553 | 1557 | } |
|
| 1554 | 1558 | } |
| 1635 | 1639 | /// For labeled fields: `{ name: T, ... }`. |
|
| 1636 | 1640 | /// For unlabeled fields: `(T, T, ...)`. |
|
| 1637 | 1641 | fn parseRecordFields( |
|
| 1638 | 1642 | p: *mut Parser, |
|
| 1639 | 1643 | mode: RecordFieldMode |
|
| 1640 | - | ) -> ast::NodeList |
|
| 1644 | + | ) -> *mut [*ast::Node] |
|
| 1641 | 1645 | throws (ParseError) |
|
| 1642 | 1646 | { |
|
| 1643 | 1647 | let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled |
|
| 1644 | 1648 | else scanner::TokenKind::RParen; |
|
| 1645 | - | let mut fields = ast::nodeList(p.arena, MAX_RECORD_FIELDS); |
|
| 1649 | + | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1646 | 1650 | while not check(p, terminator) { |
|
| 1647 | 1651 | let mut recordField: ast::NodeValue = undefined; |
|
| 1648 | 1652 | match mode { |
|
| 1649 | 1653 | case RecordFieldMode::Labeled => { |
|
| 1650 | 1654 | // Allow optional `let` keyword before field name. |
| 1669 | 1673 | recordField = ast::NodeValue::RecordField { |
|
| 1670 | 1674 | field: nil, type, value: nil, |
|
| 1671 | 1675 | }; |
|
| 1672 | 1676 | } |
|
| 1673 | 1677 | } |
|
| 1674 | - | ast::nodeListPush(&mut fields, node(p, recordField)); |
|
| 1678 | + | fields.append(node(p, recordField), p.allocator); |
|
| 1675 | 1679 | ||
| 1676 | 1680 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1677 | 1681 | break; |
|
| 1678 | 1682 | } |
|
| 1679 | 1683 | } |
| 1681 | 1685 | ||
| 1682 | 1686 | return fields; |
|
| 1683 | 1687 | } |
|
| 1684 | 1688 | ||
| 1685 | 1689 | /// Parse an optional derives list (`: Trait + Trait`). |
|
| 1686 | - | fn parseDerives(p: *mut Parser) -> ast::NodeList throws (ParseError) { |
|
| 1687 | - | let mut derives = ast::nodeList(p.arena, 4); |
|
| 1690 | + | fn parseDerives(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1691 | + | let mut derives = ast::nodeSlice(p.arena, 4); |
|
| 1688 | 1692 | ||
| 1689 | 1693 | if not consume(p, scanner::TokenKind::Colon) { |
|
| 1690 | 1694 | return derives; |
|
| 1691 | 1695 | } |
|
| 1692 | 1696 | loop { |
|
| 1693 | 1697 | let t = try parseIdent(p, "expected trait name in derive list"); |
|
| 1694 | - | ast::nodeListPush(&mut derives, t); |
|
| 1698 | + | derives.append(t, p.allocator); |
|
| 1695 | 1699 | ||
| 1696 | 1700 | if not consume(p, scanner::TokenKind::Plus) { |
|
| 1697 | 1701 | break; |
|
| 1698 | 1702 | } |
|
| 1699 | 1703 | } |
| 1723 | 1727 | /// Eg. `{ x: 1, y: 2 }` |
|
| 1724 | 1728 | /// Eg. `{ x: 1, .. }` |
|
| 1725 | 1729 | fn parseRecordLit(p: *mut Parser, typeName: ?*ast::Node) -> *ast::Node |
|
| 1726 | 1730 | throws (ParseError) |
|
| 1727 | 1731 | { |
|
| 1728 | - | let mut fields = ast::nodeList(p.arena, MAX_RECORD_FIELDS); |
|
| 1732 | + | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1729 | 1733 | let mut ignoreRest = false; |
|
| 1730 | 1734 | try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal"); |
|
| 1731 | 1735 | ||
| 1732 | 1736 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1733 | 1737 | // Check for `..` to ignore remaining fields. |
|
| 1734 | 1738 | if consume(p, scanner::TokenKind::DotDot) { |
|
| 1735 | 1739 | ignoreRest = true; |
|
| 1736 | 1740 | break; |
|
| 1737 | 1741 | } |
|
| 1738 | 1742 | let field = try parseRecordLitField(p); |
|
| 1739 | - | ast::nodeListPush(&mut fields, field); |
|
| 1743 | + | fields.append(field, p.allocator); |
|
| 1740 | 1744 | ||
| 1741 | 1745 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1742 | 1746 | break; |
|
| 1743 | 1747 | } |
|
| 1744 | 1748 | } |
| 1784 | 1788 | let name = try parseIdent(p, "expected union name"); |
|
| 1785 | 1789 | let derives = try parseDerives(p); |
|
| 1786 | 1790 | ||
| 1787 | 1791 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body"); |
|
| 1788 | 1792 | ||
| 1789 | - | let mut variants = ast::nodeList(p.arena, 128); |
|
| 1793 | + | let mut variants = ast::nodeSlice(p.arena, 128); |
|
| 1790 | 1794 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1791 | 1795 | // Allow optional `case` keyword before variant name. |
|
| 1792 | 1796 | consume(p, scanner::TokenKind::Case); |
|
| 1793 | 1797 | ||
| 1794 | 1798 | let variantName = try parseIdent(p, "expected variant name"); |
| 1812 | 1816 | explicitValue = nodeNumber(p, literal); |
|
| 1813 | 1817 | } |
|
| 1814 | 1818 | ||
| 1815 | 1819 | let variant = node(p, ast::NodeValue::UnionDeclVariant( |
|
| 1816 | 1820 | ast::UnionDeclVariant { |
|
| 1817 | - | name: variantName, index: variants.len, value: explicitValue, type: payloadType, |
|
| 1821 | + | name: variantName, index: variants.len as u32, value: explicitValue, type: payloadType, |
|
| 1818 | 1822 | } |
|
| 1819 | 1823 | )); |
|
| 1820 | - | ast::nodeListPush(&mut variants, variant); |
|
| 1824 | + | variants.append(variant, p.allocator); |
|
| 1821 | 1825 | ||
| 1822 | 1826 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1823 | 1827 | break; |
|
| 1824 | 1828 | } |
|
| 1825 | 1829 | } |
| 1842 | 1846 | ast::FnParam { name: ntv.name, type } |
|
| 1843 | 1847 | )); |
|
| 1844 | 1848 | } |
|
| 1845 | 1849 | ||
| 1846 | 1850 | /// Parse an optional `throws` clause and return the collected type list. |
|
| 1847 | - | fn parseThrowList(p: *mut Parser) -> ast::NodeList |
|
| 1851 | + | fn parseThrowList(p: *mut Parser) -> *mut [*ast::Node] |
|
| 1848 | 1852 | throws (ParseError) |
|
| 1849 | 1853 | { |
|
| 1850 | 1854 | if not consume(p, scanner::TokenKind::Throws) { |
|
| 1851 | - | return ast::nodeList(p.arena, 0); |
|
| 1855 | + | return ast::nodeSlice(p.arena, 0); |
|
| 1852 | 1856 | } |
|
| 1853 | 1857 | return try parseList( |
|
| 1854 | 1858 | p, |
|
| 1855 | 1859 | scanner::TokenKind::LParen, |
|
| 1856 | 1860 | scanner::TokenKind::RParen, |
| 1884 | 1888 | /// Parse a function signature following the function name. |
|
| 1885 | 1889 | fn parseFnTypeSig(p: *mut Parser) -> ast::FnSig |
|
| 1886 | 1890 | throws (ParseError) |
|
| 1887 | 1891 | { |
|
| 1888 | 1892 | try expect(p, scanner::TokenKind::LParen, "expected `(` after function name"); |
|
| 1889 | - | let mut params = ast::nodeList(p.arena, 8); |
|
| 1893 | + | let mut params = ast::nodeSlice(p.arena, 8); |
|
| 1890 | 1894 | ||
| 1891 | 1895 | if not check(p, scanner::TokenKind::RParen) { |
|
| 1892 | 1896 | loop { |
|
| 1893 | 1897 | let param = try parseFnParam(p); |
|
| 1894 | - | if params.len >= params.list.len { |
|
| 1895 | - | throw failParsing(p, "too many function parameters"); |
|
| 1896 | - | } |
|
| 1897 | - | ast::nodeListPush(&mut params, param); |
|
| 1898 | + | params.append(param, p.allocator); |
|
| 1898 | 1899 | ||
| 1899 | 1900 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1900 | 1901 | break; |
|
| 1901 | 1902 | } |
|
| 1902 | 1903 | } |
| 2313 | 2314 | try expect(p, scanner::TokenKind::Trait, "expected `trait`"); |
|
| 2314 | 2315 | let name = try parseIdent(p, "expected trait name"); |
|
| 2315 | 2316 | let supertraits = try parseDerives(p); |
|
| 2316 | 2317 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name"); |
|
| 2317 | 2318 | ||
| 2318 | - | let mut methods = ast::nodeList(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2319 | + | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2319 | 2320 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2320 | 2321 | not check(p, scanner::TokenKind::Eof) |
|
| 2321 | 2322 | { |
|
| 2322 | 2323 | let method = try parseTraitMethodSig(p); |
|
| 2323 | - | ast::nodeListPush(&mut methods, method); |
|
| 2324 | + | methods.append(method, p.allocator); |
|
| 2324 | 2325 | } |
|
| 2325 | 2326 | try expect(p, scanner::TokenKind::RBrace, "expected `}` after trait methods"); |
|
| 2326 | 2327 | ||
| 2327 | 2328 | return node(p, ast::NodeValue::TraitDecl { name, supertraits, methods, attrs }); |
|
| 2328 | 2329 | } |
| 2358 | 2359 | let traitName = try parseTypePath(p); |
|
| 2359 | 2360 | try expect(p, scanner::TokenKind::For, "expected `for` after trait name"); |
|
| 2360 | 2361 | let targetType = try parseTypePath(p); |
|
| 2361 | 2362 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type"); |
|
| 2362 | 2363 | ||
| 2363 | - | let mut methods = ast::nodeList(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2364 | + | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2364 | 2365 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2365 | 2366 | not check(p, scanner::TokenKind::Eof) |
|
| 2366 | 2367 | { |
|
| 2367 | 2368 | let method = try parseInstanceMethodDecl(p); |
|
| 2368 | - | ast::nodeListPush(&mut methods, method); |
|
| 2369 | + | methods.append(method, p.allocator); |
|
| 2369 | 2370 | } |
|
| 2370 | 2371 | try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods"); |
|
| 2371 | 2372 | ||
| 2372 | 2373 | return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods }); |
|
| 2373 | 2374 | } |
| 2399 | 2400 | fn parseList( |
|
| 2400 | 2401 | p: *mut Parser, |
|
| 2401 | 2402 | open: scanner::TokenKind, |
|
| 2402 | 2403 | close: scanner::TokenKind, |
|
| 2403 | 2404 | parseItem: fn (*mut Parser) -> *ast::Node throws (ParseError) |
|
| 2404 | - | ) -> ast::NodeList throws (ParseError) { |
|
| 2405 | + | ) -> *mut [*ast::Node] throws (ParseError) { |
|
| 2405 | 2406 | try expect(p, open, listExpectMessage(open)); |
|
| 2406 | - | let mut items = ast::nodeList(p.arena, 8); |
|
| 2407 | + | let mut items = ast::nodeSlice(p.arena, 8); |
|
| 2407 | 2408 | ||
| 2408 | 2409 | while not check(p, close) { |
|
| 2409 | 2410 | let item = try parseItem(p); |
|
| 2410 | - | if items.len >= items.list.len { |
|
| 2411 | - | throw failParsing(p, "too many items in list"); |
|
| 2412 | - | } |
|
| 2413 | - | ast::nodeListPush(&mut items, item); |
|
| 2411 | + | items.append(item, p.allocator); |
|
| 2414 | 2412 | ||
| 2415 | 2413 | if not consume(p, scanner::TokenKind::Comma) { |
|
| 2416 | 2414 | break; |
|
| 2417 | 2415 | } |
|
| 2418 | 2416 | } |
lib/std/lang/parser/tests.rad
+81 -84
| 172 | 172 | try testing::expect(width == expectedWidth); |
|
| 173 | 173 | try testing::expect(sign == expectedSign); |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Extract a union variant and verify its name and index. |
|
| 177 | - | /// Returns the payload's fields NodeList for further checking with `expectField`. |
|
| 178 | - | fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> *ast::NodeList |
|
| 177 | + | /// Returns the payload's fields slice for further checking with `expectField`. |
|
| 178 | + | fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> *mut [*ast::Node] |
|
| 179 | 179 | throws (testing::TestError) |
|
| 180 | 180 | { |
|
| 181 | 181 | let case ast::NodeValue::UnionDeclVariant(v) = varNode.value |
|
| 182 | 182 | else throw testing::TestError::Failed; |
|
| 183 | 183 | try expectIdent(v.name, name); |
| 188 | 188 | let case ast::NodeValue::TypeSig(sig) = payloadType.value |
|
| 189 | 189 | else throw testing::TestError::Failed; |
|
| 190 | 190 | let case ast::TypeSig::Record { fields, .. } = sig |
|
| 191 | 191 | else throw testing::TestError::Failed; |
|
| 192 | 192 | ||
| 193 | - | return &fields; |
|
| 193 | + | return fields; |
|
| 194 | 194 | } |
|
| 195 | 195 | ||
| 196 | 196 | /// Check a record field has the expected name and type signature. |
|
| 197 | 197 | fn expectFieldSig( |
|
| 198 | - | fields: *ast::NodeList, index: u32, name: ?*[u8], sig: ast::TypeSig |
|
| 198 | + | fields: *mut [*ast::Node], index: u32, name: ?*[u8], sig: ast::TypeSig |
|
| 199 | 199 | ) throws (testing::TestError) { |
|
| 200 | - | let fieldNode = fields.list[index]; |
|
| 200 | + | let fieldNode = fields[index]; |
|
| 201 | 201 | let case ast::NodeValue::RecordField { field, type: fieldType, .. } = fieldNode.value |
|
| 202 | 202 | else throw testing::TestError::Failed; |
|
| 203 | 203 | ||
| 204 | 204 | if let expectedName = name { |
|
| 205 | 205 | let actualName = field else throw testing::TestError::Failed; |
| 238 | 238 | else throw testing::TestError::Failed; |
|
| 239 | 239 | ||
| 240 | 240 | if block.statements.len == 0 { |
|
| 241 | 241 | throw testing::TestError::Failed; |
|
| 242 | 242 | } |
|
| 243 | - | return block.statements.list[0]; |
|
| 243 | + | return block.statements[0]; |
|
| 244 | 244 | } |
|
| 245 | 245 | ||
| 246 | 246 | /// Get the last statement from a block node. |
|
| 247 | 247 | pub fn getBlockLastStmt(blk: *ast::Node) -> *ast::Node |
|
| 248 | 248 | throws (testing::TestError) |
| 251 | 251 | else throw testing::TestError::Failed; |
|
| 252 | 252 | ||
| 253 | 253 | if block.statements.len == 0 { |
|
| 254 | 254 | throw testing::TestError::Failed; |
|
| 255 | 255 | } |
|
| 256 | - | return block.statements.list[block.statements.len - 1]; |
|
| 256 | + | return block.statements[block.statements.len - 1]; |
|
| 257 | 257 | } |
|
| 258 | 258 | ||
| 259 | 259 | /// Test parsing boolean literals (`true` and `false`). |
|
| 260 | 260 | @test fn testParseBool() throws (testing::TestError) { |
|
| 261 | 261 | let r1 = try! parseExprStr("true"); |
| 564 | 564 | let root = try! parseStmtStr("{ first; second; third }"); |
|
| 565 | 565 | let case ast::NodeValue::Block(body) = root.value |
|
| 566 | 566 | else throw testing::TestError::Failed; |
|
| 567 | 567 | try testing::expect(body.statements.len == 3); |
|
| 568 | 568 | ||
| 569 | - | let firstStmt = body.statements.list[0]; |
|
| 569 | + | let firstStmt = body.statements[0]; |
|
| 570 | 570 | let case ast::NodeValue::ExprStmt(firstExpr) = firstStmt.value |
|
| 571 | 571 | else throw testing::TestError::Failed; |
|
| 572 | 572 | try expectIdent(firstExpr, "first"); |
|
| 573 | 573 | ||
| 574 | - | let secondStmt = body.statements.list[1]; |
|
| 574 | + | let secondStmt = body.statements[1]; |
|
| 575 | 575 | let case ast::NodeValue::ExprStmt(secondExpr) = secondStmt.value |
|
| 576 | 576 | else throw testing::TestError::Failed; |
|
| 577 | 577 | try expectIdent(secondExpr, "second"); |
|
| 578 | 578 | ||
| 579 | - | let thirdStmt = body.statements.list[2]; |
|
| 579 | + | let thirdStmt = body.statements[2]; |
|
| 580 | 580 | let case ast::NodeValue::ExprStmt(thirdExpr) = thirdStmt.value |
|
| 581 | 581 | else throw testing::TestError::Failed; |
|
| 582 | 582 | try expectIdent(thirdExpr, "third"); |
|
| 583 | 583 | } |
|
| 584 | 584 |
| 592 | 592 | ||
| 593 | 593 | let case ast::NodeValue::Block(body) = node.thenBranch.value |
|
| 594 | 594 | else throw testing::TestError::Failed; |
|
| 595 | 595 | try testing::expect(body.statements.len == 1); |
|
| 596 | 596 | ||
| 597 | - | let stmt = body.statements.list[0]; |
|
| 597 | + | let stmt = body.statements[0]; |
|
| 598 | 598 | let case ast::NodeValue::ExprStmt(expr) = stmt.value |
|
| 599 | 599 | else throw testing::TestError::Failed; |
|
| 600 | 600 | try expectIdent(expr, "only"); |
|
| 601 | 601 | } |
|
| 602 | 602 |
| 805 | 805 | ||
| 806 | 806 | try expectIdent(decl.name, "io"); |
|
| 807 | 807 | let attrs = decl.attrs |
|
| 808 | 808 | else throw testing::TestError::Failed; |
|
| 809 | 809 | ||
| 810 | - | try testing::expect(ast::attributesLen(&attrs) == 1); |
|
| 810 | + | try testing::expect(attrs.list.len == 1); |
|
| 811 | 811 | ||
| 812 | - | let attr_node = ast::attributesGet(&attrs, 0); |
|
| 813 | - | let case ast::NodeValue::Attribute(attr) = attr_node.value |
|
| 812 | + | let case ast::NodeValue::Attribute(attr) = attrs.list[0].value |
|
| 814 | 813 | else throw testing::TestError::Failed; |
|
| 815 | 814 | ||
| 816 | 815 | try testing::expect(attr == ast::Attribute::Pub); |
|
| 817 | 816 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Pub)); |
|
| 818 | 817 | } |
| 896 | 895 | ||
| 897 | 896 | try expectIdent(decl.name, "R"); |
|
| 898 | 897 | try testing::expect(decl.derives.len == 0); |
|
| 899 | 898 | try testing::expect(decl.fields.len == 2); |
|
| 900 | 899 | ||
| 901 | - | try expectFieldSig(&decl.fields, 0, "x", ast::TypeSig::Void); |
|
| 902 | - | try expectFieldSig(&decl.fields, 1, "y", ast::TypeSig::Bool); |
|
| 900 | + | try expectFieldSig(decl.fields, 0, "x", ast::TypeSig::Void); |
|
| 901 | + | try expectFieldSig(decl.fields, 1, "y", ast::TypeSig::Bool); |
|
| 903 | 902 | } |
|
| 904 | 903 | ||
| 905 | 904 | /// Test parsing a record declaration with derives. |
|
| 906 | 905 | @test fn testParseRecordDeclDerives() throws (testing::TestError) { |
|
| 907 | 906 | let node = try! parseStmtStr("record R: Eq + Debug { field: i32 }"); |
|
| 908 | 907 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 909 | 908 | else throw testing::TestError::Failed; |
|
| 910 | 909 | ||
| 911 | 910 | try expectIdent(decl.name, "R"); |
|
| 912 | 911 | try testing::expect(decl.derives.len == 2); |
|
| 913 | - | try expectIdent(decl.derives.list[0], "Eq"); |
|
| 914 | - | try expectIdent(decl.derives.list[1], "Debug"); |
|
| 912 | + | try expectIdent(decl.derives[0], "Eq"); |
|
| 913 | + | try expectIdent(decl.derives[1], "Debug"); |
|
| 915 | 914 | } |
|
| 916 | 915 | ||
| 917 | 916 | /// Test parsing a record declaration with field initializers. |
|
| 918 | 917 | @test fn testParseRecordDeclFieldDefaults() throws (testing::TestError) { |
|
| 919 | 918 | let node = try! parseStmtStr("record Config { size: opaque = 42, flag: bool = true }"); |
| 922 | 921 | ||
| 923 | 922 | try expectIdent(decl.name, "Config"); |
|
| 924 | 923 | try testing::expect(decl.derives.len == 0); |
|
| 925 | 924 | try testing::expect(decl.fields.len == 2); |
|
| 926 | 925 | ||
| 927 | - | try expectFieldSig(&decl.fields, 0, "size", ast::TypeSig::Opaque); |
|
| 928 | - | try expectFieldSig(&decl.fields, 1, "flag", ast::TypeSig::Bool); |
|
| 926 | + | try expectFieldSig(decl.fields, 0, "size", ast::TypeSig::Opaque); |
|
| 927 | + | try expectFieldSig(decl.fields, 1, "flag", ast::TypeSig::Bool); |
|
| 929 | 928 | ||
| 930 | 929 | // Check default values. |
|
| 931 | - | let case ast::NodeValue::RecordField { value: val0, .. } = decl.fields.list[0].value |
|
| 930 | + | let case ast::NodeValue::RecordField { value: val0, .. } = decl.fields[0].value |
|
| 932 | 931 | else throw testing::TestError::Failed; |
|
| 933 | 932 | let v0 = val0 else throw testing::TestError::Failed; |
|
| 934 | 933 | try expectNumber(v0, "42"); |
|
| 935 | 934 | ||
| 936 | - | let case ast::NodeValue::RecordField { value: val1, .. } = decl.fields.list[1].value |
|
| 935 | + | let case ast::NodeValue::RecordField { value: val1, .. } = decl.fields[1].value |
|
| 937 | 936 | else throw testing::TestError::Failed; |
|
| 938 | 937 | let v1 = val1 else throw testing::TestError::Failed; |
|
| 939 | 938 | let case ast::NodeValue::Bool(flagValue) = v1.value |
|
| 940 | 939 | else throw testing::TestError::Failed; |
|
| 941 | 940 | try testing::expect(flagValue); |
| 950 | 949 | try expectIdent(decl.name, "Pair"); |
|
| 951 | 950 | try testing::expect(not decl.labeled); |
|
| 952 | 951 | try testing::expect(decl.derives.len == 0); |
|
| 953 | 952 | try testing::expect(decl.fields.len == 2); |
|
| 954 | 953 | ||
| 955 | - | try expectFieldSig(&decl.fields, 0, nil, ast::TypeSig::Void); |
|
| 956 | - | try expectFieldSig(&decl.fields, 1, nil, ast::TypeSig::Bool); |
|
| 954 | + | try expectFieldSig(decl.fields, 0, nil, ast::TypeSig::Void); |
|
| 955 | + | try expectFieldSig(decl.fields, 1, nil, ast::TypeSig::Bool); |
|
| 957 | 956 | } |
|
| 958 | 957 | ||
| 959 | 958 | /// Test parsing a single-field unlabeled record. |
|
| 960 | 959 | @test fn testParseTupleRecordSingleField() throws (testing::TestError) { |
|
| 961 | 960 | let node = try! parseStmtStr("record R(bool);"); |
| 964 | 963 | ||
| 965 | 964 | try expectIdent(decl.name, "R"); |
|
| 966 | 965 | try testing::expect(not decl.labeled); |
|
| 967 | 966 | try testing::expect(decl.fields.len == 1); |
|
| 968 | 967 | ||
| 969 | - | try expectFieldSig(&decl.fields, 0, nil, ast::TypeSig::Bool); |
|
| 968 | + | try expectFieldSig(decl.fields, 0, nil, ast::TypeSig::Bool); |
|
| 970 | 969 | } |
|
| 971 | 970 | ||
| 972 | 971 | /// Test parsing empty record literals. |
|
| 973 | 972 | @test fn testParseEmptyRecordLiteral() throws (testing::TestError) { |
|
| 974 | 973 | let r1 = try! parseExprStr("{}"); |
| 995 | 994 | let case ast::TypeSig::Fn(sig) = sigValue |
|
| 996 | 995 | else throw testing::TestError::Failed; |
|
| 997 | 996 | ||
| 998 | 997 | try testing::expect(sig.params.len == 2); |
|
| 999 | 998 | ||
| 1000 | - | let param0 = sig.params.list[0]; |
|
| 999 | + | let param0 = sig.params[0]; |
|
| 1001 | 1000 | try expectIntType(param0, 4, ast::Signedness::Signed); |
|
| 1002 | 1001 | ||
| 1003 | - | let param1 = sig.params.list[1]; |
|
| 1002 | + | let param1 = sig.params[1]; |
|
| 1004 | 1003 | let case ast::NodeValue::TypeSig(p1) = param1.value |
|
| 1005 | 1004 | else throw testing::TestError::Failed; |
|
| 1006 | 1005 | let case ast::TypeSig::Pointer { valueType: ptrTarget, mutable: _ } = p1 |
|
| 1007 | 1006 | else throw testing::TestError::Failed; |
|
| 1008 | 1007 | try expectIntType(ptrTarget, 1, ast::Signedness::Unsigned); |
| 1017 | 1016 | else throw testing::TestError::Failed; |
|
| 1018 | 1017 | let case ast::TypeSig::Fn(sig) = sigValue |
|
| 1019 | 1018 | else throw testing::TestError::Failed; |
|
| 1020 | 1019 | ||
| 1021 | 1020 | try testing::expect(sig.params.len == 1); |
|
| 1022 | - | try expectIntType(sig.params.list[0], 4, ast::Signedness::Signed); |
|
| 1021 | + | try expectIntType(sig.params[0], 4, ast::Signedness::Signed); |
|
| 1023 | 1022 | ||
| 1024 | 1023 | try testing::expect(sig.throwList.len == 2); |
|
| 1025 | - | try expectTypeIdent(sig.throwList.list[0], "Error"); |
|
| 1026 | - | try expectTypeIdent(sig.throwList.list[1], "Other"); |
|
| 1024 | + | try expectTypeIdent(sig.throwList[0], "Error"); |
|
| 1025 | + | try expectTypeIdent(sig.throwList[1], "Other"); |
|
| 1027 | 1026 | ||
| 1028 | 1027 | let returnType = sig.returnType |
|
| 1029 | 1028 | else throw testing::TestError::Failed; |
|
| 1030 | 1029 | try expectType(returnType, ast::TypeSig::Bool); |
|
| 1031 | 1030 | } |
| 1054 | 1053 | ||
| 1055 | 1054 | try expectIdent(decl.name, "add"); |
|
| 1056 | 1055 | try testing::expect(decl.sig.params.len == 2); |
|
| 1057 | 1056 | ||
| 1058 | 1057 | { |
|
| 1059 | - | let param0 = decl.sig.params.list[0]; |
|
| 1058 | + | let param0 = decl.sig.params[0]; |
|
| 1060 | 1059 | let case ast::NodeValue::FnParam(p0) = param0.value |
|
| 1061 | 1060 | else throw testing::TestError::Failed; |
|
| 1062 | 1061 | try expectIdent(p0.name, "x"); |
|
| 1063 | 1062 | try expectIntType(p0.type, 4, ast::Signedness::Signed); |
|
| 1064 | 1063 | } |
|
| 1065 | 1064 | { |
|
| 1066 | - | let param1 = decl.sig.params.list[1]; |
|
| 1065 | + | let param1 = decl.sig.params[1]; |
|
| 1067 | 1066 | let case ast::NodeValue::FnParam(p1) = param1.value |
|
| 1068 | 1067 | else throw testing::TestError::Failed; |
|
| 1069 | 1068 | try expectIdent(p1.name, "y"); |
|
| 1070 | 1069 | let case ast::NodeValue::TypeSig(sig1) = p1.type.value |
|
| 1071 | 1070 | else throw testing::TestError::Failed; |
| 1095 | 1094 | let returnType = decl.sig.returnType |
|
| 1096 | 1095 | else throw testing::TestError::Failed; |
|
| 1097 | 1096 | try expectType(returnType, ast::TypeSig::Void); |
|
| 1098 | 1097 | ||
| 1099 | 1098 | try testing::expect(decl.sig.throwList.len == 2); |
|
| 1100 | - | try expectTypeIdent(decl.sig.throwList.list[0], "Error"); |
|
| 1101 | - | try expectTypeIdent(decl.sig.throwList.list[1], "Crash"); |
|
| 1099 | + | try expectTypeIdent(decl.sig.throwList[0], "Error"); |
|
| 1100 | + | try expectTypeIdent(decl.sig.throwList[1], "Crash"); |
|
| 1102 | 1101 | ||
| 1103 | 1102 | let body = decl.body else throw testing::TestError::Failed; |
|
| 1104 | 1103 | let case ast::NodeValue::Block(_) = body.value |
|
| 1105 | 1104 | else throw testing::TestError::Failed; |
|
| 1106 | 1105 | } |
| 1114 | 1113 | try expectIdent(decl.name, "run"); |
|
| 1115 | 1114 | ||
| 1116 | 1115 | let attrs = decl.attrs |
|
| 1117 | 1116 | else throw testing::TestError::Failed; |
|
| 1118 | 1117 | ||
| 1119 | - | try testing::expect(ast::attributesLen(&attrs) == 2); |
|
| 1118 | + | try testing::expect(attrs.list.len == 2); |
|
| 1120 | 1119 | ||
| 1121 | - | let first = ast::attributesGet(&attrs, 0); |
|
| 1122 | - | let case ast::NodeValue::Attribute(attr0) = first.value |
|
| 1120 | + | let case ast::NodeValue::Attribute(attr0) = attrs.list[0].value |
|
| 1123 | 1121 | else throw testing::TestError::Failed; |
|
| 1124 | 1122 | try testing::expect(attr0 == ast::Attribute::Pub); |
|
| 1125 | 1123 | ||
| 1126 | - | let second = ast::attributesGet(&attrs, 1); |
|
| 1127 | - | let case ast::NodeValue::Attribute(attr1) = second.value |
|
| 1124 | + | let case ast::NodeValue::Attribute(attr1) = attrs.list[1].value |
|
| 1128 | 1125 | else throw testing::TestError::Failed; |
|
| 1129 | 1126 | try testing::expect(attr1 == ast::Attribute::Extern); |
|
| 1130 | 1127 | ||
| 1131 | 1128 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Pub)); |
|
| 1132 | 1129 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
| 1367 | 1364 | else throw testing::TestError::Failed; |
|
| 1368 | 1365 | ||
| 1369 | 1366 | try expectIdent(node.expr, "value"); |
|
| 1370 | 1367 | try testing::expect(node.catches.len == 1); |
|
| 1371 | 1368 | ||
| 1372 | - | let case ast::NodeValue::CatchClause(clause) = node.catches.list[0].value |
|
| 1369 | + | let case ast::NodeValue::CatchClause(clause) = node.catches[0].value |
|
| 1373 | 1370 | else throw testing::TestError::Failed; |
|
| 1374 | 1371 | try testing::expect(clause.binding == nil); |
|
| 1375 | 1372 | try testing::expect(clause.typeNode == nil); |
|
| 1376 | 1373 | try expectBlockExprStmt(clause.body, ast::NodeValue::Ident("alt")); |
|
| 1377 | 1374 | } |
| 1465 | 1462 | else throw testing::TestError::Failed; |
|
| 1466 | 1463 | ||
| 1467 | 1464 | try expectIdent(sw.subject, "subject"); |
|
| 1468 | 1465 | try testing::expect(sw.prongs.len == 1); |
|
| 1469 | 1466 | ||
| 1470 | - | let caseNode = sw.prongs.list[0]; |
|
| 1467 | + | let caseNode = sw.prongs[0]; |
|
| 1471 | 1468 | let case ast::NodeValue::MatchProng(prong) = caseNode.value |
|
| 1472 | 1469 | else throw testing::TestError::Failed; |
|
| 1473 | 1470 | ||
| 1474 | 1471 | let case ast::ProngArm::Case(patterns) = prong.arm |
|
| 1475 | 1472 | else throw testing::TestError::Failed; |
|
| 1476 | 1473 | try testing::expect(patterns.len == 1); |
|
| 1477 | - | try expectIdent(patterns.list[0], "pattern"); |
|
| 1474 | + | try expectIdent(patterns[0], "pattern"); |
|
| 1478 | 1475 | try testing::expect(prong.guard == nil); |
|
| 1479 | 1476 | ||
| 1480 | 1477 | let case ast::NodeValue::ExprStmt(bodyStmt) = prong.body.value |
|
| 1481 | 1478 | else throw testing::TestError::Failed; |
|
| 1482 | 1479 | let case ast::NodeValue::Ident(name) = bodyStmt.value |
| 1491 | 1488 | ); |
|
| 1492 | 1489 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1493 | 1490 | else throw testing::TestError::Failed; |
|
| 1494 | 1491 | ||
| 1495 | 1492 | try testing::expect(sw.prongs.len == 1); |
|
| 1496 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1493 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1497 | 1494 | else throw testing::TestError::Failed; |
|
| 1498 | 1495 | ||
| 1499 | 1496 | let case ast::ProngArm::Case(patterns) = prong.arm |
|
| 1500 | 1497 | else throw testing::TestError::Failed; |
|
| 1501 | 1498 | try testing::expect(patterns.len == 2); |
|
| 1502 | - | try expectIdent(patterns.list[0], "left"); |
|
| 1503 | - | try expectIdent(patterns.list[1], "right"); |
|
| 1499 | + | try expectIdent(patterns[0], "left"); |
|
| 1500 | + | try expectIdent(patterns[1], "right"); |
|
| 1504 | 1501 | ||
| 1505 | 1502 | let guard = prong.guard |
|
| 1506 | 1503 | else throw testing::TestError::Failed; |
|
| 1507 | 1504 | try expectIdent(guard, "cond"); |
|
| 1508 | 1505 | } |
| 1515 | 1512 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1516 | 1513 | else throw testing::TestError::Failed; |
|
| 1517 | 1514 | ||
| 1518 | 1515 | try testing::expect(sw.prongs.len == 2); |
|
| 1519 | 1516 | ||
| 1520 | - | let firstCaseNode = sw.prongs.list[0]; |
|
| 1517 | + | let firstCaseNode = sw.prongs[0]; |
|
| 1521 | 1518 | let case ast::NodeValue::MatchProng(firstProng) = firstCaseNode.value |
|
| 1522 | 1519 | else throw testing::TestError::Failed; |
|
| 1523 | 1520 | let case ast::NodeValue::Return(firstRetVal) = firstProng.body.value |
|
| 1524 | 1521 | else throw testing::TestError::Failed; |
|
| 1525 | 1522 | try testing::expect(firstRetVal == nil); |
|
| 1526 | 1523 | ||
| 1527 | - | let secondCaseNode = sw.prongs.list[1]; |
|
| 1524 | + | let secondCaseNode = sw.prongs[1]; |
|
| 1528 | 1525 | let case ast::NodeValue::MatchProng(secondProng) = secondCaseNode.value |
|
| 1529 | 1526 | else throw testing::TestError::Failed; |
|
| 1530 | 1527 | let case ast::NodeValue::Return(secondRetVal) = secondProng.body.value |
|
| 1531 | 1528 | else throw testing::TestError::Failed; |
|
| 1532 | 1529 | try testing::expect(secondRetVal == nil); |
| 1540 | 1537 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1541 | 1538 | else throw testing::TestError::Failed; |
|
| 1542 | 1539 | ||
| 1543 | 1540 | try testing::expect(sw.prongs.len == 2); |
|
| 1544 | 1541 | { |
|
| 1545 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1542 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1546 | 1543 | else throw testing::TestError::Failed; |
|
| 1547 | 1544 | let case ast::ProngArm::Case(patterns) = prong.arm |
|
| 1548 | 1545 | else throw testing::TestError::Failed; |
|
| 1549 | 1546 | try testing::expect(patterns.len == 1); |
|
| 1550 | - | try expectIdent(patterns.list[0], "First"); |
|
| 1547 | + | try expectIdent(patterns[0], "First"); |
|
| 1551 | 1548 | let case ast::NodeValue::ExprStmt(stmt) = prong.body.value |
|
| 1552 | 1549 | else throw testing::TestError::Failed; |
|
| 1553 | 1550 | let case ast::NodeValue::Ident(val) = stmt.value |
|
| 1554 | 1551 | if mem::eq(val, "first") |
|
| 1555 | 1552 | else throw testing::TestError::Failed; |
|
| 1556 | 1553 | } { |
|
| 1557 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[1].value |
|
| 1554 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[1].value |
|
| 1558 | 1555 | else throw testing::TestError::Failed; |
|
| 1559 | 1556 | let case ast::ProngArm::Case(pats) = prong.arm |
|
| 1560 | 1557 | else throw testing::TestError::Failed; |
|
| 1561 | 1558 | ||
| 1562 | 1559 | try testing::expect(pats.len == 1); |
|
| 1563 | - | try expectIdent(pats.list[0], "Second"); |
|
| 1560 | + | try expectIdent(pats[0], "Second"); |
|
| 1564 | 1561 | ||
| 1565 | 1562 | let case ast::NodeValue::ExprStmt(stmt) = prong.body.value |
|
| 1566 | 1563 | else throw testing::TestError::Failed; |
|
| 1567 | 1564 | let case ast::NodeValue::Ident(val) = stmt.value |
|
| 1568 | 1565 | if mem::eq(val, "second") |
| 1575 | 1572 | let root = try! parseStmtStr("match subject { case Pattern => { body; } }"); |
|
| 1576 | 1573 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1577 | 1574 | else throw testing::TestError::Failed; |
|
| 1578 | 1575 | ||
| 1579 | 1576 | try testing::expect(sw.prongs.len == 1); |
|
| 1580 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1577 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1581 | 1578 | else throw testing::TestError::Failed; |
|
| 1582 | 1579 | try expectBlockExprStmt(prong.body, ast::NodeValue::Ident("body")); |
|
| 1583 | 1580 | } |
|
| 1584 | 1581 | ||
| 1585 | 1582 | /// Test parsing a `match` statement with an `else` case. |
| 1587 | 1584 | let root = try! parseStmtStr("match subject { else => body }"); |
|
| 1588 | 1585 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1589 | 1586 | else throw testing::TestError::Failed; |
|
| 1590 | 1587 | ||
| 1591 | 1588 | try testing::expect(sw.prongs.len == 1); |
|
| 1592 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1589 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1593 | 1590 | else throw testing::TestError::Failed; |
|
| 1594 | 1591 | // Else prong has no pattern. |
|
| 1595 | 1592 | let case ast::ProngArm::Else = prong.arm |
|
| 1596 | 1593 | else throw testing::TestError::Failed; |
|
| 1597 | 1594 | try testing::expect(prong.guard == nil); |
| 1608 | 1605 | let root = try! parseStmtStr("match subject { x => body }"); |
|
| 1609 | 1606 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1610 | 1607 | else throw testing::TestError::Failed; |
|
| 1611 | 1608 | ||
| 1612 | 1609 | try testing::expect(sw.prongs.len == 1); |
|
| 1613 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1610 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1614 | 1611 | else throw testing::TestError::Failed; |
|
| 1615 | 1612 | // Binding prong has single identifier pattern. |
|
| 1616 | 1613 | let case ast::ProngArm::Binding(pat) = prong.arm |
|
| 1617 | 1614 | else throw testing::TestError::Failed; |
|
| 1618 | 1615 | try expectIdent(pat, "x"); |
| 1628 | 1625 | let root = try! parseStmtStr("match subject { x if x > 0 => body }"); |
|
| 1629 | 1626 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1630 | 1627 | else throw testing::TestError::Failed; |
|
| 1631 | 1628 | ||
| 1632 | 1629 | try testing::expect(sw.prongs.len == 1); |
|
| 1633 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1630 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1634 | 1631 | else throw testing::TestError::Failed; |
|
| 1635 | 1632 | // Binding prong has single identifier pattern. |
|
| 1636 | 1633 | let case ast::ProngArm::Binding(pat) = prong.arm |
|
| 1637 | 1634 | else throw testing::TestError::Failed; |
|
| 1638 | 1635 | try expectIdent(pat, "x"); |
| 1648 | 1645 | let root = try! parseStmtStr("match subject { _ => body }"); |
|
| 1649 | 1646 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1650 | 1647 | else throw testing::TestError::Failed; |
|
| 1651 | 1648 | ||
| 1652 | 1649 | try testing::expect(sw.prongs.len == 1); |
|
| 1653 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1650 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1654 | 1651 | else throw testing::TestError::Failed; |
|
| 1655 | 1652 | // Wildcard binding prong has single placeholder pattern. |
|
| 1656 | 1653 | let case ast::ProngArm::Binding(pat) = prong.arm |
|
| 1657 | 1654 | else throw testing::TestError::Failed; |
|
| 1658 | 1655 | let case ast::NodeValue::Placeholder = pat.value |
| 1669 | 1666 | let root = try! parseStmtStr("match subject { _ if cond => body }"); |
|
| 1670 | 1667 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1671 | 1668 | else throw testing::TestError::Failed; |
|
| 1672 | 1669 | ||
| 1673 | 1670 | try testing::expect(sw.prongs.len == 1); |
|
| 1674 | - | let case ast::NodeValue::MatchProng(prong) = sw.prongs.list[0].value |
|
| 1671 | + | let case ast::NodeValue::MatchProng(prong) = sw.prongs[0].value |
|
| 1675 | 1672 | else throw testing::TestError::Failed; |
|
| 1676 | 1673 | // Guarded wildcard binding prong has single placeholder pattern. |
|
| 1677 | 1674 | let case ast::ProngArm::Binding(pat) = prong.arm |
|
| 1678 | 1675 | else throw testing::TestError::Failed; |
|
| 1679 | 1676 | let case ast::NodeValue::Placeholder = pat.value |
| 1865 | 1862 | let case ast::NodeValue::Call(call) = root.value |
|
| 1866 | 1863 | else throw testing::TestError::Failed; |
|
| 1867 | 1864 | ||
| 1868 | 1865 | try expectIdent(call.callee, "func"); |
|
| 1869 | 1866 | try testing::expect(call.args.len == 2); |
|
| 1870 | - | try expectIdent(call.args.list[0], "x"); |
|
| 1871 | - | try expectIdent(call.args.list[1], "y"); |
|
| 1867 | + | try expectIdent(call.args[0], "x"); |
|
| 1868 | + | try expectIdent(call.args[1], "y"); |
|
| 1872 | 1869 | } |
|
| 1873 | 1870 | ||
| 1874 | 1871 | /// Test parsing chained postfix operators. |
|
| 1875 | 1872 | @test fn testParseChainedPostfix() throws (testing::TestError) { |
|
| 1876 | 1873 | let root = try! parseExprStr("obj.method(arg)[0]"); |
| 1884 | 1881 | else throw testing::TestError::Failed; |
|
| 1885 | 1882 | ||
| 1886 | 1883 | try expectIdent(fieldAccess.parent, "obj"); |
|
| 1887 | 1884 | try expectIdent(fieldAccess.child, "method"); |
|
| 1888 | 1885 | try testing::expect(call.args.len == 1); |
|
| 1889 | - | try expectIdent(call.args.list[0], "arg"); |
|
| 1886 | + | try expectIdent(call.args[0], "arg"); |
|
| 1890 | 1887 | } |
|
| 1891 | 1888 | ||
| 1892 | 1889 | /// Test parsing a literal cast using `as`. |
|
| 1893 | 1890 | @test fn testParseAsCastLiteral() throws (testing::TestError) { |
|
| 1894 | 1891 | let root = try! parseExprStr("1 as i32"); |
| 1914 | 1911 | else throw testing::TestError::Failed; |
|
| 1915 | 1912 | ||
| 1916 | 1913 | try expectIdent(access.parent, "value"); |
|
| 1917 | 1914 | try expectIdent(access.child, "method"); |
|
| 1918 | 1915 | try testing::expect(call.args.len == 1); |
|
| 1919 | - | try expectIdent(call.args.list[0], "arg"); |
|
| 1916 | + | try expectIdent(call.args[0], "arg"); |
|
| 1920 | 1917 | try expectIntType(asExpr.type, 4, ast::Signedness::Unsigned); |
|
| 1921 | 1918 | } |
|
| 1922 | 1919 | ||
| 1923 | 1920 | /// Test parsing @sizeOf builtin. |
|
| 1924 | 1921 | @test fn testParseBuiltinSizeOf() throws (testing::TestError) { |
| 1926 | 1923 | let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value |
|
| 1927 | 1924 | else throw testing::TestError::Failed; |
|
| 1928 | 1925 | ||
| 1929 | 1926 | try testing::expect(builtinKind == ast::Builtin::SizeOf); |
|
| 1930 | 1927 | try testing::expect(builtinArgs.len == 1); |
|
| 1931 | - | try expectType(builtinArgs.list[0], ast::TypeSig::Integer { |
|
| 1928 | + | try expectType(builtinArgs[0], ast::TypeSig::Integer { |
|
| 1932 | 1929 | width: 4, |
|
| 1933 | 1930 | sign: ast::Signedness::Signed, |
|
| 1934 | 1931 | }); |
|
| 1935 | 1932 | } |
|
| 1936 | 1933 |
| 1940 | 1937 | let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value |
|
| 1941 | 1938 | else throw testing::TestError::Failed; |
|
| 1942 | 1939 | ||
| 1943 | 1940 | try testing::expect(builtinKind == ast::Builtin::AlignOf); |
|
| 1944 | 1941 | try testing::expect(builtinArgs.len == 1); |
|
| 1945 | - | try expectType(builtinArgs.list[0], ast::TypeSig::Integer { |
|
| 1942 | + | try expectType(builtinArgs[0], ast::TypeSig::Integer { |
|
| 1946 | 1943 | width: 4, |
|
| 1947 | 1944 | sign: ast::Signedness::Signed, |
|
| 1948 | 1945 | }); |
|
| 1949 | 1946 | } |
|
| 1950 | 1947 |
| 2362 | 2359 | try testing::expect(decl.derives.len == 0); |
|
| 2363 | 2360 | ||
| 2364 | 2361 | let variants = decl.variants; |
|
| 2365 | 2362 | try testing::expect(variants.len == 3); |
|
| 2366 | 2363 | ||
| 2367 | - | let v0 = variants.list[0]; |
|
| 2364 | + | let v0 = variants[0]; |
|
| 2368 | 2365 | let case ast::NodeValue::UnionDeclVariant(var0) = v0.value |
|
| 2369 | 2366 | else throw testing::TestError::Failed; |
|
| 2370 | 2367 | try expectIdent(var0.name, "Red"); |
|
| 2371 | 2368 | try testing::expect(var0.index == 0); |
|
| 2372 | 2369 | try testing::expect(var0.type == nil); |
|
| 2373 | 2370 | try testing::expect(var0.value == nil); |
|
| 2374 | 2371 | ||
| 2375 | - | let v1 = variants.list[1]; |
|
| 2372 | + | let v1 = variants[1]; |
|
| 2376 | 2373 | let case ast::NodeValue::UnionDeclVariant(var1) = v1.value |
|
| 2377 | 2374 | else throw testing::TestError::Failed; |
|
| 2378 | 2375 | try expectIdent(var1.name, "Green"); |
|
| 2379 | 2376 | try testing::expect(var1.index == 1); |
|
| 2380 | 2377 | try testing::expect(var1.type == nil); |
|
| 2381 | 2378 | try testing::expect(var1.value == nil); |
|
| 2382 | 2379 | ||
| 2383 | - | let v2 = variants.list[2]; |
|
| 2380 | + | let v2 = variants[2]; |
|
| 2384 | 2381 | let case ast::NodeValue::UnionDeclVariant(var2) = v2.value |
|
| 2385 | 2382 | else throw testing::TestError::Failed; |
|
| 2386 | 2383 | try expectIdent(var2.name, "Blue"); |
|
| 2387 | 2384 | try testing::expect(var2.index == 2); |
|
| 2388 | 2385 | try testing::expect(var2.type == nil); |
| 2408 | 2405 | try expectIdent(decl.name, "Status"); |
|
| 2409 | 2406 | ||
| 2410 | 2407 | let variants = decl.variants; |
|
| 2411 | 2408 | try testing::expect(variants.len == 3); |
|
| 2412 | 2409 | ||
| 2413 | - | let v0 = variants.list[0]; |
|
| 2410 | + | let v0 = variants[0]; |
|
| 2414 | 2411 | let case ast::NodeValue::UnionDeclVariant(var0) = v0.value |
|
| 2415 | 2412 | else throw testing::TestError::Failed; |
|
| 2416 | 2413 | try expectIdent(var0.name, "Ok"); |
|
| 2417 | 2414 | try testing::expect(var0.index == 0); |
|
| 2418 | 2415 | try testing::expect(var0.type == nil); |
|
| 2419 | 2416 | try testing::expect(var0.value != nil); |
|
| 2420 | 2417 | ||
| 2421 | - | let v1 = variants.list[1]; |
|
| 2418 | + | let v1 = variants[1]; |
|
| 2422 | 2419 | let case ast::NodeValue::UnionDeclVariant(var1) = v1.value |
|
| 2423 | 2420 | else throw testing::TestError::Failed; |
|
| 2424 | 2421 | try expectIdent(var1.name, "Error"); |
|
| 2425 | 2422 | try testing::expect(var1.index == 1); |
|
| 2426 | 2423 | try testing::expect(var1.value != nil); |
|
| 2427 | 2424 | ||
| 2428 | - | let v2 = variants.list[2]; |
|
| 2425 | + | let v2 = variants[2]; |
|
| 2429 | 2426 | let case ast::NodeValue::UnionDeclVariant(var2) = v2.value |
|
| 2430 | 2427 | else throw testing::TestError::Failed; |
|
| 2431 | 2428 | try expectIdent(var2.name, "Pending"); |
|
| 2432 | 2429 | try testing::expect(var2.index == 2); |
|
| 2433 | 2430 | try testing::expect(var2.value != nil); |
| 2440 | 2437 | else throw testing::TestError::Failed; |
|
| 2441 | 2438 | ||
| 2442 | 2439 | try expectIdent(decl.name, "Result"); |
|
| 2443 | 2440 | try testing::expect(decl.variants.len == 2); |
|
| 2444 | 2441 | ||
| 2445 | - | let okFields = try expectVariant(decl.variants.list[0], "Ok", 0); |
|
| 2442 | + | let okFields = try expectVariant(decl.variants[0], "Ok", 0); |
|
| 2446 | 2443 | try testing::expect(okFields.len == 1); |
|
| 2447 | 2444 | try expectFieldSig(okFields, 0, nil, ast::TypeSig::Bool); |
|
| 2448 | 2445 | ||
| 2449 | - | let errFields = try expectVariant(decl.variants.list[1], "Error", 1); |
|
| 2446 | + | let errFields = try expectVariant(decl.variants[1], "Error", 1); |
|
| 2450 | 2447 | try testing::expect(errFields.len == 1); |
|
| 2451 | 2448 | try expectFieldSig(errFields, 0, nil, ast::TypeSig::Void); |
|
| 2452 | 2449 | } |
|
| 2453 | 2450 | ||
| 2454 | 2451 | /// Test parsing a union with derives. |
| 2457 | 2454 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2458 | 2455 | else throw testing::TestError::Failed; |
|
| 2459 | 2456 | ||
| 2460 | 2457 | try expectIdent(decl.name, "Option"); |
|
| 2461 | 2458 | try testing::expect(decl.derives.len == 2); |
|
| 2462 | - | try expectIdent(decl.derives.list[0], "Debug"); |
|
| 2463 | - | try expectIdent(decl.derives.list[1], "Eq"); |
|
| 2459 | + | try expectIdent(decl.derives[0], "Debug"); |
|
| 2460 | + | try expectIdent(decl.derives[1], "Eq"); |
|
| 2464 | 2461 | ||
| 2465 | 2462 | let variants = decl.variants; |
|
| 2466 | 2463 | try testing::expect(variants.len == 2); |
|
| 2467 | 2464 | ||
| 2468 | - | let v0 = variants.list[0]; |
|
| 2465 | + | let v0 = variants[0]; |
|
| 2469 | 2466 | let case ast::NodeValue::UnionDeclVariant(var0) = v0.value |
|
| 2470 | 2467 | else throw testing::TestError::Failed; |
|
| 2471 | 2468 | try expectIdent(var0.name, "None"); |
|
| 2472 | 2469 | try testing::expect(var0.type == nil); |
|
| 2473 | 2470 | ||
| 2474 | - | let v1 = variants.list[1]; |
|
| 2471 | + | let v1 = variants[1]; |
|
| 2475 | 2472 | let case ast::NodeValue::UnionDeclVariant(var1) = v1.value |
|
| 2476 | 2473 | else throw testing::TestError::Failed; |
|
| 2477 | 2474 | try expectIdent(var1.name, "Some"); |
|
| 2478 | 2475 | try testing::expect(var1.type != nil); |
|
| 2479 | 2476 | } |
| 2486 | 2483 | ||
| 2487 | 2484 | let typeName = lit.typeName else throw testing::TestError::Failed; |
|
| 2488 | 2485 | try expectIdent(typeName, "Point"); |
|
| 2489 | 2486 | try testing::expect(lit.fields.len == 2); |
|
| 2490 | 2487 | ||
| 2491 | - | let field0 = lit.fields.list[0]; |
|
| 2488 | + | let field0 = lit.fields[0]; |
|
| 2492 | 2489 | let case ast::NodeValue::RecordLitField(arg0) = field0.value |
|
| 2493 | 2490 | else throw testing::TestError::Failed; |
|
| 2494 | 2491 | let label0 = arg0.label else throw testing::TestError::Failed; |
|
| 2495 | 2492 | try expectIdent(label0, "x"); |
|
| 2496 | 2493 | try expectNumber(arg0.value, "5"); |
| 2503 | 2500 | else throw testing::TestError::Failed; |
|
| 2504 | 2501 | ||
| 2505 | 2502 | try testing::expect(lit.typeName == nil); |
|
| 2506 | 2503 | try testing::expect(lit.fields.len == 2); |
|
| 2507 | 2504 | ||
| 2508 | - | let field0 = lit.fields.list[0]; |
|
| 2505 | + | let field0 = lit.fields[0]; |
|
| 2509 | 2506 | let case ast::NodeValue::RecordLitField(arg0) = field0.value |
|
| 2510 | 2507 | else throw testing::TestError::Failed; |
|
| 2511 | 2508 | let label0 = arg0.label else throw testing::TestError::Failed; |
|
| 2512 | 2509 | try expectIdent(label0, "x"); |
|
| 2513 | 2510 | try expectNumber(arg0.value, "10"); |
|
| 2514 | 2511 | ||
| 2515 | - | let field1 = lit.fields.list[1]; |
|
| 2512 | + | let field1 = lit.fields[1]; |
|
| 2516 | 2513 | let case ast::NodeValue::RecordLitField(arg1) = field1.value |
|
| 2517 | 2514 | else throw testing::TestError::Failed; |
|
| 2518 | 2515 | let label1 = arg1.label else throw testing::TestError::Failed; |
|
| 2519 | 2516 | try expectIdent(label1, "y"); |
|
| 2520 | 2517 | try expectNumber(arg1.value, "20"); |
| 2530 | 2527 | let typeName = lit.typeName else throw testing::TestError::Failed; |
|
| 2531 | 2528 | try expectIdent(typeName, "Point"); |
|
| 2532 | 2529 | try testing::expect(lit.fields.len == 2); |
|
| 2533 | 2530 | ||
| 2534 | 2531 | // First field: shorthand `x`. |
|
| 2535 | - | let field0 = lit.fields.list[0]; |
|
| 2532 | + | let field0 = lit.fields[0]; |
|
| 2536 | 2533 | let case ast::NodeValue::RecordLitField(arg0) = field0.value |
|
| 2537 | 2534 | else throw testing::TestError::Failed; |
|
| 2538 | 2535 | let label0 = arg0.label else throw testing::TestError::Failed; |
|
| 2539 | 2536 | try expectIdent(label0, "x"); |
|
| 2540 | 2537 | try expectIdent(arg0.value, "x"); |
|
| 2541 | 2538 | // Label and value should point to the same node. |
|
| 2542 | 2539 | try testing::expect(label0 == arg0.value); |
|
| 2543 | 2540 | ||
| 2544 | 2541 | // Second field: shorthand `y`. |
|
| 2545 | - | let field1 = lit.fields.list[1]; |
|
| 2542 | + | let field1 = lit.fields[1]; |
|
| 2546 | 2543 | let case ast::NodeValue::RecordLitField(arg1) = field1.value |
|
| 2547 | 2544 | else throw testing::TestError::Failed; |
|
| 2548 | 2545 | let label1 = arg1.label else throw testing::TestError::Failed; |
|
| 2549 | 2546 | try expectIdent(label1, "y"); |
|
| 2550 | 2547 | try expectIdent(arg1.value, "y"); |
| 2558 | 2555 | else throw testing::TestError::Failed; |
|
| 2559 | 2556 | ||
| 2560 | 2557 | try testing::expect(lit.fields.len == 2); |
|
| 2561 | 2558 | ||
| 2562 | 2559 | // First field: shorthand `x`. |
|
| 2563 | - | let field0 = lit.fields.list[0]; |
|
| 2560 | + | let field0 = lit.fields[0]; |
|
| 2564 | 2561 | let case ast::NodeValue::RecordLitField(arg0) = field0.value |
|
| 2565 | 2562 | else throw testing::TestError::Failed; |
|
| 2566 | 2563 | let label0 = arg0.label else throw testing::TestError::Failed; |
|
| 2567 | 2564 | try expectIdent(label0, "x"); |
|
| 2568 | 2565 | try expectIdent(arg0.value, "x"); |
|
| 2569 | 2566 | ||
| 2570 | 2567 | // Second field: explicit `y: 10`. |
|
| 2571 | - | let field1 = lit.fields.list[1]; |
|
| 2568 | + | let field1 = lit.fields[1]; |
|
| 2572 | 2569 | let case ast::NodeValue::RecordLitField(arg1) = field1.value |
|
| 2573 | 2570 | else throw testing::TestError::Failed; |
|
| 2574 | 2571 | let label1 = arg1.label else throw testing::TestError::Failed; |
|
| 2575 | 2572 | try expectIdent(label1, "y"); |
|
| 2576 | 2573 | try expectNumber(arg1.value, "10"); |
| 2599 | 2596 | ||
| 2600 | 2597 | let case ast::NodeValue::Block(module) = r.value |
|
| 2601 | 2598 | else throw testing::TestError::Failed; |
|
| 2602 | 2599 | try testing::expect(module.statements.len == 2); |
|
| 2603 | 2600 | ||
| 2604 | - | let first = module.statements.list[0]; |
|
| 2601 | + | let first = module.statements[0]; |
|
| 2605 | 2602 | let case ast::NodeValue::FnDecl(fDecl) = first.value |
|
| 2606 | 2603 | else throw testing::TestError::Failed; |
|
| 2607 | 2604 | try expectIdent(fDecl.name, "f"); |
|
| 2608 | 2605 | ||
| 2609 | - | let second = module.statements.list[1]; |
|
| 2606 | + | let second = module.statements[1]; |
|
| 2610 | 2607 | let case ast::NodeValue::FnDecl(gDecl) = second.value |
|
| 2611 | 2608 | else throw testing::TestError::Failed; |
|
| 2612 | 2609 | try expectIdent(gDecl.name, "g"); |
|
| 2613 | 2610 | } |
|
| 2614 | 2611 |
lib/std/lang/resolver.rad
+270 -346
| 48 | 48 | /// Trait definition stored in the resolver. |
|
| 49 | 49 | pub record TraitType { |
|
| 50 | 50 | /// Trait name. |
|
| 51 | 51 | name: *[u8], |
|
| 52 | 52 | /// Method signatures, including from supertraits. |
|
| 53 | - | methods: [TraitMethod; ast::MAX_TRAIT_METHODS], |
|
| 54 | - | /// Number of methods. |
|
| 55 | - | methodsLen: u32, |
|
| 53 | + | methods: *mut [TraitMethod], |
|
| 56 | 54 | /// Supertraits that must also be implemented. |
|
| 57 | - | supertraits: [*TraitType; ast::MAX_SUPERTRAITS], |
|
| 58 | - | /// Number of supertraits. |
|
| 59 | - | supertraitsLen: u32, |
|
| 55 | + | supertraits: *mut [*TraitType], |
|
| 60 | 56 | } |
|
| 61 | 57 | ||
| 62 | 58 | /// A single method signature within a trait. |
|
| 63 | 59 | pub record TraitMethod { |
|
| 64 | 60 | /// Method name. |
| 80 | 76 | /// Name of the concrete type. |
|
| 81 | 77 | concreteTypeName: *[u8], |
|
| 82 | 78 | /// Module where this instance was declared. |
|
| 83 | 79 | moduleId: u16, |
|
| 84 | 80 | /// Method symbols for each trait method, in declaration order. |
|
| 85 | - | methods: [*mut Symbol; ast::MAX_TRAIT_METHODS], |
|
| 86 | - | /// Number of methods. |
|
| 87 | - | methodsLen: u32, |
|
| 81 | + | methods: *mut [*mut Symbol], |
|
| 88 | 82 | } |
|
| 89 | 83 | ||
| 90 | 84 | /// Identifier for the synthetic `len` field. |
|
| 91 | 85 | pub const LEN_FIELD: *[u8] = "len"; |
|
| 92 | 86 | /// Identifier for the synthetic `ptr` field. |
| 143 | 137 | length: u32, |
|
| 144 | 138 | } |
|
| 145 | 139 | ||
| 146 | 140 | /// Record nominal type. |
|
| 147 | 141 | pub record RecordType { |
|
| 148 | - | fields: [RecordField; parser::MAX_RECORD_FIELDS], |
|
| 149 | - | fieldsLen: u32, |
|
| 142 | + | fields: *[RecordField], |
|
| 150 | 143 | labeled: bool, |
|
| 151 | 144 | /// Cached layout. |
|
| 152 | 145 | layout: Layout, |
|
| 153 | 146 | } |
|
| 154 | 147 | ||
| 155 | 148 | /// Union nominal type. |
|
| 156 | 149 | pub record UnionType { |
|
| 157 | - | variants: [UnionVariant; MAX_UNION_VARIANTS], |
|
| 158 | - | variantsLen: u32, |
|
| 150 | + | variants: *[UnionVariant], |
|
| 159 | 151 | /// Cached layout. |
|
| 160 | 152 | layout: Layout, |
|
| 161 | 153 | /// Cached payload offset within the union aggregate. |
|
| 162 | 154 | valOffset: u32, |
|
| 163 | 155 | /// If all variants have void payloads. |
| 245 | 237 | }, |
|
| 246 | 238 | } |
|
| 247 | 239 | ||
| 248 | 240 | /// Resolved function signature details. |
|
| 249 | 241 | pub record FnType { |
|
| 250 | - | paramTypes: [*Type; MAX_FN_PARAMS], |
|
| 251 | - | paramTypesLen: u32, |
|
| 242 | + | paramTypes: *[*Type], |
|
| 252 | 243 | returnType: *Type, |
|
| 253 | - | throwList: [*Type; MAX_FN_THROWS], |
|
| 254 | - | throwListLen: u32, |
|
| 244 | + | throwList: *[*Type], |
|
| 255 | 245 | localCount: u32, |
|
| 256 | 246 | } |
|
| 257 | 247 | ||
| 258 | 248 | /// Describes a type computed during semantic analysis. |
|
| 259 | 249 | pub union Type { |
| 578 | 568 | MissingSupertraitInstance(*[u8]), |
|
| 579 | 569 | /// Internal error. |
|
| 580 | 570 | Internal, |
|
| 581 | 571 | } |
|
| 582 | 572 | ||
| 583 | - | /// Fixed-capacity diagnostic sink. |
|
| 584 | - | pub record ErrorList { |
|
| 585 | - | list: *mut [Error], |
|
| 586 | - | listLen: u32, |
|
| 587 | - | } |
|
| 588 | - | ||
| 589 | 573 | /// Diagnostics returned by the analyzer. |
|
| 590 | 574 | pub record Diagnostics { |
|
| 591 | - | errors: ErrorList, |
|
| 575 | + | errors: *mut [Error], |
|
| 592 | 576 | } |
|
| 593 | 577 | ||
| 594 | 578 | /// Call context. |
|
| 595 | 579 | union CallCtx { |
|
| 596 | 580 | /// Normal function call. |
| 761 | 745 | /// Combined semantic metadata table indexed by node ID. |
|
| 762 | 746 | nodeData: NodeDataTable, |
|
| 763 | 747 | /// Linked list of interned types. |
|
| 764 | 748 | types: ?*TypeNode, |
|
| 765 | 749 | /// Diagnostics recorded so far. |
|
| 766 | - | errors: ErrorList, |
|
| 750 | + | errors: *mut [Error], |
|
| 767 | 751 | /// Module graph for the current package. |
|
| 768 | 752 | moduleGraph: *module::ModuleGraph, |
|
| 769 | 753 | /// Cache of module scopes indexed by module ID. |
|
| 770 | 754 | // TODO: Why is this optional? |
|
| 771 | 755 | moduleScopes: [?*mut Scope; module::MAX_MODULES], |
| 825 | 809 | *entry = info; |
|
| 826 | 810 | ||
| 827 | 811 | return entry; |
|
| 828 | 812 | } |
|
| 829 | 813 | ||
| 830 | - | /// Construct an empty errors list backed by the provided buffer. |
|
| 831 | - | fn errorList(list: *mut [Error]) -> ErrorList { |
|
| 832 | - | return ErrorList { list, listLen: 0 }; |
|
| 833 | - | } |
|
| 834 | - | ||
| 835 | 814 | /// Returns an error, if any, associated with the given node. |
|
| 836 | 815 | fn errorForNode(self: *Resolver, node: *ast::Node) -> ?*Error { |
|
| 837 | - | for i in 0..self.errors.listLen { |
|
| 838 | - | let err = &self.errors.list[i]; |
|
| 816 | + | for i in 0..self.errors.len { |
|
| 817 | + | let err = &self.errors[i]; |
|
| 839 | 818 | if err.node == node { |
|
| 840 | 819 | return err; |
|
| 841 | 820 | } |
|
| 842 | 821 | } |
|
| 843 | 822 | return nil; |
| 894 | 873 | extra: NodeExtra::None, |
|
| 895 | 874 | }; |
|
| 896 | 875 | } |
|
| 897 | 876 | ||
| 898 | 877 | let mut moduleScopes: [?*mut Scope; module::MAX_MODULES] = undefined; |
|
| 878 | + | // TODO: Simplify. |
|
| 899 | 879 | for i in 0..moduleScopes.len { |
|
| 900 | 880 | moduleScopes[i] = nil; |
|
| 901 | 881 | } |
|
| 902 | 882 | return Resolver { |
|
| 903 | 883 | scope: storage.pkgScope, |
| 908 | 888 | currentMod: 0, |
|
| 909 | 889 | config, |
|
| 910 | 890 | arena, |
|
| 911 | 891 | nodeData: NodeDataTable { entries: storage.nodeData }, |
|
| 912 | 892 | types: nil, |
|
| 913 | - | errors: errorList(storage.errors), |
|
| 893 | + | errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len), |
|
| 914 | 894 | // TODO: Shouldn't be undefined. |
|
| 915 | 895 | moduleGraph: undefined, |
|
| 916 | 896 | moduleScopes, |
|
| 917 | 897 | instances: undefined, |
|
| 918 | 898 | instancesLen: 0, |
|
| 919 | 899 | }; |
|
| 920 | 900 | } |
|
| 921 | 901 | ||
| 922 | 902 | /// Return `true` if there are no errors in the diagnostics. |
|
| 923 | 903 | pub fn success(diag: *Diagnostics) -> bool { |
|
| 924 | - | return diag.errors.listLen == 0; |
|
| 904 | + | return diag.errors.len == 0; |
|
| 925 | 905 | } |
|
| 926 | 906 | ||
| 927 | 907 | /// Retrieve an error diagnostic by index, if present. |
|
| 928 | - | pub fn errorAt(errs: *ErrorList, index: u32) -> ?*Error { |
|
| 929 | - | if index >= errs.listLen { |
|
| 908 | + | pub fn errorAt(errs: *[Error], index: u32) -> ?*Error { |
|
| 909 | + | if index >= errs.len { |
|
| 930 | 910 | return nil; |
|
| 931 | 911 | } |
|
| 932 | - | return &errs.list[index]; |
|
| 912 | + | return &errs[index]; |
|
| 933 | 913 | } |
|
| 934 | 914 | ||
| 935 | 915 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
|
| 936 | 916 | fn emitError(self: *mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 937 | 917 | // If our error list is full, just return an error without recording it. |
|
| 938 | - | if self.errors.listLen >= self.errors.list.len { |
|
| 918 | + | if self.errors.len >= self.errors.cap { |
|
| 939 | 919 | return ResolveError::Failure; |
|
| 940 | 920 | } |
|
| 941 | 921 | // Don't record more than one error per node. |
|
| 942 | 922 | if let n = node; errorForNode(self, n) != nil { |
|
| 943 | 923 | return ResolveError::Failure; |
|
| 944 | 924 | } |
|
| 945 | - | self.errors.list[self.errors.listLen] = Error { kind, node, moduleId: self.currentMod }; |
|
| 946 | - | self.errors.listLen += 1; |
|
| 925 | + | let idx = self.errors.len; |
|
| 926 | + | self.errors = @sliceOf(self.errors.ptr, idx + 1, self.errors.cap); |
|
| 927 | + | self.errors[idx] = Error { kind, node, moduleId: self.currentMod }; |
|
| 947 | 928 | ||
| 948 | 929 | return ResolveError::Failure; |
|
| 949 | 930 | } |
|
| 950 | 931 | ||
| 951 | 932 | /// Like [`emitError`], but for type mismatches specifically. |
| 1308 | 1289 | else return nil; |
|
| 1309 | 1290 | ||
| 1310 | 1291 | if call.args.len == 0 { |
|
| 1311 | 1292 | return nil; |
|
| 1312 | 1293 | } |
|
| 1313 | - | let arg = call.args.list[0]; |
|
| 1294 | + | let arg = call.args[0]; |
|
| 1314 | 1295 | ||
| 1315 | 1296 | if let case ast::NodeValue::Placeholder = arg.value { |
|
| 1316 | 1297 | return nil; |
|
| 1317 | 1298 | } |
|
| 1318 | 1299 | return arg; |
| 1481 | 1462 | pub fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
|
| 1482 | 1463 | let payloadLayout = getTypeLayout(payload); |
|
| 1483 | 1464 | let mut maxSize = payloadLayout.size; |
|
| 1484 | 1465 | let mut maxAlign = payloadLayout.alignment; |
|
| 1485 | 1466 | ||
| 1486 | - | for i in 0..throwList.len { |
|
| 1487 | - | let errLayout = getTypeLayout(*throwList[i]); |
|
| 1467 | + | for errType in throwList { |
|
| 1468 | + | let errLayout = getTypeLayout(*errType); |
|
| 1488 | 1469 | maxSize = max(maxSize, errLayout.size); |
|
| 1489 | 1470 | maxAlign = max(maxAlign, errLayout.alignment); |
|
| 1490 | 1471 | } |
|
| 1491 | 1472 | return Layout { |
|
| 1492 | 1473 | size: PTR_SIZE + maxSize, |
| 1499 | 1480 | let tagSize: u32 = 1; |
|
| 1500 | 1481 | let mut maxVarSize: u32 = 0; |
|
| 1501 | 1482 | let mut maxVarAlign: u32 = 1; |
|
| 1502 | 1483 | let mut isAllVoid: bool = true; |
|
| 1503 | 1484 | ||
| 1504 | - | for i in 0..variants.len { |
|
| 1505 | - | let payloadType = variants[i].valueType; |
|
| 1506 | - | if payloadType != Type::Void { |
|
| 1485 | + | for variant in variants { |
|
| 1486 | + | if variant.valueType != Type::Void { |
|
| 1507 | 1487 | isAllVoid = false; |
|
| 1508 | - | let payloadLayout = getTypeLayout(payloadType); |
|
| 1488 | + | let payloadLayout = getTypeLayout(variant.valueType); |
|
| 1509 | 1489 | maxVarSize = max(maxVarSize, payloadLayout.size); |
|
| 1510 | 1490 | maxVarAlign = max(maxVarAlign, payloadLayout.alignment); |
|
| 1511 | 1491 | } |
|
| 1512 | 1492 | } |
|
| 1513 | 1493 | let unionAlignment: u32 = max(1, maxVarAlign); |
| 1665 | 1645 | self.currentMod = prevMod; |
|
| 1666 | 1646 | } |
|
| 1667 | 1647 | } |
|
| 1668 | 1648 | ||
| 1669 | 1649 | /// Check if all elements in a node list are assignable to the target type. |
|
| 1670 | - | fn isListAssignable(self: *mut Resolver, targetType: Type, items: *ast::NodeList) -> bool { |
|
| 1671 | - | for i in 0..items.len { |
|
| 1672 | - | let itemNode = items.list[i]; |
|
| 1650 | + | fn isListAssignable(self: *mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool { |
|
| 1651 | + | for itemNode in items { |
|
| 1673 | 1652 | let elemTy = typeFor(self, itemNode) |
|
| 1674 | 1653 | else return false; |
|
| 1675 | 1654 | if let _ = isAssignable(self, targetType, elemTy, itemNode) { |
|
| 1676 | 1655 | // Do nothing. |
|
| 1677 | 1656 | } else { |
| 1716 | 1695 | return Coercion::Identity; |
|
| 1717 | 1696 | } |
|
| 1718 | 1697 | // TODO: This won't work, because we should be setting coercions |
|
| 1719 | 1698 | // for every list item, but we don't. It's best to not have an |
|
| 1720 | 1699 | // `isAssignable` function and just have one that records coercions. |
|
| 1721 | - | if isListAssignable(self, *lhs.item, &items) { |
|
| 1700 | + | if isListAssignable(self, *lhs.item, items) { |
|
| 1722 | 1701 | return Coercion::Identity; |
|
| 1723 | 1702 | } |
|
| 1724 | 1703 | return nil; |
|
| 1725 | 1704 | } |
|
| 1726 | 1705 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
| 1845 | 1824 | return nil; |
|
| 1846 | 1825 | } |
|
| 1847 | 1826 | ||
| 1848 | 1827 | /// Check if two function type descriptors are structurally equivalent. |
|
| 1849 | 1828 | fn fnTypeEqual(a: *FnType, b: *FnType) -> bool { |
|
| 1850 | - | if a.paramTypesLen != b.paramTypesLen { |
|
| 1829 | + | if a.paramTypes.len != b.paramTypes.len { |
|
| 1851 | 1830 | return false; |
|
| 1852 | 1831 | } |
|
| 1853 | - | if a.throwListLen != b.throwListLen { |
|
| 1832 | + | if a.throwList.len != b.throwList.len { |
|
| 1854 | 1833 | return false; |
|
| 1855 | 1834 | } |
|
| 1856 | 1835 | if not typesEqual(*a.returnType, *b.returnType) { |
|
| 1857 | 1836 | return false; |
|
| 1858 | 1837 | } |
|
| 1859 | - | for i in 0..a.paramTypesLen { |
|
| 1838 | + | for i in 0..a.paramTypes.len { |
|
| 1860 | 1839 | if not typesEqual(*a.paramTypes[i], *b.paramTypes[i]) { |
|
| 1861 | 1840 | return false; |
|
| 1862 | 1841 | } |
|
| 1863 | 1842 | } |
|
| 1864 | - | for i in 0..a.throwListLen { |
|
| 1843 | + | for i in 0..a.throwList.len { |
|
| 1865 | 1844 | if not typesEqual(*a.throwList[i], *b.throwList[i]) { |
|
| 1866 | 1845 | return false; |
|
| 1867 | 1846 | } |
|
| 1868 | 1847 | } |
|
| 1869 | 1848 | return true; |
| 1918 | 1897 | /// Get field info for a record-like type (records, slices) by field index. |
|
| 1919 | 1898 | pub fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 1920 | 1899 | match ty { |
|
| 1921 | 1900 | case Type::Nominal(info) => { |
|
| 1922 | 1901 | let case NominalType::Record(recInfo) = *info else return nil; |
|
| 1923 | - | if index >= recInfo.fieldsLen { |
|
| 1902 | + | if index >= recInfo.fields.len { |
|
| 1924 | 1903 | return nil; |
|
| 1925 | 1904 | } |
|
| 1926 | 1905 | return recInfo.fields[index]; |
|
| 1927 | 1906 | } |
|
| 1928 | 1907 | case Type::Slice { item, mutable } => { |
| 2499 | 2478 | } |
|
| 2500 | 2479 | case ast::NodeValue::Use(_) => { |
|
| 2501 | 2480 | // Handled in previous pass. |
|
| 2502 | 2481 | } |
|
| 2503 | 2482 | case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 2504 | - | try resolveInstanceDecl(self, node, traitName, targetType, &methods); |
|
| 2483 | + | try resolveInstanceDecl(self, node, traitName, targetType, methods); |
|
| 2505 | 2484 | } |
|
| 2506 | 2485 | else => { |
|
| 2507 | 2486 | // Ignore non-declaration nodes. |
|
| 2508 | 2487 | } |
|
| 2509 | 2488 | } |
| 2539 | 2518 | } |
|
| 2540 | 2519 | case ast::NodeValue::TraitDecl { .. } => { |
|
| 2541 | 2520 | // Skip: already analyzed in declaration phase. |
|
| 2542 | 2521 | } |
|
| 2543 | 2522 | case ast::NodeValue::InstanceDecl { methods, .. } => { |
|
| 2544 | - | try resolveInstanceMethodBodies(self, &methods); |
|
| 2523 | + | try resolveInstanceMethodBodies(self, methods); |
|
| 2545 | 2524 | } |
|
| 2546 | 2525 | else => { |
|
| 2547 | 2526 | // FIXME: This allows module-level statements that should |
|
| 2548 | 2527 | // normally only be valid inside function bodies. We currently |
|
| 2549 | 2528 | // need this because of how tests are written, but it should |
| 2619 | 2598 | }, |
|
| 2620 | 2599 | case ast::NodeValue::Match(sw) => return try resolveMatch(self, node, sw), |
|
| 2621 | 2600 | case ast::NodeValue::MatchProng(_) => panic "visit: `MatchProng` not handled here", |
|
| 2622 | 2601 | case ast::NodeValue::LetElse(letElse) => return try resolveLetElse(self, node, letElse), |
|
| 2623 | 2602 | case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal), |
|
| 2624 | - | case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, &args), |
|
| 2603 | + | case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, args), |
|
| 2625 | 2604 | case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign), |
|
| 2626 | 2605 | case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint), |
|
| 2627 | - | case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, &items, hint), |
|
| 2606 | + | case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint), |
|
| 2628 | 2607 | case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint), |
|
| 2629 | 2608 | case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index), |
|
| 2630 | 2609 | case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access), |
|
| 2631 | 2610 | case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access), |
|
| 2632 | 2611 | case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint), |
| 2749 | 2728 | } |
|
| 2750 | 2729 | return nil; |
|
| 2751 | 2730 | } |
|
| 2752 | 2731 | ||
| 2753 | 2732 | /// Visit every node contained in a list, returning the last resolved type. |
|
| 2754 | - | fn visitList(self: *mut Resolver, list: *ast::NodeList) -> Type |
|
| 2733 | + | fn visitList(self: *mut Resolver, list: *mut [*ast::Node]) -> Type |
|
| 2755 | 2734 | throws (ResolveError) |
|
| 2756 | 2735 | { |
|
| 2757 | 2736 | let mut diverges = false; |
|
| 2758 | - | for i in 0..list.len { |
|
| 2759 | - | let item = list.list[i]; |
|
| 2737 | + | for item in list { |
|
| 2760 | 2738 | if try infer(self, item) == Type::Never { |
|
| 2761 | 2739 | diverges = true; |
|
| 2762 | 2740 | } |
|
| 2763 | 2741 | } |
|
| 2764 | 2742 | if diverges { |
| 2770 | 2748 | /// Collect attribute flags applied to a declaration. |
|
| 2771 | 2749 | fn resolveAttributes(self: *mut Resolver, attrs: ?ast::Attributes) -> u32 |
|
| 2772 | 2750 | throws (ResolveError) |
|
| 2773 | 2751 | { |
|
| 2774 | 2752 | let list = attrs else return 0; |
|
| 2775 | - | let attrNodes = &list.list; |
|
| 2753 | + | let attrNodes = list.list; |
|
| 2776 | 2754 | let mut mask: u32 = 0; |
|
| 2777 | 2755 | ||
| 2778 | - | for i in 0..attrNodes.len { |
|
| 2779 | - | let node = attrNodes.list[i]; |
|
| 2756 | + | for node in attrNodes { |
|
| 2780 | 2757 | let case ast::NodeValue::Attribute(attr) = node.value |
|
| 2781 | 2758 | else panic "resolveAttributes: invalid attribute node"; |
|
| 2782 | 2759 | mask |= (attr as u32); |
|
| 2783 | 2760 | } |
|
| 2784 | 2761 | return mask; |
| 2797 | 2774 | /// Analyze a block node, allocating a nested lexical scope. |
|
| 2798 | 2775 | fn resolveBlock(self: *mut Resolver, node: *ast::Node, block: ast::Block) -> Type |
|
| 2799 | 2776 | throws (ResolveError) |
|
| 2800 | 2777 | { |
|
| 2801 | 2778 | enterScope(self, node); |
|
| 2802 | - | let blockTy = try visitList(self, &block.statements) catch { |
|
| 2779 | + | let blockTy = try visitList(self, block.statements) catch { |
|
| 2803 | 2780 | // One of the statements in the block failed analysis. We simply proceed |
|
| 2804 | 2781 | // without checking the rest of the block statements. Return `Never` to |
|
| 2805 | 2782 | // avoid spurious `FnMissingReturn` errors. |
|
| 2806 | 2783 | exitScope(self); |
|
| 2807 | 2784 | return try setNodeType(self, node, Type::Never); |
| 2865 | 2842 | ast::NodeValue::Undef, |
|
| 2866 | 2843 | ast::NodeValue::Nil => { |
|
| 2867 | 2844 | return true; |
|
| 2868 | 2845 | }, |
|
| 2869 | 2846 | case ast::NodeValue::ArrayLit(items) => { |
|
| 2870 | - | for i in 0..items.len { |
|
| 2871 | - | if not isConstExpr(self, items.list[i]) { |
|
| 2847 | + | for item in items { |
|
| 2848 | + | if not isConstExpr(self, item) { |
|
| 2872 | 2849 | return false; |
|
| 2873 | 2850 | } |
|
| 2874 | 2851 | } |
|
| 2875 | 2852 | return true; |
|
| 2876 | 2853 | }, |
| 2886 | 2863 | } |
|
| 2887 | 2864 | return false; |
|
| 2888 | 2865 | }, |
|
| 2889 | 2866 | case ast::NodeValue::RecordLit(lit) => { |
|
| 2890 | 2867 | // Record literals are constant if all field values are constant. |
|
| 2891 | - | for i in 0..lit.fields.len { |
|
| 2892 | - | let field = lit.fields.list[i]; |
|
| 2868 | + | for field in lit.fields { |
|
| 2893 | 2869 | if let case ast::NodeValue::RecordLitField(fieldLit) = field.value { |
|
| 2894 | 2870 | if not isConstExpr(self, fieldLit.value) { |
|
| 2895 | 2871 | return false; |
|
| 2896 | 2872 | } |
|
| 2897 | 2873 | } |
| 2946 | 2922 | return false; |
|
| 2947 | 2923 | } |
|
| 2948 | 2924 | }, |
|
| 2949 | 2925 | else => return false, |
|
| 2950 | 2926 | } |
|
| 2951 | - | for i in 0..call.args.len { |
|
| 2952 | - | if not isConstExpr(self, call.args.list[i]) { |
|
| 2927 | + | for arg in call.args { |
|
| 2928 | + | if not isConstExpr(self, arg) { |
|
| 2953 | 2929 | return false; |
|
| 2954 | 2930 | } |
|
| 2955 | 2931 | } |
|
| 2956 | 2932 | return true; |
|
| 2957 | 2933 | } |
| 3012 | 2988 | ||
| 3013 | 2989 | /// Check that constructor arguments match record fields. |
|
| 3014 | 2990 | /// |
|
| 3015 | 2991 | /// Verifies argument count matches field count, and that each argument is |
|
| 3016 | 2992 | /// assignable to its corresponding field type. |
|
| 3017 | - | fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *ast::NodeList, recInfo: RecordType) |
|
| 2993 | + | fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType) |
|
| 3018 | 2994 | throws (ResolveError) |
|
| 3019 | 2995 | { |
|
| 3020 | 2996 | try checkRecordArity(self, args, recInfo, node); |
|
| 3021 | 2997 | for i in 0..args.len { |
|
| 3022 | - | let arg = args.list[i]; |
|
| 2998 | + | let arg = args[i]; |
|
| 3023 | 2999 | let fieldType = recInfo.fields[i].fieldType; |
|
| 3024 | 3000 | try checkAssignable(self, arg, fieldType); |
|
| 3025 | 3001 | } |
|
| 3026 | 3002 | } |
|
| 3027 | 3003 | ||
| 3028 | 3004 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
|
| 3029 | - | fn checkRecordArity(self: *mut Resolver, args: *ast::NodeList, recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3030 | - | if args.len != recInfo.fieldsLen { |
|
| 3005 | + | fn checkRecordArity(self: *mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3006 | + | if args.len != recInfo.fields.len { |
|
| 3031 | 3007 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 3032 | - | expected: recInfo.fieldsLen, |
|
| 3008 | + | expected: recInfo.fields.len as u32, |
|
| 3033 | 3009 | actual: args.len, |
|
| 3034 | 3010 | })); |
|
| 3035 | 3011 | } |
|
| 3036 | 3012 | } |
|
| 3037 | 3013 |
| 3073 | 3049 | let attrMask = try resolveAttributes(self, decl.attrs); |
|
| 3074 | 3050 | let mut retTy = Type::Void; |
|
| 3075 | 3051 | if let retNode = decl.sig.returnType { |
|
| 3076 | 3052 | retTy = try infer(self, retNode); |
|
| 3077 | 3053 | } |
|
| 3054 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3055 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3056 | + | let mut throwList: *mut [*Type] = &mut []; |
|
| 3078 | 3057 | let mut fnType = FnType { |
|
| 3079 | - | paramTypes: undefined, |
|
| 3080 | - | paramTypesLen: 0, |
|
| 3058 | + | paramTypes: &[], |
|
| 3081 | 3059 | returnType: allocType(self, retTy), |
|
| 3082 | - | throwList: undefined, |
|
| 3083 | - | throwListLen: 0, |
|
| 3060 | + | throwList: &[], |
|
| 3084 | 3061 | localCount: 0, |
|
| 3085 | 3062 | }; |
|
| 3086 | 3063 | // Enter the function scope to process parameters. |
|
| 3087 | 3064 | enterFn(self, node, &fnType); |
|
| 3088 | 3065 | ||
| 3089 | - | if decl.sig.params.len > fnType.paramTypes.len { |
|
| 3066 | + | if decl.sig.params.len > MAX_FN_PARAMS { |
|
| 3090 | 3067 | exitFn(self); |
|
| 3091 | 3068 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
|
| 3092 | - | expected: fnType.paramTypes.len, |
|
| 3069 | + | expected: MAX_FN_PARAMS, |
|
| 3093 | 3070 | actual: decl.sig.params.len, |
|
| 3094 | 3071 | })); |
|
| 3095 | 3072 | } |
|
| 3096 | - | for i in 0..decl.sig.params.len { |
|
| 3097 | - | let paramNode = decl.sig.params.list[i]; |
|
| 3073 | + | for paramNode in decl.sig.params { |
|
| 3098 | 3074 | let paramTy = try infer(self, paramNode) catch { |
|
| 3099 | 3075 | exitFn(self); |
|
| 3100 | 3076 | throw ResolveError::Failure; |
|
| 3101 | 3077 | }; |
|
| 3102 | - | fnType.paramTypes[fnType.paramTypesLen] = allocType(self, paramTy); |
|
| 3103 | - | fnType.paramTypesLen += 1; |
|
| 3078 | + | paramTypes.append(allocType(self, paramTy), a); |
|
| 3104 | 3079 | } |
|
| 3105 | 3080 | ||
| 3106 | - | if decl.sig.throwList.len > fnType.throwList.len { |
|
| 3081 | + | if decl.sig.throwList.len > MAX_FN_THROWS { |
|
| 3107 | 3082 | exitFn(self); |
|
| 3108 | 3083 | throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch { |
|
| 3109 | - | expected: fnType.throwList.len, |
|
| 3084 | + | expected: MAX_FN_THROWS, |
|
| 3110 | 3085 | actual: decl.sig.throwList.len, |
|
| 3111 | 3086 | })); |
|
| 3112 | 3087 | } |
|
| 3113 | - | for i in 0..decl.sig.throwList.len { |
|
| 3114 | - | let throwNode = decl.sig.throwList.list[i]; |
|
| 3088 | + | for throwNode in decl.sig.throwList { |
|
| 3115 | 3089 | let throwTy = try infer(self, throwNode) catch { |
|
| 3116 | 3090 | exitFn(self); |
|
| 3117 | 3091 | throw ResolveError::Failure; |
|
| 3118 | 3092 | }; |
|
| 3119 | - | fnType.throwList[fnType.throwListLen] = allocType(self, throwTy); |
|
| 3120 | - | fnType.throwListLen += 1; |
|
| 3093 | + | throwList.append(allocType(self, throwTy), a); |
|
| 3121 | 3094 | } |
|
| 3122 | 3095 | exitFn(self); |
|
| 3096 | + | fnType.paramTypes = ¶mTypes[..]; |
|
| 3097 | + | fnType.throwList = &throwList[..]; |
|
| 3123 | 3098 | ||
| 3124 | 3099 | // Bind the function name. |
|
| 3125 | 3100 | let ty = Type::Fn(allocFnType(self, fnType)); |
|
| 3126 | 3101 | let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask) |
|
| 3127 | 3102 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
| 3179 | 3154 | ||
| 3180 | 3155 | return ty; |
|
| 3181 | 3156 | } |
|
| 3182 | 3157 | ||
| 3183 | 3158 | /// Resolve record fields from a node list. |
|
| 3184 | - | fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: ast::NodeList, labeled: bool) -> RecordType |
|
| 3159 | + | fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType |
|
| 3185 | 3160 | throws (ResolveError) |
|
| 3186 | 3161 | { |
|
| 3187 | - | let mut result: [RecordField; parser::MAX_RECORD_FIELDS] = undefined; |
|
| 3188 | - | let mut fieldsLen: u32 = 0; |
|
| 3162 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3163 | + | let mut result: *mut [RecordField] = &mut []; |
|
| 3189 | 3164 | let mut currentOffset: u32 = 0; |
|
| 3190 | 3165 | let mut maxAlignment: u32 = 1; |
|
| 3191 | 3166 | ||
| 3192 | - | if fields.len > result.len { |
|
| 3167 | + | if fields.len > parser::MAX_RECORD_FIELDS { |
|
| 3193 | 3168 | throw emitError(self, node, ErrorKind::Internal); |
|
| 3194 | 3169 | } |
|
| 3195 | 3170 | // TODO: Add cycle detection to catch invalid recursive types like `record A { a: A }`. |
|
| 3196 | - | for i in 0..fields.len { |
|
| 3197 | - | let field = fields.list[i]; |
|
| 3171 | + | for field in fields { |
|
| 3198 | 3172 | let case ast::NodeValue::RecordField { |
|
| 3199 | 3173 | field: fieldNode, |
|
| 3200 | 3174 | type: typeNode, |
|
| 3201 | 3175 | value: valueNode |
|
| 3202 | 3176 | } = field.value else panic "resolveRecordFields: invalid record field"; |
| 3220 | 3194 | ||
| 3221 | 3195 | // Compute field offset by aligning to field's alignment. |
|
| 3222 | 3196 | let fieldLayout = getTypeLayout(fieldType); |
|
| 3223 | 3197 | currentOffset = mem::alignUp(currentOffset, fieldLayout.alignment); |
|
| 3224 | 3198 | ||
| 3225 | - | result[fieldsLen] = RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }; |
|
| 3226 | - | fieldsLen += 1; |
|
| 3199 | + | result.append(RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }, a); |
|
| 3227 | 3200 | ||
| 3228 | 3201 | // Advance offset past this field. |
|
| 3229 | 3202 | currentOffset += fieldLayout.size; |
|
| 3230 | 3203 | ||
| 3231 | 3204 | // Track max alignment for record layout. |
| 3234 | 3207 | // Compute cached layout. |
|
| 3235 | 3208 | let recordLayout = Layout { |
|
| 3236 | 3209 | size: mem::alignUp(currentOffset, maxAlignment), |
|
| 3237 | 3210 | alignment: maxAlignment |
|
| 3238 | 3211 | }; |
|
| 3239 | - | return RecordType { fields: result, fieldsLen, labeled, layout: recordLayout }; |
|
| 3212 | + | return RecordType { fields: &result[..], labeled, layout: recordLayout }; |
|
| 3240 | 3213 | } |
|
| 3241 | 3214 | ||
| 3242 | 3215 | /// Resolve record field types for a named record declaration. |
|
| 3243 | 3216 | fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl) |
|
| 3244 | 3217 | throws (ResolveError) |
| 3252 | 3225 | ||
| 3253 | 3226 | // Skip if already resolved. |
|
| 3254 | 3227 | if let case NominalType::Record(_) = *nominalTy { |
|
| 3255 | 3228 | return; |
|
| 3256 | 3229 | } |
|
| 3257 | - | try visitList(self, &decl.derives); |
|
| 3230 | + | try visitList(self, decl.derives); |
|
| 3258 | 3231 | let recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled); |
|
| 3259 | 3232 | ||
| 3260 | 3233 | *nominalTy = NominalType::Record(recordType); |
|
| 3261 | 3234 | } |
|
| 3262 | 3235 |
| 3276 | 3249 | ||
| 3277 | 3250 | /// Allocate a trait type descriptor and return a pointer to it. |
|
| 3278 | 3251 | fn allocTraitType(self: *mut Resolver, name: *[u8]) -> *mut TraitType { |
|
| 3279 | 3252 | let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 3280 | 3253 | let entry = p as *mut TraitType; |
|
| 3281 | - | *entry = TraitType { name, methods: undefined, methodsLen: 0, supertraits: undefined, supertraitsLen: 0 }; |
|
| 3254 | + | *entry = TraitType { name, methods: &mut [], supertraits: &mut [] }; |
|
| 3282 | 3255 | ||
| 3283 | 3256 | return entry; |
|
| 3284 | 3257 | } |
|
| 3285 | 3258 | ||
| 3286 | 3259 | /// Bind a trait name in the current scope. |
| 3301 | 3274 | return sym; |
|
| 3302 | 3275 | } |
|
| 3303 | 3276 | ||
| 3304 | 3277 | /// Find a trait method by name. |
|
| 3305 | 3278 | pub fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod { |
|
| 3306 | - | for i in 0..traitType.methodsLen { |
|
| 3279 | + | for i in 0..traitType.methods.len { |
|
| 3307 | 3280 | if traitType.methods[i].name == name { |
|
| 3308 | 3281 | return &traitType.methods[i]; |
|
| 3309 | 3282 | } |
|
| 3310 | 3283 | } |
|
| 3311 | 3284 | return nil; |
|
| 3312 | 3285 | } |
|
| 3313 | 3286 | ||
| 3314 | 3287 | /// Resolve a trait declaration body: supertrait methods, then own methods. |
|
| 3315 | - | fn resolveTraitBody(self: *mut Resolver, node: *ast::Node, supertraits: *ast::NodeList, methods: *ast::NodeList) |
|
| 3288 | + | fn resolveTraitBody(self: *mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node]) |
|
| 3316 | 3289 | throws (ResolveError) |
|
| 3317 | 3290 | { |
|
| 3318 | 3291 | let sym = symbolFor(self, node) |
|
| 3319 | 3292 | else return; |
|
| 3320 | 3293 | let case SymbolData::Trait(traitType) = sym.data |
|
| 3321 | 3294 | else return; |
|
| 3322 | 3295 | ||
| 3323 | 3296 | // Resolve supertrait bounds and copy their methods into this trait. |
|
| 3324 | - | for i in 0..supertraits.len { |
|
| 3325 | - | if traitType.supertraitsLen >= traitType.supertraits.len { |
|
| 3326 | - | throw emitError(self, node, ErrorKind::Internal); |
|
| 3327 | - | } |
|
| 3328 | - | let superNode = supertraits.list[i]; |
|
| 3297 | + | for superNode in supertraits { |
|
| 3329 | 3298 | let superSym = try resolveNamePath(self, superNode); |
|
| 3330 | 3299 | let case SymbolData::Trait(superTrait) = superSym.data |
|
| 3331 | 3300 | else throw emitError(self, superNode, ErrorKind::Internal); |
|
| 3332 | 3301 | try setNodeSymbol(self, superNode, superSym); |
|
| 3333 | 3302 | ||
| 3334 | - | if traitType.methodsLen + superTrait.methodsLen > ast::MAX_TRAIT_METHODS { |
|
| 3303 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3304 | + | if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS { |
|
| 3335 | 3305 | throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch { |
|
| 3336 | 3306 | expected: ast::MAX_TRAIT_METHODS, |
|
| 3337 | - | actual: traitType.methodsLen + superTrait.methodsLen, |
|
| 3307 | + | actual: traitType.methods.len as u32 + superTrait.methods.len as u32, |
|
| 3338 | 3308 | })); |
|
| 3339 | 3309 | } |
|
| 3340 | 3310 | // Copy inherited methods into this trait's method table. |
|
| 3341 | - | for j in 0..superTrait.methodsLen { |
|
| 3342 | - | let inherited = superTrait.methods[j]; |
|
| 3311 | + | for inherited in superTrait.methods { |
|
| 3343 | 3312 | if let _ = findTraitMethod(traitType, inherited.name) { |
|
| 3344 | 3313 | throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name)); |
|
| 3345 | 3314 | } |
|
| 3346 | - | traitType.methods[traitType.methodsLen] = TraitMethod { |
|
| 3315 | + | traitType.methods.append(TraitMethod { |
|
| 3347 | 3316 | name: inherited.name, |
|
| 3348 | 3317 | fnType: inherited.fnType, |
|
| 3349 | 3318 | mutable: inherited.mutable, |
|
| 3350 | - | index: traitType.methodsLen, |
|
| 3351 | - | }; |
|
| 3352 | - | traitType.methodsLen += 1; |
|
| 3319 | + | index: traitType.methods.len as u32, |
|
| 3320 | + | }, a); |
|
| 3353 | 3321 | } |
|
| 3354 | - | traitType.supertraits[traitType.supertraitsLen] = superTrait; |
|
| 3355 | - | traitType.supertraitsLen += 1; |
|
| 3322 | + | traitType.supertraits.append(superTrait, a); |
|
| 3356 | 3323 | } |
|
| 3357 | 3324 | ||
| 3358 | - | if traitType.methodsLen + methods.len > ast::MAX_TRAIT_METHODS { |
|
| 3325 | + | if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS { |
|
| 3359 | 3326 | throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch { |
|
| 3360 | 3327 | expected: ast::MAX_TRAIT_METHODS, |
|
| 3361 | - | actual: traitType.methodsLen + methods.len, |
|
| 3328 | + | actual: traitType.methods.len as u32 + methods.len as u32, |
|
| 3362 | 3329 | })); |
|
| 3363 | 3330 | } |
|
| 3364 | 3331 | ||
| 3365 | - | for i in 0..methods.len { |
|
| 3366 | - | let methodNode = methods.list[i]; |
|
| 3332 | + | for methodNode in methods { |
|
| 3367 | 3333 | let case ast::NodeValue::TraitMethodSig { name, receiver, sig } = methodNode.value |
|
| 3368 | 3334 | else continue; |
|
| 3369 | 3335 | let methodName = try nodeName(self, name); |
|
| 3370 | 3336 | ||
| 3371 | 3337 | // Reject duplicate method names. |
| 3387 | 3353 | ||
| 3388 | 3354 | if receiverTargetName != traitType.name { |
|
| 3389 | 3355 | throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3390 | 3356 | } |
|
| 3391 | 3357 | // Resolve parameter types and return type. |
|
| 3392 | - | let mut fnType = FnType { |
|
| 3393 | - | paramTypes: undefined, |
|
| 3394 | - | paramTypesLen: 0, |
|
| 3395 | - | returnType: allocType(self, Type::Void), |
|
| 3396 | - | throwList: undefined, |
|
| 3397 | - | throwListLen: 0, |
|
| 3398 | - | localCount: 0, |
|
| 3399 | - | }; |
|
| 3400 | - | if sig.params.len > fnType.paramTypes.len { |
|
| 3358 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3359 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3360 | + | let mut throwList: *mut [*Type] = &mut []; |
|
| 3361 | + | let mut retType = allocType(self, Type::Void); |
|
| 3362 | + | ||
| 3363 | + | if sig.params.len > MAX_FN_PARAMS { |
|
| 3401 | 3364 | throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch { |
|
| 3402 | - | expected: fnType.paramTypes.len, |
|
| 3365 | + | expected: MAX_FN_PARAMS, |
|
| 3403 | 3366 | actual: sig.params.len, |
|
| 3404 | 3367 | })); |
|
| 3405 | 3368 | } |
|
| 3406 | - | for j in 0..sig.params.len { |
|
| 3407 | - | let paramNode = sig.params.list[j]; |
|
| 3369 | + | for paramNode in sig.params { |
|
| 3408 | 3370 | let paramTy = try infer(self, paramNode); |
|
| 3409 | - | ||
| 3410 | - | fnType.paramTypes[fnType.paramTypesLen] = allocType(self, paramTy); |
|
| 3411 | - | fnType.paramTypesLen += 1; |
|
| 3371 | + | paramTypes.append(allocType(self, paramTy), a); |
|
| 3412 | 3372 | } |
|
| 3413 | 3373 | if let ret = sig.returnType { |
|
| 3414 | - | fnType.returnType = allocType(self, try infer(self, ret)); |
|
| 3374 | + | retType = allocType(self, try infer(self, ret)); |
|
| 3415 | 3375 | } |
|
| 3416 | 3376 | // Resolve throws list. |
|
| 3417 | - | if sig.throwList.len > fnType.throwList.len { |
|
| 3377 | + | if sig.throwList.len > MAX_FN_THROWS { |
|
| 3418 | 3378 | throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch { |
|
| 3419 | - | expected: fnType.throwList.len, |
|
| 3379 | + | expected: MAX_FN_THROWS, |
|
| 3420 | 3380 | actual: sig.throwList.len, |
|
| 3421 | 3381 | })); |
|
| 3422 | 3382 | } |
|
| 3423 | - | for j in 0..sig.throwList.len { |
|
| 3424 | - | let throwNode = sig.throwList.list[j]; |
|
| 3383 | + | for throwNode in sig.throwList { |
|
| 3425 | 3384 | let throwTy = try infer(self, throwNode); |
|
| 3426 | - | ||
| 3427 | - | fnType.throwList[fnType.throwListLen] = allocType(self, throwTy); |
|
| 3428 | - | fnType.throwListLen += 1; |
|
| 3385 | + | throwList.append(allocType(self, throwTy), a); |
|
| 3429 | 3386 | } |
|
| 3430 | - | ||
| 3431 | - | traitType.methods[traitType.methodsLen] = TraitMethod { |
|
| 3387 | + | let fnType = FnType { |
|
| 3388 | + | paramTypes: ¶mTypes[..], |
|
| 3389 | + | returnType: retType, |
|
| 3390 | + | throwList: &throwList[..], |
|
| 3391 | + | localCount: 0, |
|
| 3392 | + | }; |
|
| 3393 | + | traitType.methods.append(TraitMethod { |
|
| 3432 | 3394 | name: methodName, |
|
| 3433 | 3395 | fnType: allocFnType(self, fnType), |
|
| 3434 | 3396 | mutable, |
|
| 3435 | - | index: traitType.methodsLen, |
|
| 3436 | - | }; |
|
| 3437 | - | traitType.methodsLen += 1; |
|
| 3397 | + | index: traitType.methods.len as u32, |
|
| 3398 | + | }, a); |
|
| 3438 | 3399 | ||
| 3439 | 3400 | try setNodeType(self, methodNode, Type::Void); |
|
| 3440 | 3401 | } |
|
| 3441 | 3402 | } |
|
| 3442 | 3403 |
| 3466 | 3427 | fn resolveInstanceDecl( |
|
| 3467 | 3428 | self: *mut Resolver, |
|
| 3468 | 3429 | node: *ast::Node, |
|
| 3469 | 3430 | traitName: *ast::Node, |
|
| 3470 | 3431 | targetType: *ast::Node, |
|
| 3471 | - | methods: *ast::NodeList |
|
| 3432 | + | methods: *mut [*ast::Node] |
|
| 3472 | 3433 | ) throws (ResolveError) { |
|
| 3473 | 3434 | // Look up the trait. |
|
| 3474 | 3435 | let traitSym = try resolveNamePath(self, traitName); |
|
| 3475 | 3436 | let case SymbolData::Trait(traitInfo) = traitSym.data |
|
| 3476 | 3437 | else throw emitError(self, traitName, ErrorKind::Internal); |
| 3493 | 3454 | ||
| 3494 | 3455 | // Build the instance entry. |
|
| 3495 | 3456 | if self.instancesLen >= MAX_INSTANCES { |
|
| 3496 | 3457 | throw emitError(self, node, ErrorKind::Internal); |
|
| 3497 | 3458 | } |
|
| 3459 | + | let methodSlice = try! alloc::allocSlice( |
|
| 3460 | + | &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), traitInfo.methods.len as u32 |
|
| 3461 | + | ) as *mut [*mut Symbol]; |
|
| 3498 | 3462 | let mut entry = InstanceEntry { |
|
| 3499 | 3463 | traitType: traitInfo, |
|
| 3500 | 3464 | concreteType, |
|
| 3501 | 3465 | concreteTypeName: typeSym.name, |
|
| 3502 | 3466 | moduleId: self.currentMod, |
|
| 3503 | - | methods: undefined, |
|
| 3504 | - | methodsLen: 0, |
|
| 3467 | + | methods: methodSlice, |
|
| 3505 | 3468 | }; |
|
| 3506 | 3469 | // Track which trait methods are covered by the instance. |
|
| 3507 | 3470 | let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
|
| 3508 | 3471 | ||
| 3509 | 3472 | // Match each instance method to a trait method. |
|
| 3510 | - | for i in 0..methods.len { |
|
| 3511 | - | let methodNode = methods.list[i]; |
|
| 3473 | + | for methodNode in methods { |
|
| 3512 | 3474 | let case ast::NodeValue::InstanceMethodDecl { |
|
| 3513 | 3475 | name, receiverName, receiverType, sig, body |
|
| 3514 | 3476 | } = methodNode.value else continue; |
|
| 3515 | 3477 | ||
| 3516 | 3478 | let methodName = try nodeName(self, name); |
| 3544 | 3506 | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
|
| 3545 | 3507 | } |
|
| 3546 | 3508 | ||
| 3547 | 3509 | // Build the function type for the instance method. |
|
| 3548 | 3510 | // The receiver becomes the first parameter. |
|
| 3549 | - | let mut fnType = FnType { |
|
| 3550 | - | paramTypes: undefined, |
|
| 3551 | - | paramTypesLen: 0, |
|
| 3552 | - | returnType: allocType(self, Type::Void), |
|
| 3553 | - | throwList: undefined, |
|
| 3554 | - | throwListLen: 0, |
|
| 3555 | - | localCount: 0, |
|
| 3556 | - | }; |
|
| 3557 | - | // First param is the receiver. |
|
| 3558 | 3511 | let receiverPtrType = Type::Pointer { |
|
| 3559 | 3512 | target: allocType(self, concreteType), |
|
| 3560 | 3513 | mutable: receiverMut, |
|
| 3561 | 3514 | }; |
|
| 3562 | - | fnType.paramTypes[0] = allocType(self, receiverPtrType); |
|
| 3563 | - | fnType.paramTypesLen = 1; |
|
| 3564 | 3515 | ||
| 3565 | 3516 | // Validate that the instance method's signature matches the |
|
| 3566 | 3517 | // trait method's signature exactly (params, return type, throws). |
|
| 3567 | - | if sig.params.len != tm.fnType.paramTypesLen { |
|
| 3518 | + | if sig.params.len != tm.fnType.paramTypes.len { |
|
| 3568 | 3519 | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 3569 | - | expected: tm.fnType.paramTypesLen, |
|
| 3520 | + | expected: tm.fnType.paramTypes.len as u32, |
|
| 3570 | 3521 | actual: sig.params.len, |
|
| 3571 | 3522 | })); |
|
| 3572 | 3523 | } |
|
| 3573 | 3524 | for j in 0..sig.params.len { |
|
| 3574 | - | let paramNode = sig.params.list[j]; |
|
| 3525 | + | let paramNode = sig.params[j]; |
|
| 3575 | 3526 | let case ast::NodeValue::FnParam(param) = paramNode.value |
|
| 3576 | 3527 | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
|
| 3577 | 3528 | let instanceParamTy = try resolveValueType(self, param.type); |
|
| 3578 | 3529 | if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) { |
|
| 3579 | 3530 | throw emitTypeMismatch(self, paramNode, TypeMismatch { |
| 3590 | 3541 | throw emitTypeMismatch(self, methodNode, TypeMismatch { |
|
| 3591 | 3542 | expected: *tm.fnType.returnType, |
|
| 3592 | 3543 | actual: instanceRetTy, |
|
| 3593 | 3544 | }); |
|
| 3594 | 3545 | } |
|
| 3595 | - | if sig.throwList.len != tm.fnType.throwListLen { |
|
| 3546 | + | if sig.throwList.len != tm.fnType.throwList.len { |
|
| 3596 | 3547 | throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch { |
|
| 3597 | - | expected: tm.fnType.throwListLen, |
|
| 3548 | + | expected: tm.fnType.throwList.len as u32, |
|
| 3598 | 3549 | actual: sig.throwList.len, |
|
| 3599 | 3550 | })); |
|
| 3600 | 3551 | } |
|
| 3601 | 3552 | for j in 0..sig.throwList.len { |
|
| 3602 | - | let instanceThrowTy = try resolveValueType(self, sig.throwList.list[j]); |
|
| 3553 | + | let instanceThrowTy = try resolveValueType(self, sig.throwList[j]); |
|
| 3603 | 3554 | if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) { |
|
| 3604 | - | throw emitTypeMismatch(self, sig.throwList.list[j], TypeMismatch { |
|
| 3555 | + | throw emitTypeMismatch(self, sig.throwList[j], TypeMismatch { |
|
| 3605 | 3556 | expected: *tm.fnType.throwList[j], |
|
| 3606 | 3557 | actual: instanceThrowTy, |
|
| 3607 | 3558 | }); |
|
| 3608 | 3559 | } |
|
| 3609 | 3560 | } |
|
| 3610 | 3561 | ||
| 3611 | - | // Copy the trait's canonical types into the final function type. |
|
| 3612 | - | for j in 0..tm.fnType.paramTypesLen { |
|
| 3613 | - | fnType.paramTypes[fnType.paramTypesLen] = tm.fnType.paramTypes[j]; |
|
| 3614 | - | fnType.paramTypesLen += 1; |
|
| 3615 | - | } |
|
| 3616 | - | fnType.returnType = tm.fnType.returnType; |
|
| 3617 | - | for j in 0..tm.fnType.throwListLen { |
|
| 3618 | - | fnType.throwList[fnType.throwListLen] = tm.fnType.throwList[j]; |
|
| 3619 | - | fnType.throwListLen += 1; |
|
| 3562 | + | // Build final function type: receiver plus trait's canonical types. |
|
| 3563 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3564 | + | // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`? |
|
| 3565 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3566 | + | paramTypes.append(allocType(self, receiverPtrType), a); |
|
| 3567 | + | ||
| 3568 | + | for ty in tm.fnType.paramTypes { |
|
| 3569 | + | paramTypes.append(ty, a); |
|
| 3620 | 3570 | } |
|
| 3571 | + | let fnType = FnType { |
|
| 3572 | + | paramTypes: ¶mTypes[..], |
|
| 3573 | + | returnType: tm.fnType.returnType, |
|
| 3574 | + | throwList: tm.fnType.throwList, |
|
| 3575 | + | localCount: 0, |
|
| 3576 | + | }; |
|
| 3621 | 3577 | ||
| 3622 | 3578 | // Create a symbol for the instance method without binding it into the |
|
| 3623 | 3579 | // module scope. Instance methods are dispatched via v-table, so they |
|
| 3624 | 3580 | // must not pollute the enclosing scope. |
|
| 3625 | 3581 | let fnTy = Type::Fn(allocFnType(self, fnType)); |
| 3632 | 3588 | try setNodeType(self, methodNode, fnTy); |
|
| 3633 | 3589 | try setNodeType(self, name, fnTy); |
|
| 3634 | 3590 | ||
| 3635 | 3591 | // Store in instance entry at the matching v-table slot. |
|
| 3636 | 3592 | entry.methods[tm.index] = sym; |
|
| 3637 | - | entry.methodsLen += 1; |
|
| 3638 | - | ||
| 3639 | 3593 | covered[tm.index] = true; |
|
| 3640 | 3594 | } |
|
| 3641 | 3595 | ||
| 3642 | 3596 | // Fill inherited method slots from supertrait instances. |
|
| 3643 | - | for si in 0..traitInfo.supertraitsLen { |
|
| 3644 | - | let superTrait = traitInfo.supertraits[si]; |
|
| 3597 | + | for superTrait in traitInfo.supertraits { |
|
| 3645 | 3598 | let superInst = findInstance(self, superTrait, concreteType) |
|
| 3646 | 3599 | else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name)); |
|
| 3647 | - | for mi in 0..superTrait.methodsLen { |
|
| 3600 | + | for mi in 0..superTrait.methods.len { |
|
| 3648 | 3601 | let merged = findTraitMethod(traitInfo, superTrait.methods[mi].name) |
|
| 3649 | 3602 | else panic "resolveInstanceDecl: inherited method not found"; |
|
| 3650 | 3603 | if not covered[merged.index] { |
|
| 3651 | 3604 | entry.methods[merged.index] = superInst.methods[mi]; |
|
| 3652 | - | entry.methodsLen += 1; |
|
| 3653 | 3605 | covered[merged.index] = true; |
|
| 3654 | 3606 | } |
|
| 3655 | 3607 | } |
|
| 3656 | 3608 | } |
|
| 3657 | 3609 | ||
| 3658 | 3610 | // Check that all trait methods are implemented. |
|
| 3659 | - | for i in 0..traitInfo.methodsLen { |
|
| 3611 | + | for i in 0..traitInfo.methods.len { |
|
| 3660 | 3612 | if not covered[i] { |
|
| 3661 | 3613 | throw emitError(self, node, ErrorKind::MissingTraitMethod(traitInfo.methods[i].name)); |
|
| 3662 | 3614 | } |
|
| 3663 | 3615 | } |
|
| 3664 | 3616 | self.instances[self.instancesLen] = entry; |
| 3666 | 3618 | ||
| 3667 | 3619 | try setNodeType(self, node, Type::Void); |
|
| 3668 | 3620 | } |
|
| 3669 | 3621 | ||
| 3670 | 3622 | /// Resolve instance method bodies. |
|
| 3671 | - | fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *ast::NodeList) |
|
| 3623 | + | fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *mut [*ast::Node]) |
|
| 3672 | 3624 | throws (ResolveError) |
|
| 3673 | 3625 | { |
|
| 3674 | - | for i in 0..methods.len { |
|
| 3675 | - | let methodNode = methods.list[i]; |
|
| 3626 | + | for methodNode in methods { |
|
| 3676 | 3627 | let case ast::NodeValue::InstanceMethodDecl { |
|
| 3677 | 3628 | name, receiverName, receiverType, sig, body |
|
| 3678 | 3629 | } = methodNode.value else continue; |
|
| 3679 | 3630 | ||
| 3680 | 3631 | // Symbol may be absent if [`resolveInstanceDecl`] reported an error |
| 3694 | 3645 | try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch { |
|
| 3695 | 3646 | exitFn(self); |
|
| 3696 | 3647 | throw ResolveError::Failure; |
|
| 3697 | 3648 | }; |
|
| 3698 | 3649 | // Bind the remaining parameters from the signature. |
|
| 3699 | - | for j in 0..sig.params.len { |
|
| 3700 | - | let paramNode = sig.params.list[j]; |
|
| 3650 | + | for paramNode in sig.params { |
|
| 3701 | 3651 | let paramTy = try infer(self, paramNode) catch { |
|
| 3702 | 3652 | exitFn(self); |
|
| 3703 | 3653 | throw ResolveError::Failure; |
|
| 3704 | 3654 | }; |
|
| 3705 | 3655 | } |
| 3743 | 3693 | // Check if already resolved, in which case there's no need to |
|
| 3744 | 3694 | // do it again. |
|
| 3745 | 3695 | if let case NominalType::Union(_) = *nominalTy { |
|
| 3746 | 3696 | return; |
|
| 3747 | 3697 | } |
|
| 3748 | - | let mut variants: [UnionVariant; MAX_UNION_VARIANTS] = undefined; |
|
| 3749 | - | let mut variantCount: u32 = 0; |
|
| 3698 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3699 | + | let mut variants: *mut [UnionVariant] = &mut []; |
|
| 3750 | 3700 | ||
| 3751 | 3701 | // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion |
|
| 3752 | 3702 | // when a variant references this union type (e.g. record payloads with `*[Self]`). |
|
| 3753 | 3703 | // TODO: It would be best to have a resolving state eg. `Visiting` for this situation. |
|
| 3754 | 3704 | *nominalTy = NominalType::Union(UnionType { |
|
| 3755 | - | variants, |
|
| 3756 | - | variantsLen: variantCount, |
|
| 3705 | + | variants: &[], |
|
| 3757 | 3706 | layout: Layout { size: 0, alignment: 0 }, |
|
| 3758 | 3707 | valOffset: 0, |
|
| 3759 | 3708 | isAllVoid: true |
|
| 3760 | 3709 | }); |
|
| 3761 | 3710 | ||
| 3762 | - | try visitList(self, &decl.derives); |
|
| 3711 | + | try visitList(self, decl.derives); |
|
| 3763 | 3712 | ||
| 3764 | - | if decl.variants.len > variants.len { |
|
| 3713 | + | if decl.variants.len > MAX_UNION_VARIANTS { |
|
| 3765 | 3714 | panic "resolveUnionBody: maximum union variants exceeded"; |
|
| 3766 | 3715 | } |
|
| 3767 | 3716 | let mut iota: u32 = 0; |
|
| 3768 | 3717 | for i in 0..decl.variants.len { |
|
| 3769 | - | let variantNode = decl.variants.list[i]; |
|
| 3718 | + | let variantNode = decl.variants[i]; |
|
| 3770 | 3719 | let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value |
|
| 3771 | 3720 | else panic "resolveUnionBody: invalid union variant"; |
|
| 3772 | 3721 | let variantName = try nodeName(self, variantDecl.name); |
|
| 3773 | 3722 | // Resolve the variant's payload type if present. |
|
| 3774 | 3723 | let mut variantType = Type::Void; |
| 3780 | 3729 | let tag = variantTag(variantDecl, &mut iota); |
|
| 3781 | 3730 | // Create a symbol for this variant. |
|
| 3782 | 3731 | let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag }; |
|
| 3783 | 3732 | let variantSym = allocSymbol(self, data, variantName, variantNode, 0); |
|
| 3784 | 3733 | ||
| 3785 | - | variants[variantCount] = UnionVariant { |
|
| 3734 | + | variants.append(UnionVariant { |
|
| 3786 | 3735 | name: variantName, |
|
| 3787 | 3736 | valueType: variantType, |
|
| 3788 | 3737 | symbol: variantSym, |
|
| 3789 | - | }; |
|
| 3790 | - | variantCount += 1; |
|
| 3738 | + | }, a); |
|
| 3791 | 3739 | } |
|
| 3792 | - | let info = computeUnionLayout(&variants[..variantCount]); |
|
| 3740 | + | let info = computeUnionLayout(&variants[..]); |
|
| 3793 | 3741 | ||
| 3794 | 3742 | // Update the nominal type with the resolved variants. |
|
| 3795 | 3743 | *nominalTy = NominalType::Union(UnionType { |
|
| 3796 | - | variants, |
|
| 3797 | - | variantsLen: variantCount, |
|
| 3744 | + | variants: &variants[..], |
|
| 3798 | 3745 | layout: info.layout, |
|
| 3799 | 3746 | valOffset: info.valOffset, |
|
| 3800 | 3747 | isAllVoid: info.isAllVoid, |
|
| 3801 | 3748 | }); |
|
| 3802 | 3749 | } |
| 3974 | 3921 | } |
|
| 3975 | 3922 | } |
|
| 3976 | 3923 | case Type::Array(arrayInfo) => { |
|
| 3977 | 3924 | if let case ast::NodeValue::ArrayLit(items) = pattern.value { |
|
| 3978 | 3925 | let elemTy = *arrayInfo.item; |
|
| 3979 | - | for i in 0..items.len { |
|
| 3980 | - | try resolveCasePattern(self, items.list[i], elemTy, IdentMode::Bind, matchBy); |
|
| 3926 | + | for item in items { |
|
| 3927 | + | try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy); |
|
| 3981 | 3928 | } |
|
| 3982 | 3929 | return; |
|
| 3983 | 3930 | } |
|
| 3984 | 3931 | } else => {} |
|
| 3985 | 3932 | } |
| 4098 | 4045 | ||
| 4099 | 4046 | return try setNodeType(self, node, Type::Void); |
|
| 4100 | 4047 | } |
|
| 4101 | 4048 | ||
| 4102 | 4049 | /// Check whether any pattern in a case prong is a wildcard `_`. |
|
| 4103 | - | fn hasWildcardPattern(patterns: ast::NodeList) -> bool { |
|
| 4104 | - | for i in 0..patterns.len { |
|
| 4105 | - | if let case ast::NodeValue::Placeholder = patterns.list[i].value { |
|
| 4050 | + | fn hasWildcardPattern(patterns: *mut [*ast::Node]) -> bool { |
|
| 4051 | + | for pattern in patterns { |
|
| 4052 | + | if let case ast::NodeValue::Placeholder = pattern.value { |
|
| 4106 | 4053 | return true; |
|
| 4107 | 4054 | } |
|
| 4108 | 4055 | } |
|
| 4109 | 4056 | return false; |
|
| 4110 | 4057 | } |
| 4162 | 4109 | } else { |
|
| 4163 | 4110 | try resolveMatchGeneric(self, node, sw, subject.effectiveTy); |
|
| 4164 | 4111 | } |
|
| 4165 | 4112 | ||
| 4166 | 4113 | // Mark last non-guarded prong as exhaustive. |
|
| 4167 | - | let lastProng = sw.prongs.list[sw.prongs.len - 1]; |
|
| 4114 | + | let lastProng = sw.prongs[sw.prongs.len - 1]; |
|
| 4168 | 4115 | let case ast::NodeValue::MatchProng(p) = lastProng.value |
|
| 4169 | 4116 | else panic "resolveMatch: expected match prong"; |
|
| 4170 | 4117 | if p.guard == nil { |
|
| 4171 | 4118 | try setProngCatchAll(self, lastProng, true); |
|
| 4172 | 4119 | } |
| 4179 | 4126 | /// Analyze a `match` expression on an optional subject. |
|
| 4180 | 4127 | fn resolveMatchOptional(self: *mut Resolver, node: *ast::Node, sw: ast::Match, innerTy: *Type) -> Type |
|
| 4181 | 4128 | throws (ResolveError) |
|
| 4182 | 4129 | { |
|
| 4183 | 4130 | let subjectTy = Type::Optional(innerTy); |
|
| 4184 | - | let prongs = &sw.prongs; |
|
| 4131 | + | let prongs = sw.prongs; |
|
| 4185 | 4132 | let mut hasValue = false; |
|
| 4186 | 4133 | let mut hasNil = false; |
|
| 4187 | 4134 | let mut catchAll = false; |
|
| 4188 | 4135 | let mut matchType = Type::Never; |
|
| 4189 | 4136 | ||
| 4190 | - | for i in 0..prongs.len { |
|
| 4191 | - | let prongNode = prongs.list[i]; |
|
| 4137 | + | for prongNode in prongs { |
|
| 4192 | 4138 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 4193 | 4139 | else panic "resolveMatchOptional: expected match prong"; |
|
| 4194 | 4140 | ||
| 4195 | 4141 | // For optionals, a binding prong only covers the value case, not `nil`. |
|
| 4196 | 4142 | // Only `else` without a guard is a true catch-all. |
| 4220 | 4166 | if hasValue { |
|
| 4221 | 4167 | throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern); |
|
| 4222 | 4168 | } |
|
| 4223 | 4169 | hasValue = true; |
|
| 4224 | 4170 | } else if let case ast::ProngArm::Case(patterns) = prong.arm { |
|
| 4225 | - | for idx in 0..patterns.len { |
|
| 4226 | - | if let case ast::NodeValue::Nil = patterns.list[idx].value { |
|
| 4171 | + | for pat in patterns { |
|
| 4172 | + | if let case ast::NodeValue::Nil = pat.value { |
|
| 4227 | 4173 | if hasNil { |
|
| 4228 | - | throw emitError(self, patterns.list[idx], ErrorKind::DuplicateMatchPattern); |
|
| 4174 | + | throw emitError(self, pat, ErrorKind::DuplicateMatchPattern); |
|
| 4229 | 4175 | } |
|
| 4230 | 4176 | hasNil = true; |
|
| 4231 | 4177 | } |
|
| 4232 | 4178 | } |
|
| 4233 | 4179 | } |
| 4255 | 4201 | sw: ast::Match, |
|
| 4256 | 4202 | subjectTy: Type, |
|
| 4257 | 4203 | info: UnionType, |
|
| 4258 | 4204 | matchBy: MatchBy |
|
| 4259 | 4205 | ) -> Type throws (ResolveError) { |
|
| 4260 | - | let prongs = &sw.prongs; |
|
| 4206 | + | let prongs = sw.prongs; |
|
| 4261 | 4207 | let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS]; |
|
| 4262 | 4208 | let mut coveredCount: u32 = 0; |
|
| 4263 | 4209 | let mut state = MatchState { catchAll: false, isConst: false }; |
|
| 4264 | 4210 | let mut matchType = Type::Never; |
|
| 4265 | 4211 | ||
| 4266 | - | for i in 0..prongs.len { |
|
| 4267 | - | let prongNode = prongs.list[i]; |
|
| 4212 | + | for prongNode in prongs { |
|
| 4268 | 4213 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 4269 | 4214 | else panic "resolveMatchUnion: expected match prong"; |
|
| 4270 | 4215 | ||
| 4271 | 4216 | matchType = try resolveMatchProng(self, prongNode, prong, subjectTy, &mut state, matchType, matchBy); |
|
| 4272 | 4217 | ||
| 4273 | 4218 | // Record covered variants. Guarded prongs don't count as covering. |
|
| 4274 | 4219 | if prong.guard == nil { |
|
| 4275 | 4220 | if let case ast::ProngArm::Case(patterns) = prong.arm { |
|
| 4276 | - | for idx in 0..patterns.len { |
|
| 4277 | - | let pattern = patterns.list[idx]; |
|
| 4221 | + | for pattern in patterns { |
|
| 4278 | 4222 | if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra { |
|
| 4279 | 4223 | if covered[ix] { |
|
| 4280 | 4224 | throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern); |
|
| 4281 | 4225 | } |
|
| 4282 | 4226 | covered[ix] = true; |
| 4286 | 4230 | } |
|
| 4287 | 4231 | } |
|
| 4288 | 4232 | } |
|
| 4289 | 4233 | // Check that all variants are covered. |
|
| 4290 | 4234 | if not state.catchAll { |
|
| 4291 | - | for i in 0..info.variantsLen { |
|
| 4235 | + | for i in 0..info.variants.len { |
|
| 4292 | 4236 | if not covered[i] { |
|
| 4293 | 4237 | throw emitError( |
|
| 4294 | 4238 | self, node, ErrorKind::UnionMatchNonExhaustive(info.variants[i].name) |
|
| 4295 | 4239 | ); |
|
| 4296 | 4240 | } |
|
| 4297 | 4241 | } |
|
| 4298 | - | } else if coveredCount == info.variantsLen as u32 { |
|
| 4242 | + | } else if coveredCount == info.variants.len as u32 { |
|
| 4299 | 4243 | throw emitError(self, node, ErrorKind::UnreachableElse); |
|
| 4300 | 4244 | } |
|
| 4301 | 4245 | return try setNodeType(self, node, matchType); |
|
| 4302 | 4246 | } |
|
| 4303 | 4247 | ||
| 4304 | 4248 | /// Analyze a `match` expression on a generic subject type. Requires exhaustiveness: |
|
| 4305 | 4249 | /// booleans must cover both `true` and `false`, other types require a catch-all. |
|
| 4306 | 4250 | fn resolveMatchGeneric(self: *mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type |
|
| 4307 | 4251 | throws (ResolveError) |
|
| 4308 | 4252 | { |
|
| 4309 | - | let prongs = &sw.prongs; |
|
| 4253 | + | let prongs = sw.prongs; |
|
| 4310 | 4254 | let mut state = MatchState { catchAll: false, isConst: true }; |
|
| 4311 | 4255 | let mut matchType = Type::Never; |
|
| 4312 | 4256 | let mut hasTrue = false; |
|
| 4313 | 4257 | let mut hasFalse = false; |
|
| 4314 | 4258 | let mut hasConstCase = false; |
|
| 4315 | 4259 | ||
| 4316 | - | for i in 0..prongs.len { |
|
| 4317 | - | let prongNode = prongs.list[i]; |
|
| 4260 | + | for prongNode in prongs { |
|
| 4318 | 4261 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 4319 | 4262 | else panic "resolveMatchGeneric: expected match prong"; |
|
| 4320 | 4263 | ||
| 4321 | 4264 | matchType = try resolveMatchProng( |
|
| 4322 | 4265 | self, prongNode, prong, subjectTy, &mut state, matchType, MatchBy::Value |
|
| 4323 | 4266 | ); |
|
| 4324 | 4267 | // Track boolean coverage. Guarded prongs don't count as covering. |
|
| 4325 | 4268 | if let case ast::ProngArm::Case(patterns) = prong.arm { |
|
| 4326 | - | for idx in 0..patterns.len { |
|
| 4327 | - | let p = patterns.list[idx]; |
|
| 4269 | + | for p in patterns { |
|
| 4328 | 4270 | if prong.guard == nil { |
|
| 4329 | 4271 | if let case ast::NodeValue::Bool(val) = p.value { |
|
| 4330 | 4272 | if (val and hasTrue) or (not val and hasFalse) { |
|
| 4331 | 4273 | throw emitError(self, p, ErrorKind::DuplicateMatchPattern); |
|
| 4332 | 4274 | } |
| 4405 | 4347 | bindTy = *inner; |
|
| 4406 | 4348 | } |
|
| 4407 | 4349 | try bindPatternVar(self, pat, bindTy, matchBy); |
|
| 4408 | 4350 | } |
|
| 4409 | 4351 | case ast::ProngArm::Case(patterns) => { |
|
| 4410 | - | for i in 0..patterns.len { |
|
| 4411 | - | try resolveCasePattern(self, patterns.list[i], subjectTy, IdentMode::Compare, matchBy); |
|
| 4352 | + | for pattern in patterns { |
|
| 4353 | + | try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy); |
|
| 4412 | 4354 | } |
|
| 4413 | 4355 | } |
|
| 4414 | 4356 | case ast::ProngArm::Else => {} |
|
| 4415 | 4357 | } |
|
| 4416 | 4358 | if let g = prong.guard { |
| 4521 | 4463 | matchBy: MatchBy |
|
| 4522 | 4464 | ) throws (ResolveError) { |
|
| 4523 | 4465 | match pattern.value { |
|
| 4524 | 4466 | case ast::NodeValue::Call(call) => { |
|
| 4525 | 4467 | // Unlabeled patterns: `S(x, y)`. |
|
| 4526 | - | try checkRecordArity(self, &call.args, recInfo, pattern); |
|
| 4468 | + | try checkRecordArity(self, call.args, recInfo, pattern); |
|
| 4527 | 4469 | ||
| 4528 | 4470 | for i in 0..call.args.len { |
|
| 4529 | - | let binding = call.args.list[i]; |
|
| 4471 | + | let binding = call.args[i]; |
|
| 4530 | 4472 | let fieldType = recInfo.fields[i].fieldType; |
|
| 4531 | 4473 | try bindPatternVar(self, binding, fieldType, matchBy); |
|
| 4532 | 4474 | } |
|
| 4533 | 4475 | } |
|
| 4534 | 4476 | case ast::NodeValue::RecordLit(lit) => { |
|
| 4535 | 4477 | // Labeled patterns: `T { x, y }` or `T { x: binding }`. |
|
| 4536 | 4478 | if not lit.ignoreRest { |
|
| 4537 | - | try checkRecordArity(self, &lit.fields, recInfo, pattern); |
|
| 4479 | + | try checkRecordArity(self, lit.fields, recInfo, pattern); |
|
| 4538 | 4480 | } |
|
| 4539 | - | for i in 0..lit.fields.len { |
|
| 4540 | - | let fieldNode = lit.fields.list[i]; |
|
| 4481 | + | for fieldNode in lit.fields { |
|
| 4541 | 4482 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value |
|
| 4542 | 4483 | else panic "expected RecordLitField"; |
|
| 4543 | 4484 | ||
| 4544 | 4485 | // Brace patterns require labeled fields. |
|
| 4545 | 4486 | let label = field.label else panic "expected labeled field"; |
| 4658 | 4599 | /// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`. |
|
| 4659 | 4600 | fn resolveBuiltinCall( |
|
| 4660 | 4601 | self: *mut Resolver, |
|
| 4661 | 4602 | node: *ast::Node, |
|
| 4662 | 4603 | kind: ast::Builtin, |
|
| 4663 | - | args: *ast::NodeList |
|
| 4604 | + | args: *mut [*ast::Node] |
|
| 4664 | 4605 | ) -> Type throws (ResolveError) { |
|
| 4665 | 4606 | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
|
| 4666 | 4607 | if kind == ast::Builtin::SliceOf { |
|
| 4667 | 4608 | if args.len != 2 and args.len != 3 { |
|
| 4668 | 4609 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 4669 | 4610 | expected: 2, |
|
| 4670 | 4611 | actual: args.len as u32, |
|
| 4671 | 4612 | })); |
|
| 4672 | 4613 | } |
|
| 4673 | - | let ptrType = try visit(self, args.list[0], Type::Unknown); |
|
| 4614 | + | let ptrType = try visit(self, args[0], Type::Unknown); |
|
| 4674 | 4615 | let case Type::Pointer { target, mutable } = ptrType else { |
|
| 4675 | 4616 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
|
| 4676 | 4617 | }; |
|
| 4677 | - | let _ = try checkAssignable(self, args.list[1], Type::U32); |
|
| 4618 | + | let _ = try checkAssignable(self, args[1], Type::U32); |
|
| 4678 | 4619 | if args.len == 3 { |
|
| 4679 | - | let _ = try checkAssignable(self, args.list[2], Type::U32); |
|
| 4620 | + | let _ = try checkAssignable(self, args[2], Type::U32); |
|
| 4680 | 4621 | } |
|
| 4681 | 4622 | return try setNodeType(self, node, Type::Slice { item: target, mutable }); |
|
| 4682 | 4623 | } |
|
| 4683 | 4624 | if args.len != 1 { |
|
| 4684 | 4625 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 4685 | 4626 | expected: 1, |
|
| 4686 | 4627 | actual: args.len as u32, |
|
| 4687 | 4628 | })); |
|
| 4688 | 4629 | } |
|
| 4689 | 4630 | ||
| 4690 | - | let ty = try resolveValueType(self, args.list[0]); |
|
| 4631 | + | let ty = try resolveValueType(self, args[0]); |
|
| 4691 | 4632 | // Ensure the type body is resolved before computing layout. |
|
| 4692 | 4633 | // TODO: Somehow, ensuring the type is resolved should just happen all |
|
| 4693 | 4634 | // the time, lazily. |
|
| 4694 | - | try ensureTypeResolved(self, ty, args.list[0]); |
|
| 4635 | + | try ensureTypeResolved(self, ty, args[0]); |
|
| 4695 | 4636 | // TODO: This should be stored in `symbol` instead of having to recompute it. |
|
| 4696 | 4637 | // That way there's a canonical place to look for code gen. |
|
| 4697 | 4638 | let layout = getTypeLayout(ty); |
|
| 4698 | 4639 | ||
| 4699 | 4640 | // Evaluate the built-in. |
| 4722 | 4663 | /// Validate call arguments against a function type: check argument count, |
|
| 4723 | 4664 | /// type-check each argument, and verify that throwing functions use `try`. |
|
| 4724 | 4665 | fn checkCallArgs(self: *mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
|
| 4725 | 4666 | throws (ResolveError) |
|
| 4726 | 4667 | { |
|
| 4727 | - | if ctx == CallCtx::Normal and info.throwListLen > 0 { |
|
| 4668 | + | if ctx == CallCtx::Normal and info.throwList.len > 0 { |
|
| 4728 | 4669 | throw emitError(self, node, ErrorKind::MissingTry); |
|
| 4729 | 4670 | } |
|
| 4730 | - | if call.args.len != info.paramTypesLen { |
|
| 4671 | + | if call.args.len != info.paramTypes.len as u32 { |
|
| 4731 | 4672 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 4732 | - | expected: info.paramTypesLen, |
|
| 4673 | + | expected: info.paramTypes.len as u32, |
|
| 4733 | 4674 | actual: call.args.len, |
|
| 4734 | 4675 | })); |
|
| 4735 | 4676 | } |
|
| 4736 | 4677 | for i in 0..call.args.len { |
|
| 4737 | - | assert i < MAX_FN_PARAMS; |
|
| 4738 | - | ||
| 4739 | - | let argNode = call.args.list[i]; |
|
| 4678 | + | let argNode = call.args[i]; |
|
| 4740 | 4679 | let expectedTy = *info.paramTypes[i]; |
|
| 4741 | 4680 | ||
| 4742 | 4681 | try checkAssignable(self, argNode, expectedTy); |
|
| 4743 | 4682 | } |
|
| 4744 | 4683 | } |
| 4753 | 4692 | let subjectTy = autoDeref(parentTy); |
|
| 4754 | 4693 | ||
| 4755 | 4694 | if let case Type::Slice { item, mutable } = subjectTy { |
|
| 4756 | 4695 | let methodName = try nodeName(self, access.child); |
|
| 4757 | 4696 | if methodName == "append" { |
|
| 4758 | - | return try resolveSliceAppend(self, node, access.parent, &call.args, item, mutable); |
|
| 4697 | + | return try resolveSliceAppend(self, node, access.parent, call.args, item, mutable); |
|
| 4759 | 4698 | } |
|
| 4760 | 4699 | if methodName == "delete" { |
|
| 4761 | - | return try resolveSliceDelete(self, node, access.parent, &call.args, item, mutable); |
|
| 4700 | + | return try resolveSliceDelete(self, node, access.parent, call.args, item, mutable); |
|
| 4762 | 4701 | } |
|
| 4763 | 4702 | } |
|
| 4764 | 4703 | } |
|
| 4765 | 4704 | let calleeTy = try infer(self, call.callee); |
|
| 4766 | 4705 |
| 4825 | 4764 | /// Resolve `slice.append(val, allocator)`. |
|
| 4826 | 4765 | fn resolveSliceAppend( |
|
| 4827 | 4766 | self: *mut Resolver, |
|
| 4828 | 4767 | node: *ast::Node, |
|
| 4829 | 4768 | parent: *ast::Node, |
|
| 4830 | - | args: *ast::NodeList, |
|
| 4769 | + | args: *mut [*ast::Node], |
|
| 4831 | 4770 | elemType: *Type, |
|
| 4832 | 4771 | mutable: bool |
|
| 4833 | 4772 | ) -> Type throws (ResolveError) { |
|
| 4834 | 4773 | if not mutable { |
|
| 4835 | 4774 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 4839 | 4778 | expected: 2, |
|
| 4840 | 4779 | actual: args.len as u32, |
|
| 4841 | 4780 | })); |
|
| 4842 | 4781 | } |
|
| 4843 | 4782 | // First argument must be assignable to the element type. |
|
| 4844 | - | try checkAssignable(self, args.list[0], *elemType); |
|
| 4783 | + | try checkAssignable(self, args[0], *elemType); |
|
| 4845 | 4784 | // Second argument: the allocator. We accept any type -- the lowerer |
|
| 4846 | 4785 | // reads .func and .ctx at fixed offsets (structurally typed). |
|
| 4847 | - | try visit(self, args.list[1], Type::Unknown); |
|
| 4786 | + | try visit(self, args[1], Type::Unknown); |
|
| 4848 | 4787 | self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
|
| 4849 | 4788 | ||
| 4850 | 4789 | return try setNodeType(self, node, Type::Void); |
|
| 4851 | 4790 | } |
|
| 4852 | 4791 | ||
| 4853 | 4792 | /// Resolve `slice.delete(index)`. |
|
| 4854 | 4793 | fn resolveSliceDelete( |
|
| 4855 | 4794 | self: *mut Resolver, |
|
| 4856 | 4795 | node: *ast::Node, |
|
| 4857 | 4796 | parent: *ast::Node, |
|
| 4858 | - | args: *ast::NodeList, |
|
| 4797 | + | args: *mut [*ast::Node], |
|
| 4859 | 4798 | elemType: *Type, |
|
| 4860 | 4799 | mutable: bool |
|
| 4861 | 4800 | ) -> Type throws (ResolveError) { |
|
| 4862 | 4801 | if not mutable { |
|
| 4863 | 4802 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 4866 | 4805 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 4867 | 4806 | expected: 1, |
|
| 4868 | 4807 | actual: args.len as u32, |
|
| 4869 | 4808 | })); |
|
| 4870 | 4809 | } |
|
| 4871 | - | try checkAssignable(self, args.list[0], Type::U32); |
|
| 4810 | + | try checkAssignable(self, args[0], Type::U32); |
|
| 4872 | 4811 | self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType }; |
|
| 4873 | 4812 | ||
| 4874 | 4813 | return try setNodeType(self, node, Type::Void); |
|
| 4875 | 4814 | } |
|
| 4876 | 4815 |
| 4955 | 4894 | } |
|
| 4956 | 4895 | } |
|
| 4957 | 4896 | ||
| 4958 | 4897 | /// Find a record field by name. |
|
| 4959 | 4898 | fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 { |
|
| 4960 | - | for i in 0..s.fieldsLen { |
|
| 4899 | + | for i in 0..s.fields.len { |
|
| 4961 | 4900 | if let name = s.fields[i].name { |
|
| 4962 | 4901 | if name == fieldName { |
|
| 4963 | 4902 | return i; |
|
| 4964 | 4903 | } |
|
| 4965 | 4904 | } |
| 4987 | 4926 | // Check if this variant expects a payload. |
|
| 4988 | 4927 | let payloadType = variant.valueType; |
|
| 4989 | 4928 | if payloadType != Type::Void { |
|
| 4990 | 4929 | let recInfo = getRecord(payloadType) |
|
| 4991 | 4930 | else panic "resolveUnionVariantConstructor: payload is not a record"; |
|
| 4992 | - | try checkRecordConstructorArgs(self, node, &call.args, recInfo); |
|
| 4931 | + | try checkRecordConstructorArgs(self, node, call.args, recInfo); |
|
| 4993 | 4932 | } else { |
|
| 4994 | 4933 | if call.args.len > 0 { |
|
| 4995 | 4934 | throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
|
| 4996 | 4935 | } |
|
| 4997 | 4936 | } |
| 5007 | 4946 | throws (ResolveError) |
|
| 5008 | 4947 | { |
|
| 5009 | 4948 | let case NominalType::Record(recInfo) = *recordType |
|
| 5010 | 4949 | else panic "resolveRecordConstructorCall: not a record type"; |
|
| 5011 | 4950 | ||
| 5012 | - | try checkRecordConstructorArgs(self, node, &call.args, recInfo); |
|
| 4951 | + | try checkRecordConstructorArgs(self, node, call.args, recInfo); |
|
| 5013 | 4952 | return try setNodeType(self, node, Type::Nominal(recordType)); |
|
| 5014 | 4953 | } |
|
| 5015 | 4954 | ||
| 5016 | 4955 | /// Resolve the type name of a record literal, handling both record types and |
|
| 5017 | 4956 | /// union variant payloads like `Union::Variant { ... }`. |
| 5081 | 5020 | // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax. |
|
| 5082 | 5021 | if not recordType.labeled { |
|
| 5083 | 5022 | throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch); |
|
| 5084 | 5023 | } |
|
| 5085 | 5024 | // Check field count. With `{ .. }` syntax, fewer fields are allowed. |
|
| 5086 | - | if lit.fields.len > recordType.fieldsLen { |
|
| 5025 | + | if lit.fields.len > recordType.fields.len { |
|
| 5087 | 5026 | throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 5088 | - | expected: recordType.fieldsLen, |
|
| 5027 | + | expected: recordType.fields.len as u32, |
|
| 5089 | 5028 | actual: lit.fields.len, |
|
| 5090 | 5029 | })); |
|
| 5091 | 5030 | } |
|
| 5092 | - | if not lit.ignoreRest and lit.fields.len < recordType.fieldsLen { |
|
| 5031 | + | if not lit.ignoreRest and lit.fields.len < recordType.fields.len { |
|
| 5093 | 5032 | let missingName = recordType.fields[lit.fields.len].name else panic; |
|
| 5094 | 5033 | throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName)); |
|
| 5095 | 5034 | } |
|
| 5096 | 5035 | ||
| 5097 | 5036 | // Fields must be in declaration order. |
|
| 5098 | 5037 | for idx in 0..lit.fields.len { |
|
| 5099 | - | let fieldNode = lit.fields.list[idx]; |
|
| 5038 | + | let fieldNode = lit.fields[idx]; |
|
| 5100 | 5039 | let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value |
|
| 5101 | 5040 | else panic "resolveRecordLit: expected field node value"; |
|
| 5102 | 5041 | let label = fieldArg.label |
|
| 5103 | 5042 | else panic "resolveRecordLit: expected labeled field"; |
|
| 5104 | 5043 | let fieldName = try nodeName(self, label); |
| 5137 | 5076 | let targetInfo = hintInfo else { |
|
| 5138 | 5077 | throw emitError(self, node, ErrorKind::CannotInferType); |
|
| 5139 | 5078 | }; |
|
| 5140 | 5079 | ||
| 5141 | 5080 | // Check field count. |
|
| 5142 | - | if lit.fields.len != targetInfo.fieldsLen { |
|
| 5143 | - | if lit.fields.len < targetInfo.fieldsLen { |
|
| 5081 | + | if lit.fields.len != targetInfo.fields.len { |
|
| 5082 | + | if lit.fields.len < targetInfo.fields.len { |
|
| 5144 | 5083 | let missingName = targetInfo.fields[lit.fields.len].name else panic; |
|
| 5145 | 5084 | throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName)); |
|
| 5146 | 5085 | } else { |
|
| 5147 | 5086 | throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 5148 | - | expected: targetInfo.fieldsLen, |
|
| 5087 | + | expected: targetInfo.fields.len as u32, |
|
| 5149 | 5088 | actual: lit.fields.len, |
|
| 5150 | 5089 | })); |
|
| 5151 | 5090 | } |
|
| 5152 | 5091 | } |
|
| 5153 | 5092 | ||
| 5154 | 5093 | // Fields must be in declaration order. |
|
| 5155 | 5094 | for idx in 0..lit.fields.len { |
|
| 5156 | - | let fieldNode = lit.fields.list[idx]; |
|
| 5095 | + | let fieldNode = lit.fields[idx]; |
|
| 5157 | 5096 | let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value |
|
| 5158 | 5097 | else panic "resolveAnonRecordLit: expected field node value"; |
|
| 5159 | 5098 | let label = fieldArg.label |
|
| 5160 | 5099 | else panic "resolveAnonRecordLit: expected labeled field"; |
|
| 5161 | 5100 | let fieldName = try nodeName(self, label); |
| 5176 | 5115 | } |
|
| 5177 | 5116 | return try setNodeType(self, node, innerHint); |
|
| 5178 | 5117 | } |
|
| 5179 | 5118 | ||
| 5180 | 5119 | /// Analyze an array literal expression. |
|
| 5181 | - | fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *ast::NodeList, hint: Type) -> Type |
|
| 5120 | + | fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type |
|
| 5182 | 5121 | throws (ResolveError) |
|
| 5183 | 5122 | { |
|
| 5184 | 5123 | let length = items.len; |
|
| 5185 | 5124 | let mut expectedTy: Type = Type::Unknown; |
|
| 5186 | 5125 | ||
| 5187 | 5126 | if let case Type::Array(ary) = hint { |
|
| 5188 | 5127 | expectedTy = *ary.item; |
|
| 5189 | 5128 | }; |
|
| 5190 | - | for i in 0..length { |
|
| 5191 | - | let itemNode = items.list[i]; |
|
| 5129 | + | for itemNode in items { |
|
| 5192 | 5130 | let itemTy = try visit(self, itemNode, expectedTy); |
|
| 5193 | 5131 | assert itemTy != Type::Unknown; |
|
| 5194 | 5132 | ||
| 5195 | 5133 | // Set the expected type to the first type we encounter. |
|
| 5196 | 5134 | if expectedTy == Type::Unknown { |
| 5226 | 5164 | access: ast::Access, |
|
| 5227 | 5165 | unionType: UnionType, |
|
| 5228 | 5166 | variantName: *[u8] |
|
| 5229 | 5167 | ) -> *mut Symbol throws (ResolveError) { |
|
| 5230 | 5168 | // Look up the variant in the union's nominal type. |
|
| 5231 | - | for i in 0..unionType.variantsLen { |
|
| 5169 | + | for i in 0..unionType.variants.len { |
|
| 5232 | 5170 | let variant = &unionType.variants[i]; |
|
| 5233 | 5171 | if variant.name == variantName { |
|
| 5234 | 5172 | let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data |
|
| 5235 | 5173 | else panic "resolveUnionVariantAccess: expected variant symbol"; |
|
| 5236 | 5174 |
| 5654 | 5592 | let calleeTy = typeFor(self, callExpr.callee) |
|
| 5655 | 5593 | else return try setNodeType(self, node, resultTy); |
|
| 5656 | 5594 | let case Type::Fn(calleeInfo) = calleeTy |
|
| 5657 | 5595 | else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing); |
|
| 5658 | 5596 | ||
| 5659 | - | if calleeInfo.throwListLen == 0 { |
|
| 5597 | + | if calleeInfo.throwList.len == 0 { |
|
| 5660 | 5598 | throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing); |
|
| 5661 | 5599 | } |
|
| 5662 | 5600 | // If we're not catching the error, nor panicking on error, nor returning |
|
| 5663 | 5601 | // optional, then the current function must be able to propagate it. |
|
| 5664 | 5602 | let mut tryResultTy = resultTy; |
| 5673 | 5611 | // `try ... catch` -- one or more catch clauses. |
|
| 5674 | 5612 | tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint); |
|
| 5675 | 5613 | } else if not tryExpr.shouldPanic { |
|
| 5676 | 5614 | let fnInfo = self.currentFn |
|
| 5677 | 5615 | else throw emitError(self, node, ErrorKind::TryRequiresThrows); |
|
| 5678 | - | if fnInfo.throwListLen == 0 { |
|
| 5616 | + | if fnInfo.throwList.len == 0 { |
|
| 5679 | 5617 | throw emitError(self, node, ErrorKind::TryRequiresThrows); |
|
| 5680 | 5618 | } |
|
| 5681 | 5619 | // Check that *all* thrown errors of the callee can be propagated by |
|
| 5682 | 5620 | // the caller. |
|
| 5683 | - | for i in 0..calleeInfo.throwListLen { |
|
| 5684 | - | let throwTy = calleeInfo.throwList[i]; |
|
| 5621 | + | for throwTy in calleeInfo.throwList { |
|
| 5685 | 5622 | let mut found = false; |
|
| 5686 | 5623 | ||
| 5687 | - | for j in 0..fnInfo.throwListLen { |
|
| 5688 | - | if fnInfo.throwList[j] == throwTy { |
|
| 5624 | + | for callerThrowTy in fnInfo.throwList { |
|
| 5625 | + | if callerThrowTy == throwTy { |
|
| 5689 | 5626 | found = true; |
|
| 5690 | 5627 | break; |
|
| 5691 | 5628 | } |
|
| 5692 | 5629 | } |
|
| 5693 | 5630 | if not found { |
| 5715 | 5652 | /// body and returns the result type. Multi-error callees with inferred bindings |
|
| 5716 | 5653 | /// are rejected; you must use typed catches. |
|
| 5717 | 5654 | fn resolveTryCatches( |
|
| 5718 | 5655 | self: *mut Resolver, |
|
| 5719 | 5656 | node: *ast::Node, |
|
| 5720 | - | catches: ast::NodeList, |
|
| 5657 | + | catches: *mut [*ast::Node], |
|
| 5721 | 5658 | calleeInfo: *FnType, |
|
| 5722 | 5659 | resultTy: Type, |
|
| 5723 | 5660 | hint: Type |
|
| 5724 | 5661 | ) -> Type throws (ResolveError) { |
|
| 5725 | - | let firstNode = catches.list[0]; |
|
| 5662 | + | let firstNode = catches[0]; |
|
| 5726 | 5663 | let case ast::NodeValue::CatchClause(first) = firstNode.value else |
|
| 5727 | 5664 | throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode)); |
|
| 5728 | 5665 | ||
| 5729 | 5666 | // Typed catches: dispatch to dedicated handler. |
|
| 5730 | 5667 | if first.typeNode != nil { |
|
| 5731 | 5668 | return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint); |
|
| 5732 | 5669 | } |
|
| 5733 | 5670 | // Single untyped catch clause. |
|
| 5734 | 5671 | if let binding = first.binding { |
|
| 5735 | - | if calleeInfo.throwListLen > 1 { |
|
| 5672 | + | if calleeInfo.throwList.len > 1 { |
|
| 5736 | 5673 | throw emitError(self, binding, ErrorKind::TryCatchMultiError); |
|
| 5737 | 5674 | } |
|
| 5738 | 5675 | enterScope(self, node); |
|
| 5739 | 5676 | ||
| 5740 | 5677 | let errTy = *calleeInfo.throwList[0]; |
| 5755 | 5692 | /// Validates that each type annotation is in the callee's throw list, that |
|
| 5756 | 5693 | /// there are no duplicate catch types, and that the clauses are exhaustive. |
|
| 5757 | 5694 | fn resolveTypedCatches( |
|
| 5758 | 5695 | self: *mut Resolver, |
|
| 5759 | 5696 | node: *ast::Node, |
|
| 5760 | - | catches: ast::NodeList, |
|
| 5697 | + | catches: *mut [*ast::Node], |
|
| 5761 | 5698 | calleeInfo: *FnType, |
|
| 5762 | 5699 | resultTy: Type, |
|
| 5763 | 5700 | hint: Type |
|
| 5764 | 5701 | ) -> Type throws (ResolveError) { |
|
| 5765 | 5702 | // Track which of the callee's throw types have been covered. |
|
| 5766 | 5703 | let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS]; |
|
| 5767 | 5704 | let mut hasCatchAll = false; |
|
| 5768 | 5705 | ||
| 5769 | - | for i in 0..catches.len { |
|
| 5770 | - | let clauseNode = catches.list[i]; |
|
| 5706 | + | for clauseNode in catches { |
|
| 5771 | 5707 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value else |
|
| 5772 | 5708 | throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode)); |
|
| 5773 | 5709 | ||
| 5774 | 5710 | if let typeNode = clause.typeNode { |
|
| 5775 | 5711 | // Typed catch clause: validate against callee's throw list. |
|
| 5776 | 5712 | let errTy = try infer(self, typeNode); |
|
| 5777 | 5713 | let mut foundIdx: ?u32 = nil; |
|
| 5778 | 5714 | ||
| 5779 | - | for j in 0..calleeInfo.throwListLen { |
|
| 5715 | + | for j in 0..calleeInfo.throwList.len { |
|
| 5780 | 5716 | if errTy == *calleeInfo.throwList[j] { |
|
| 5781 | 5717 | foundIdx = j; |
|
| 5782 | 5718 | break; |
|
| 5783 | 5719 | } |
|
| 5784 | 5720 | } |
| 5808 | 5744 | try checkCatchBody(self, clause.body, resultTy, hint); |
|
| 5809 | 5745 | } |
|
| 5810 | 5746 | ||
| 5811 | 5747 | // Check exhaustiveness: all callee error types must be covered. |
|
| 5812 | 5748 | if not hasCatchAll { |
|
| 5813 | - | for i in 0..calleeInfo.throwListLen { |
|
| 5749 | + | for i in 0..calleeInfo.throwList.len { |
|
| 5814 | 5750 | if not covered[i] { |
|
| 5815 | 5751 | throw emitError(self, node, ErrorKind::TryCatchNonExhaustive); |
|
| 5816 | 5752 | } |
|
| 5817 | 5753 | } |
|
| 5818 | 5754 | } |
| 5823 | 5759 | fn resolveThrow(self: *mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type |
|
| 5824 | 5760 | throws (ResolveError) |
|
| 5825 | 5761 | { |
|
| 5826 | 5762 | let fnInfo = self.currentFn |
|
| 5827 | 5763 | else throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
|
| 5828 | - | if fnInfo.throwListLen == 0 { |
|
| 5764 | + | if fnInfo.throwList.len == 0 { |
|
| 5829 | 5765 | throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
|
| 5830 | 5766 | } |
|
| 5831 | 5767 | let throwTy = try infer(self, expr); |
|
| 5832 | - | for i in 0..fnInfo.throwListLen { |
|
| 5833 | - | if let coerce = isAssignable(self, *fnInfo.throwList[i], throwTy, expr) { |
|
| 5768 | + | for errTy in fnInfo.throwList { |
|
| 5769 | + | if let coerce = isAssignable(self, *errTy, throwTy, expr) { |
|
| 5834 | 5770 | try setNodeCoercion(self, expr, coerce); |
|
| 5835 | 5771 | return try setNodeType(self, node, Type::Never); |
|
| 5836 | 5772 | } |
|
| 5837 | 5773 | } |
|
| 5838 | 5774 | throw emitError(self, expr, ErrorKind::ThrowIncompatibleError); |
| 5850 | 5786 | let _actualTy = try checkAssignable(self, val, expected); |
|
| 5851 | 5787 | } else if expected != Type::Void { |
|
| 5852 | 5788 | throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void }); |
|
| 5853 | 5789 | } |
|
| 5854 | 5790 | // In throwing functions, return values are wrapped in the success variant. |
|
| 5855 | - | if f.throwListLen > 0 { |
|
| 5791 | + | if f.throwList.len > 0 { |
|
| 5856 | 5792 | try setNodeCoercion(self, node, Coercion::ResultWrap); |
|
| 5857 | 5793 | } |
|
| 5858 | 5794 | return try setNodeType(self, node, Type::Never); |
|
| 5859 | 5795 | } |
|
| 5860 | 5796 |
| 6059 | 5995 | let recordType = try resolveRecordFields(self, node, fields, labeled); |
|
| 6060 | 5996 | let nominalTy = allocNominalType(self, NominalType::Record(recordType)); |
|
| 6061 | 5997 | return Type::Nominal(nominalTy); |
|
| 6062 | 5998 | } |
|
| 6063 | 5999 | case ast::TypeSig::Fn(t) => { |
|
| 6064 | - | let mut fnType = FnType { |
|
| 6065 | - | paramTypes: undefined, |
|
| 6066 | - | paramTypesLen: 0, |
|
| 6067 | - | returnType: allocType(self, Type::Void), |
|
| 6068 | - | throwList: undefined, |
|
| 6069 | - | throwListLen: 0, |
|
| 6070 | - | localCount: 0, |
|
| 6071 | - | }; |
|
| 6072 | - | if t.params.len > fnType.paramTypes.len { |
|
| 6000 | + | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 6001 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 6002 | + | let mut throwList: *mut [*Type] = &mut []; |
|
| 6003 | + | ||
| 6004 | + | if t.params.len > MAX_FN_PARAMS { |
|
| 6073 | 6005 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
|
| 6074 | - | expected: fnType.paramTypes.len, |
|
| 6006 | + | expected: MAX_FN_PARAMS, |
|
| 6075 | 6007 | actual: t.params.len, |
|
| 6076 | 6008 | })); |
|
| 6077 | 6009 | } |
|
| 6078 | - | if t.throwList.len > fnType.throwList.len { |
|
| 6010 | + | if t.throwList.len > MAX_FN_THROWS { |
|
| 6079 | 6011 | throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch { |
|
| 6080 | - | expected: fnType.throwList.len, |
|
| 6012 | + | expected: MAX_FN_THROWS, |
|
| 6081 | 6013 | actual: t.throwList.len, |
|
| 6082 | 6014 | })); |
|
| 6083 | 6015 | } |
|
| 6084 | 6016 | ||
| 6085 | - | for i in 0..t.params.len { |
|
| 6086 | - | let paramTy = try infer(self, t.params.list[i]); |
|
| 6087 | - | ||
| 6088 | - | fnType.paramTypes[fnType.paramTypesLen] = allocType(self, paramTy); |
|
| 6089 | - | fnType.paramTypesLen += 1; |
|
| 6017 | + | for paramNode in t.params { |
|
| 6018 | + | let paramTy = try infer(self, paramNode); |
|
| 6019 | + | paramTypes.append(allocType(self, paramTy), a); |
|
| 6090 | 6020 | } |
|
| 6091 | - | for i in 0..t.throwList.len { |
|
| 6092 | - | let tyNode = t.throwList.list[i]; |
|
| 6021 | + | for tyNode in t.throwList { |
|
| 6093 | 6022 | let throwTy = try infer(self, tyNode); |
|
| 6094 | - | ||
| 6095 | - | fnType.throwList[fnType.throwListLen] = allocType(self, throwTy); |
|
| 6096 | - | fnType.throwListLen += 1; |
|
| 6023 | + | throwList.append(allocType(self, throwTy), a); |
|
| 6097 | 6024 | } |
|
| 6025 | + | let mut retType = allocType(self, Type::Void); |
|
| 6098 | 6026 | if let ret = t.returnType { |
|
| 6099 | - | fnType.returnType = allocType(self, try infer(self, ret)); |
|
| 6100 | - | } else { |
|
| 6101 | - | fnType.returnType = allocType(self, Type::Void); |
|
| 6027 | + | retType = allocType(self, try infer(self, ret)); |
|
| 6102 | 6028 | } |
|
| 6029 | + | let fnType = FnType { |
|
| 6030 | + | paramTypes: ¶mTypes[..], |
|
| 6031 | + | returnType: retType, |
|
| 6032 | + | throwList: &throwList[..], |
|
| 6033 | + | localCount: 0, |
|
| 6034 | + | }; |
|
| 6103 | 6035 | return Type::Fn(allocFnType(self, fnType)); |
|
| 6104 | 6036 | } |
|
| 6105 | 6037 | case ast::TypeSig::TraitObject { traitName, mutable } => { |
|
| 6106 | 6038 | let sym = try resolveNamePath(self, traitName); |
|
| 6107 | 6039 | let case SymbolData::Trait(traitInfo) = sym.data |
| 6126 | 6058 | ||
| 6127 | 6059 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
|
| 6128 | 6060 | pub fn resolveExpr( |
|
| 6129 | 6061 | self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena |
|
| 6130 | 6062 | ) -> Diagnostics throws (ResolveError) { |
|
| 6131 | - | let mut bodyStmts = ast::nodeList(arena, 1); |
|
| 6063 | + | let a = ast::nodeAllocator(arena); |
|
| 6064 | + | let mut bodyStmts = ast::nodeSlice(arena, 1); |
|
| 6132 | 6065 | let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr)); |
|
| 6133 | - | ast::nodeListPush(&mut bodyStmts, exprStmt); |
|
| 6066 | + | bodyStmts.append(exprStmt, a); |
|
| 6134 | 6067 | let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts); |
|
| 6135 | 6068 | ||
| 6136 | 6069 | let case ast::NodeValue::Block(block) = module.modBody.value |
|
| 6137 | 6070 | else panic "resolveExpr: expected block for module body"; |
|
| 6138 | 6071 | enterScope(self, module.modBody); |
| 6169 | 6102 | /// and scopes for them, and also binds type names in each module so that cross-module |
|
| 6170 | 6103 | /// type references work regardless of declaration order. |
|
| 6171 | 6104 | fn resolveModuleGraph(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 6172 | 6105 | try bindTypeNames(self, block); |
|
| 6173 | 6106 | ||
| 6174 | - | for i in 0..block.statements.len { |
|
| 6175 | - | let node = block.statements.list[i]; |
|
| 6107 | + | for node in block.statements { |
|
| 6176 | 6108 | if let case ast::NodeValue::Mod(decl) = node.value { |
|
| 6177 | 6109 | try resolveModGraph(self, node, decl); |
|
| 6178 | 6110 | } |
|
| 6179 | 6111 | } |
|
| 6180 | 6112 | } |
|
| 6181 | 6113 | ||
| 6182 | 6114 | /// Bind all type names in a module. |
|
| 6183 | 6115 | /// Skips declarations that have already been bound. |
|
| 6184 | 6116 | fn bindTypeNames(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 6185 | - | for i in 0..block.statements.len { |
|
| 6186 | - | let node = block.statements.list[i]; |
|
| 6117 | + | for node in block.statements { |
|
| 6187 | 6118 | match node.value { |
|
| 6188 | 6119 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 6189 | 6120 | if symbolFor(self, node) == nil { |
|
| 6190 | 6121 | try bindTypeName(self, node, decl.name, decl.attrs) catch {}; |
|
| 6191 | 6122 | } |
| 6205 | 6136 | } |
|
| 6206 | 6137 | } |
|
| 6207 | 6138 | ||
| 6208 | 6139 | /// Resolve all type bodies in a module. |
|
| 6209 | 6140 | fn resolveTypeBodies(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 6210 | - | for i in 0..block.statements.len { |
|
| 6211 | - | let node = block.statements.list[i]; |
|
| 6141 | + | for node in block.statements { |
|
| 6212 | 6142 | match node.value { |
|
| 6213 | 6143 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 6214 | 6144 | try resolveRecordBody(self, node, decl) catch { |
|
| 6215 | 6145 | // Continue resolving other types even if one fails. |
|
| 6216 | 6146 | }; |
| 6219 | 6149 | try resolveUnionBody(self, node, decl) catch { |
|
| 6220 | 6150 | // Continue resolving other types even if one fails. |
|
| 6221 | 6151 | }; |
|
| 6222 | 6152 | } |
|
| 6223 | 6153 | case ast::NodeValue::TraitDecl { supertraits, methods, .. } => { |
|
| 6224 | - | try resolveTraitBody(self, node, &supertraits, &methods) catch { |
|
| 6154 | + | try resolveTraitBody(self, node, supertraits, methods) catch { |
|
| 6225 | 6155 | // Continue resolving other types even if one fails. |
|
| 6226 | 6156 | }; |
|
| 6227 | 6157 | } |
|
| 6228 | 6158 | else => { |
|
| 6229 | 6159 | // Ignore other declarations. |
| 6243 | 6173 | fn resolveModuleDecls(res: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 6244 | 6174 | // Phase 1: Bind all type names as placeholders. |
|
| 6245 | 6175 | try bindTypeNames(res, block); |
|
| 6246 | 6176 | // Phase 2: Process non-wildcard imports so module names are available |
|
| 6247 | 6177 | // for submodule resolution and type lookups. |
|
| 6248 | - | for i in 0..block.statements.len { |
|
| 6249 | - | let node = block.statements.list[i]; |
|
| 6178 | + | for node in block.statements { |
|
| 6250 | 6179 | if let case ast::NodeValue::Use(decl) = node.value { |
|
| 6251 | 6180 | if not decl.wildcard { |
|
| 6252 | 6181 | try resolveUse(res, node, decl); |
|
| 6253 | 6182 | } |
|
| 6254 | 6183 | } |
|
| 6255 | 6184 | } |
|
| 6256 | 6185 | // Phase 3: Process submodule declarations -- recurses into child modules. |
|
| 6257 | 6186 | // Child modules may trigger on-demand type resolution via |
|
| 6258 | 6187 | // [`ensureNominalResolved`] which switches to the declaring module's |
|
| 6259 | 6188 | // scope. |
|
| 6260 | - | for i in 0..block.statements.len { |
|
| 6261 | - | let node = block.statements.list[i]; |
|
| 6189 | + | for node in block.statements { |
|
| 6262 | 6190 | if let case ast::NodeValue::Mod(decl) = node.value { |
|
| 6263 | 6191 | try resolveModDecl(res, node, decl); |
|
| 6264 | 6192 | } |
|
| 6265 | 6193 | } |
|
| 6266 | 6194 | // Phase 3b: Process wildcard imports after submodules are resolved, |
|
| 6267 | 6195 | // so that transitive re-exports (pub use foo::*) are visible. |
|
| 6268 | - | for i in 0..block.statements.len { |
|
| 6269 | - | let node = block.statements.list[i]; |
|
| 6196 | + | for node in block.statements { |
|
| 6270 | 6197 | if let case ast::NodeValue::Use(decl) = node.value { |
|
| 6271 | 6198 | if decl.wildcard { |
|
| 6272 | 6199 | try resolveUse(res, node, decl); |
|
| 6273 | 6200 | } |
|
| 6274 | 6201 | } |
|
| 6275 | 6202 | } |
|
| 6276 | 6203 | // Phase 4: Bind function signatures so that function references are |
|
| 6277 | 6204 | // available in constant and static initializers. |
|
| 6278 | - | for i in 0..block.statements.len { |
|
| 6279 | - | let node = block.statements.list[i]; |
|
| 6205 | + | for node in block.statements { |
|
| 6280 | 6206 | if let case ast::NodeValue::FnDecl(decl) = node.value { |
|
| 6281 | 6207 | try resolveFnDecl(res, node, decl); |
|
| 6282 | 6208 | } |
|
| 6283 | 6209 | } |
|
| 6284 | 6210 | // Phase 5: Process constants. |
|
| 6285 | - | for i in 0..block.statements.len { |
|
| 6286 | - | let node = block.statements.list[i]; |
|
| 6211 | + | for node in block.statements { |
|
| 6287 | 6212 | if let case ast::NodeValue::ConstDecl(_) = node.value { |
|
| 6288 | 6213 | try infer(res, node); |
|
| 6289 | 6214 | } |
|
| 6290 | 6215 | } |
|
| 6291 | 6216 | // Phase 6: Resolve type bodies (record fields, union variants). |
|
| 6292 | 6217 | try resolveTypeBodies(res, block); |
|
| 6293 | 6218 | // Phase 7: Process all other declarations (statics, etc.). |
|
| 6294 | - | for i in 0..block.statements.len { |
|
| 6219 | + | for stmt in block.statements { |
|
| 6295 | 6220 | // TODO: This error should propagate, since we catch errors in the `resolveModule` entry point. |
|
| 6296 | - | try visitDecl(res, block.statements.list[i]) catch { |
|
| 6221 | + | try visitDecl(res, stmt) catch { |
|
| 6297 | 6222 | break; |
|
| 6298 | 6223 | }; |
|
| 6299 | 6224 | } |
|
| 6300 | 6225 | } |
|
| 6301 | 6226 | ||
| 6302 | 6227 | /// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules. |
|
| 6303 | 6228 | fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 6304 | - | for i in 0..block.statements.len { |
|
| 6305 | - | let stmt = block.statements.list[i]; |
|
| 6229 | + | for stmt in block.statements { |
|
| 6306 | 6230 | try visitDef(self, stmt); |
|
| 6307 | 6231 | } |
|
| 6308 | 6232 | } |
|
| 6309 | 6233 | ||
| 6310 | 6234 | /// Resolve all packages. |
| 6345 | 6269 | let case ast::NodeValue::Block(block) = node.value |
|
| 6346 | 6270 | else panic "resolvePackage: expected block for module root"; |
|
| 6347 | 6271 | ||
| 6348 | 6272 | // Module graph analysis phase: bind all module name symbols and scopes. |
|
| 6349 | 6273 | try resolveModuleGraph(self, &block) catch { |
|
| 6350 | - | assert self.errors.listLen > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6274 | + | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6351 | 6275 | return Diagnostics { errors: self.errors }; |
|
| 6352 | 6276 | }; |
|
| 6353 | 6277 | ||
| 6354 | 6278 | // Declaration phase: bind all names and analyze top-level declarations. |
|
| 6355 | 6279 | try resolveModuleDecls(self, &block) catch { |
|
| 6356 | - | assert self.errors.listLen > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6280 | + | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6357 | 6281 | }; |
|
| 6358 | - | if self.errors.listLen > 0 { |
|
| 6282 | + | if self.errors.len > 0 { |
|
| 6359 | 6283 | return Diagnostics { errors: self.errors }; |
|
| 6360 | 6284 | } |
|
| 6361 | 6285 | ||
| 6362 | 6286 | // Definition phase: analyze function bodies and sub-module definitions. |
|
| 6363 | 6287 | try resolveModuleDefs(self, &block) catch { |
|
| 6364 | - | assert self.errors.listLen > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6288 | + | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 6365 | 6289 | }; |
|
| 6366 | 6290 | try setNodeType(self, node, Type::Void); |
|
| 6367 | 6291 | ||
| 6368 | 6292 | return Diagnostics { errors: self.errors }; |
|
| 6369 | 6293 | } |
lib/std/lang/resolver/printer.rad
+13 -13
| 110 | 110 | io::print("?"); |
|
| 111 | 111 | printTypeBody(*inner, brief); |
|
| 112 | 112 | } |
|
| 113 | 113 | case super::Type::Fn(fnType) => { |
|
| 114 | 114 | io::print("fn("); |
|
| 115 | - | for i in 0..fnType.paramTypesLen { |
|
| 115 | + | for i in 0..fnType.paramTypes.len { |
|
| 116 | 116 | if i > 0 { |
|
| 117 | 117 | io::print(", "); |
|
| 118 | 118 | } |
|
| 119 | 119 | printTypeName(*fnType.paramTypes[i]); |
|
| 120 | 120 | } |
|
| 121 | 121 | io::print(")"); |
|
| 122 | 122 | io::print(" -> "); |
|
| 123 | 123 | printTypeName(*fnType.returnType); |
|
| 124 | - | if fnType.throwListLen > 0 { |
|
| 124 | + | if fnType.throwList.len > 0 { |
|
| 125 | 125 | io::print(" throws "); |
|
| 126 | - | for i in 0..fnType.throwListLen { |
|
| 126 | + | for i in 0..fnType.throwList.len { |
|
| 127 | 127 | if i > 0 { |
|
| 128 | 128 | io::print(", "); |
|
| 129 | 129 | } |
|
| 130 | 130 | printTypeName(*fnType.throwList[i]); |
|
| 131 | 131 | } |
| 164 | 164 | case super::NominalType::Placeholder(_) => { |
|
| 165 | 165 | io::print("<placeholder>"); |
|
| 166 | 166 | } |
|
| 167 | 167 | case super::NominalType::Record(recordType) => { |
|
| 168 | 168 | io::print("record {"); |
|
| 169 | - | if recordType.fieldsLen > 0 { |
|
| 169 | + | if recordType.fields.len > 0 { |
|
| 170 | 170 | io::print(" "); |
|
| 171 | - | for i in 0..recordType.fieldsLen { |
|
| 171 | + | for i in 0..recordType.fields.len { |
|
| 172 | 172 | if i > 0 { |
|
| 173 | 173 | io::print(", "); |
|
| 174 | 174 | } |
|
| 175 | 175 | let field = &recordType.fields[i]; |
|
| 176 | 176 | if let name = field.name { |
|
| 177 | 177 | io::print(name); |
|
| 178 | 178 | io::print(": "); |
|
| 179 | 179 | } |
|
| 180 | 180 | printTypeName(field.fieldType); |
|
| 181 | 181 | ||
| 182 | - | if i >= 4 and i < recordType.fieldsLen - 1 { |
|
| 182 | + | if i >= 4 and i < recordType.fields.len - 1 { |
|
| 183 | 183 | io::print(", ..."); |
|
| 184 | 184 | break; |
|
| 185 | 185 | } |
|
| 186 | 186 | } |
|
| 187 | 187 | io::print(" "); |
|
| 188 | 188 | } |
|
| 189 | 189 | io::print("}"); |
|
| 190 | 190 | } |
|
| 191 | 191 | case super::NominalType::Union(unionType) => { |
|
| 192 | 192 | io::print("union {"); |
|
| 193 | - | if unionType.variantsLen > 0 { |
|
| 193 | + | if unionType.variants.len > 0 { |
|
| 194 | 194 | io::print(" "); |
|
| 195 | - | for i in 0..unionType.variantsLen { |
|
| 195 | + | for i in 0..unionType.variants.len { |
|
| 196 | 196 | if i > 0 { |
|
| 197 | 197 | io::print(", "); |
|
| 198 | 198 | } |
|
| 199 | 199 | let variant = &unionType.variants[i]; |
|
| 200 | 200 | io::print(variant.name); |
|
| 201 | 201 | io::print(": "); |
|
| 202 | 202 | printTypeName(variant.valueType); |
|
| 203 | 203 | ||
| 204 | - | if i >= 4 and i < unionType.variantsLen - 1 { |
|
| 204 | + | if i >= 4 and i < unionType.variants.len - 1 { |
|
| 205 | 205 | io::print(", ..."); |
|
| 206 | 206 | break; |
|
| 207 | 207 | } |
|
| 208 | 208 | } |
|
| 209 | 209 | io::print(" "); |
| 634 | 634 | io::print("<root>"); |
|
| 635 | 635 | } |
|
| 636 | 636 | io::print("\n"); |
|
| 637 | 637 | printScope(scope, depth); |
|
| 638 | 638 | ||
| 639 | - | for i in 0..res.nodeData.entries.len { |
|
| 640 | - | if let childScopePtr = res.nodeData.entries[i].scope { |
|
| 639 | + | for entry in res.nodeData.entries { |
|
| 640 | + | if let childScopePtr = entry.scope { |
|
| 641 | 641 | if childScopePtr.parent != nil and childScopePtr.parent == scope { |
|
| 642 | 642 | printScopeTree(res, childScopePtr, depth + 1); |
|
| 643 | 643 | } |
|
| 644 | 644 | } |
|
| 645 | 645 | } |
| 678 | 678 | } |
|
| 679 | 679 | } |
|
| 680 | 680 | ||
| 681 | 681 | /// Entry point for printing resolver diagnostics in vim quickfix format. |
|
| 682 | 682 | pub fn printDiagnostics(diag: *super::Diagnostics, res: *super::Resolver) { |
|
| 683 | - | for i in 0..diag.errors.listLen { |
|
| 684 | - | printError(&diag.errors.list[i], res); |
|
| 683 | + | for i in 0..diag.errors.len { |
|
| 684 | + | printError(&diag.errors[i], res); |
|
| 685 | 685 | } |
|
| 686 | 686 | } |
lib/std/lang/resolver/tests.rad
+21 -21
| 186 | 186 | try testing::expect(super::success(&r.diagnostics)); |
|
| 187 | 187 | } |
|
| 188 | 188 | ||
| 189 | 189 | /// Extract the first error from a test result, failing if none exists. |
|
| 190 | 190 | fn expectError(result: *TestResult) -> *super::Error throws (testing::TestError) { |
|
| 191 | - | let err = super::errorAt(&result.diagnostics.errors, 0) |
|
| 191 | + | let err = super::errorAt(&result.diagnostics.errors[..], 0) |
|
| 192 | 192 | else throw testing::TestError::Failed; |
|
| 193 | 193 | return err; |
|
| 194 | 194 | } |
|
| 195 | 195 | ||
| 196 | 196 | /// Check if two error kinds match. |
| 318 | 318 | else throw testing::TestError::Failed; |
|
| 319 | 319 | ||
| 320 | 320 | if index >= body.statements.len { |
|
| 321 | 321 | throw testing::TestError::Failed; |
|
| 322 | 322 | } |
|
| 323 | - | return body.statements.list[index]; |
|
| 323 | + | return body.statements[index]; |
|
| 324 | 324 | } |
|
| 325 | 325 | ||
| 326 | 326 | /// Retrieve a function body block by function name from the program scope. |
|
| 327 | 327 | fn getFnBody(a: *super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 328 | 328 | throws (testing::TestError) |
| 349 | 349 | /// Get the payload type of a union variant, if it has one. |
|
| 350 | 350 | /// For single-field unlabeled variants like `Variant(i32)`, unwraps to return the inner type. |
|
| 351 | 351 | fn getUnionVariantPayload(nominalTy: *super::NominalType, variantName: *[u8]) -> super::Type { |
|
| 352 | 352 | let case super::NominalType::Union(unionType) = *nominalTy |
|
| 353 | 353 | else panic "getUnionVariantPayload: not a union"; |
|
| 354 | - | for i in 0..unionType.variantsLen { |
|
| 354 | + | for i in 0..unionType.variants.len { |
|
| 355 | 355 | if mem::eq(unionType.variants[i].name, variantName) { |
|
| 356 | 356 | let payloadType = unionType.variants[i].valueType; |
|
| 357 | 357 | // Unwrap single-field unlabeled records to get the inner type. |
|
| 358 | 358 | if let case super::Type::Nominal(nominalInfo) = payloadType { |
|
| 359 | 359 | if let case super::NominalType::Record(recInfo) = *nominalInfo { |
|
| 360 | - | if not recInfo.labeled and recInfo.fieldsLen == 1 { |
|
| 360 | + | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 361 | 361 | return recInfo.fields[0].fieldType; |
|
| 362 | 362 | } |
|
| 363 | 363 | } |
|
| 364 | 364 | } |
|
| 365 | 365 | return payloadType; |
| 1266 | 1266 | try expectNoErrors(&result); |
|
| 1267 | 1267 | ||
| 1268 | 1268 | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 1269 | 1269 | try testing::expect(fnBlock.statements.len > 0); |
|
| 1270 | 1270 | ||
| 1271 | - | let matchNode = fnBlock.statements.list[0]; |
|
| 1271 | + | let matchNode = fnBlock.statements[0]; |
|
| 1272 | 1272 | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 1273 | 1273 | else throw testing::TestError::Failed; |
|
| 1274 | - | let caseNode = sw.prongs.list[0]; |
|
| 1274 | + | let caseNode = sw.prongs[0]; |
|
| 1275 | 1275 | ||
| 1276 | 1276 | let scope = super::scopeFor(&a, caseNode) |
|
| 1277 | 1277 | else throw testing::TestError::Failed; |
|
| 1278 | 1278 | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 1279 | 1279 | else throw testing::TestError::Failed; |
| 1471 | 1471 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1472 | 1472 | else throw testing::TestError::Failed; |
|
| 1473 | 1473 | ||
| 1474 | 1474 | let case super::Type::Fn(fnTy) = valType |
|
| 1475 | 1475 | else throw testing::TestError::Failed; |
|
| 1476 | - | try testing::expect(fnTy.paramTypesLen == 0); |
|
| 1476 | + | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1477 | 1477 | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1478 | 1478 | } |
|
| 1479 | 1479 | { // Checking that the type of the call matches the function return type. |
|
| 1480 | 1480 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1481 | 1481 |
| 1506 | 1506 | else throw testing::TestError::Failed; |
|
| 1507 | 1507 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1508 | 1508 | else throw testing::TestError::Failed; |
|
| 1509 | 1509 | let case super::Type::Fn(fnTy) = valType |
|
| 1510 | 1510 | else throw testing::TestError::Failed; |
|
| 1511 | - | try testing::expect(fnTy.paramTypesLen == 0); |
|
| 1511 | + | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1512 | 1512 | try testing::expect(*fnTy.returnType == super::Type::I32); |
|
| 1513 | 1513 | } |
|
| 1514 | 1514 | { // Call expression should inherit the function's return type. |
|
| 1515 | 1515 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32); |
|
| 1516 | 1516 |
| 1541 | 1541 | else throw testing::TestError::Failed; |
|
| 1542 | 1542 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1543 | 1543 | else throw testing::TestError::Failed; |
|
| 1544 | 1544 | let case super::Type::Fn(fnTy) = valType |
|
| 1545 | 1545 | else throw testing::TestError::Failed; |
|
| 1546 | - | try testing::expect(fnTy.paramTypesLen == 1); |
|
| 1546 | + | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 1547 | 1547 | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 1548 | 1548 | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1549 | 1549 | ||
| 1550 | 1550 | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 1551 | 1551 | else throw testing::TestError::Failed; |
|
| 1552 | 1552 | try testing::expect(fnDecl.sig.params.len == 1); |
|
| 1553 | 1553 | ||
| 1554 | - | let paramNode = fnDecl.sig.params.list[0]; |
|
| 1554 | + | let paramNode = fnDecl.sig.params[0]; |
|
| 1555 | 1555 | try expectType(&a, paramNode, super::Type::I8); |
|
| 1556 | 1556 | } |
|
| 1557 | 1557 | { // Call should resolve to void, matching the function's return type. |
|
| 1558 | 1558 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1559 | 1559 | let fnSym = super::symbolFor(&a, fnNode) |
| 1583 | 1583 | else throw testing::TestError::Failed; |
|
| 1584 | 1584 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1585 | 1585 | else throw testing::TestError::Failed; |
|
| 1586 | 1586 | let case super::Type::Fn(fnTy) = valType |
|
| 1587 | 1587 | else throw testing::TestError::Failed; |
|
| 1588 | - | try testing::expect(fnTy.paramTypesLen == 2); |
|
| 1588 | + | try testing::expect(fnTy.paramTypes.len == 2); |
|
| 1589 | 1589 | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 1590 | 1590 | try testing::expect(*fnTy.paramTypes[1] == super::Type::I32); |
|
| 1591 | 1591 | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1592 | 1592 | ||
| 1593 | 1593 | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 1594 | 1594 | else throw testing::TestError::Failed; |
|
| 1595 | 1595 | try testing::expect(fnDecl.sig.params.len == 2); |
|
| 1596 | 1596 | ||
| 1597 | - | let firstParam = fnDecl.sig.params.list[0]; |
|
| 1598 | - | let secondParam = fnDecl.sig.params.list[1]; |
|
| 1597 | + | let firstParam = fnDecl.sig.params[0]; |
|
| 1598 | + | let secondParam = fnDecl.sig.params[1]; |
|
| 1599 | 1599 | try expectType(&a, firstParam, super::Type::I8); |
|
| 1600 | 1600 | try expectType(&a, secondParam, super::Type::I32); |
|
| 1601 | 1601 | } |
|
| 1602 | 1602 | { // Call expression should again mirror the function return type. |
|
| 1603 | 1603 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
| 1628 | 1628 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1629 | 1629 | else throw testing::TestError::Failed; |
|
| 1630 | 1630 | ||
| 1631 | 1631 | let case super::Type::Fn(fnTy) = valType |
|
| 1632 | 1632 | else throw testing::TestError::Failed; |
|
| 1633 | - | try testing::expect(fnTy.paramTypesLen == 1); |
|
| 1633 | + | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 1634 | 1634 | try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool); |
|
| 1635 | 1635 | try testing::expect(*fnTy.returnType == super::Type::Bool); |
|
| 1636 | 1636 | } |
|
| 1637 | 1637 | } |
|
| 1638 | 1638 |
| 1977 | 1977 | // Verify the type symbol was created with labeled=false. |
|
| 1978 | 1978 | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 1979 | 1979 | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 1980 | 1980 | else throw testing::TestError::Failed; |
|
| 1981 | 1981 | try testing::expect(not recordType.labeled); |
|
| 1982 | - | try testing::expect(recordType.fieldsLen == 2); |
|
| 1982 | + | try testing::expect(recordType.fields.len == 2); |
|
| 1983 | 1983 | try testing::expect(recordType.fields[0].name == nil); |
|
| 1984 | 1984 | try testing::expect(recordType.fields[1].name == nil); |
|
| 1985 | 1985 | } |
|
| 1986 | 1986 | ||
| 1987 | 1987 | @test fn testResolveLabeledRecordDecl() throws (testing::TestError) { |
| 1992 | 1992 | ||
| 1993 | 1993 | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 1994 | 1994 | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 1995 | 1995 | else throw testing::TestError::Failed; |
|
| 1996 | 1996 | try testing::expect(recordType.labeled); |
|
| 1997 | - | try testing::expect(recordType.fieldsLen == 2); |
|
| 1997 | + | try testing::expect(recordType.fields.len == 2); |
|
| 1998 | 1998 | try testing::expect(recordType.fields[0].name != nil); |
|
| 1999 | 1999 | try testing::expect(recordType.fields[1].name != nil); |
|
| 2000 | 2000 | } |
|
| 2001 | 2001 | ||
| 2002 | 2002 | @test fn testResolveRecordFieldAccessValid() throws (testing::TestError) { |
| 2726 | 2726 | let result = try resolveProgramStr(&mut a, program); |
|
| 2727 | 2727 | ||
| 2728 | 2728 | let ty = try getTypeInScopeOf(&a, result.root, "Status"); |
|
| 2729 | 2729 | let case super::NominalType::Union(unionType) = *ty |
|
| 2730 | 2730 | else throw testing::TestError::Failed; |
|
| 2731 | - | try testing::expect(unionType.variantsLen == 2); |
|
| 2731 | + | try testing::expect(unionType.variants.len == 2); |
|
| 2732 | 2732 | try testing::expect(mem::eq(unionType.variants[0].name, "Ok")); |
|
| 2733 | 2733 | try testing::expect(mem::eq(unionType.variants[1].name, "Error")); |
|
| 2734 | 2734 | if getUnionVariantPayload(ty, "Ok") != super::Type::Void { |
|
| 2735 | 2735 | throw testing::TestError::Failed; |
|
| 2736 | 2736 | } |
| 4256 | 4256 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4257 | 4257 | let result = try resolveProgramStr(&mut a, program); |
|
| 4258 | 4258 | try expectNoErrors(&result); |
|
| 4259 | 4259 | ||
| 4260 | 4260 | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 4261 | - | let matchNode = fnBlock.statements.list[1]; |
|
| 4261 | + | let matchNode = fnBlock.statements[1]; |
|
| 4262 | 4262 | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 4263 | 4263 | else throw testing::TestError::Failed; |
|
| 4264 | - | let caseNode = sw.prongs.list[0]; |
|
| 4264 | + | let caseNode = sw.prongs[0]; |
|
| 4265 | 4265 | ||
| 4266 | 4266 | let scope = super::scopeFor(&a, caseNode) |
|
| 4267 | 4267 | else throw testing::TestError::Failed; |
|
| 4268 | 4268 | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4269 | 4269 | else throw testing::TestError::Failed; |
| 4279 | 4279 | let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4280 | 4280 | let result = try resolveProgramStr(&mut a, program); |
|
| 4281 | 4281 | try expectNoErrors(&result); |
|
| 4282 | 4282 | ||
| 4283 | 4283 | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 4284 | - | let matchNode = fnBlock.statements.list[1]; |
|
| 4284 | + | let matchNode = fnBlock.statements[1]; |
|
| 4285 | 4285 | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 4286 | 4286 | else throw testing::TestError::Failed; |
|
| 4287 | - | let caseNode = sw.prongs.list[0]; |
|
| 4287 | + | let caseNode = sw.prongs[0]; |
|
| 4288 | 4288 | ||
| 4289 | 4289 | let scope = super::scopeFor(&a, caseNode) |
|
| 4290 | 4290 | else throw testing::TestError::Failed; |
|
| 4291 | 4291 | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4292 | 4292 | else throw testing::TestError::Failed; |