Use growable slices throughout the compiler

c4b1fccc71edd1efaf5842fe51a2f83d34229a6401b1cbe83d1abf3e396f2291
This allows for massive simplifications where we have dynamic lists.
Alexis Sellier committed ago 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 = &paramTypes[..];
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: &paramTypes[..],
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: &paramTypes[..],
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: &paramTypes[..],
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;