feat(lang): Generalize core types (WIP)

b5fc83f3c8d96e8d017850be318ee4b3a38083c97dcd5698f6401b3d851d1b7c
Use generic arrays, slices, and vectors throughout the compiler and RV64 backend. Keep slice values limited to pointer and length while vectors track initialized length and capacity.

Add bounded overlap-safe relocation, canonical cross-package generic names, and tests for vector growth, array lengths, and relocation.
Alexis Sellier committed ago 1 parent 3225bfc0
Makefile +6 -1
90 90
91 91
# A `.rad` executable test can have a same-basename `.ras` module.
92 92
$(patsubst %.ras,%.rv64,$(BIN_TEST_RAD_ASM_SRC)): %.rv64: %.ras
93 93
$(patsubst %.start.ras,%.rv64,$(BIN_TEST_RAD_START_SRC)): %.rv64: %.start.ras
94 94
95 +
# Cross-package generic tests compile with the standard library.
96 +
$(BIN_TEST_DIR)/vec.generic.rv64: $(BIN_TEST_DIR)/vec.generic.rad $(STD_LIB) $(RAD_BIN)
97 +
	@echo "radiance $(BIN_TEST_DIR)/vec.generic.rad => $@"
98 +
	@$(RADIANCE) $(STD) -pkg test -mod $< -entry test -o $@
99 +
95 100
# Compile each executable test to a binary.
96 101
$(BIN_TEST_DIR)/%.rv64: $(BIN_TEST_DIR)/%.rad $(RAD_BIN)
97 102
	@echo "radiance $< => $@"
98 103
	@$(RADIANCE) -pkg test -mod $< $(patsubst %,-mod %,$(wildcard $(@:.rv64=)/*.rad)) $(patsubst %,-start %,$(wildcard $(@:.rv64=.start.ras))) $(patsubst %,-mod %,$(wildcard $(@:.rv64=.ras))) -o $@
99 104
104 109
clean-bin-test:
105 110
	@rm -f $(BIN_RUNNER) \
106 111
		$(BIN_RUNNER:.rv64=.rv64.debug) \
107 112
		$(BIN_RUNNER:.rv64=.rv64.s) \
108 113
		$(BIN_RUNNER:.rv64=.rv64.o) \
109 -
		$(BIN_TEST_EXE_BIN) \
114 +
		$(wildcard $(BIN_TEST_DIR)/*.rv64) \
110 115
		$(wildcard $(BIN_TEST_DIR)/*.rv64.debug) \
111 116
		$(wildcard $(BIN_TEST_DIR)/*.rv64.s)
112 117
113 118
seed:
114 119
	seed/update
compiler/radiance.rad +62 -22
1 1
//! Radiance compiler front-end.
2 2
use std::mem;
3 3
use std::fmt;
4 +
use std::vec;
4 5
use std::io;
5 6
use std::lang::alloc;
6 7
use std::lang::ast;
7 8
use std::lang::parser;
8 9
use std::lang::scanner;
20 21
use std::lang::gen::types;
21 22
use std::sys;
22 23
use std::sys::unix;
23 24
use std::collections::dict;
24 25
26 +
instantiate vec::append⟨*ast::Node⟩,
27 +
            vec::viewMut⟨*ast::Node⟩,
28 +
            vec::viewMut⟨resolver::Error⟩,
29 +
            vec::view⟨il::Data⟩;
30 +
25 31
/// Maximum number of modules we can load per package.
26 32
constant MAX_LOADED_MODULES: u32 = 64;
27 33
/// Maximum number of packages we can compile.
28 34
constant MAX_PACKAGES: u32 = 4;
29 35
/// Total module entries across all packages.
658 664
    }
659 665
    set funcPath[desc.modPath.len - 1] = desc.fnName;
660 666
    let funcArg = synthScopeAccess(arena, &funcPath[..desc.modPath.len]);
661 667
662 668
    let a = alloc::arenaAllocator(&mut arena.arena);
663 -
    let args = ast::nodeSlice(arena, 3)
664 -
        .append(modArg, a)
665 -
        .append(nameArg, a)
666 -
        .append(funcArg, a);
667 -
668 -
    return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args }));
669 +
    let mut args = ast::nodeVec(arena, 3);
670 +
    vec::append⟨*ast::Node⟩(&mut args, modArg, a);
671 +
    vec::append⟨*ast::Node⟩(&mut args, nameArg, a);
672 +
    vec::append⟨*ast::Node⟩(&mut args, funcArg, a);
673 +
    let argsView = vec::viewMut⟨*ast::Node⟩(&mut args);
674 +
675 +
    return ast::synthNode(arena, ast::NodeValue::Call(ast::Call {
676 +
        callee,
677 +
        args: argsView,
678 +
    }));
669 679
}
670 680
671 681
/// Inject a test runner into the entry package's root module.
672 682
///
673 683
/// Scans all parsed modules for `@test fn` declarations, then appends
714 724
715 725
/// Synthesize the test entry point.
716 726
fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node {
717 727
    // Build array literal: `[testing::test(...), ...]`.
718 728
    let a = alloc::arenaAllocator(&mut arena.arena);
719 -
    let mut elements = ast::nodeSlice(arena, tests.len as u32);
729 +
    let mut elements = ast::nodeVec(arena, tests.len as u32);
720 730
    for i in 0..tests.len {
721 -
        elements.append(synthTestCall(arena, &tests[i]), a);
731 +
        vec::append⟨*ast::Node⟩(&mut elements, synthTestCall(arena, &tests[i]), a);
722 732
    }
723 -
    let arrayLit = ast::synthNode(arena, ast::NodeValue::ArrayLit(elements));
733 +
    let arrayLit = ast::synthNode(
734 +
        arena,
735 +
        ast::NodeValue::ArrayLit(vec::viewMut⟨*ast::Node⟩(&mut elements))
736 +
    );
724 737
725 738
    // Build: `&[...]`.
726 739
    let testsRef = ast::synthNode(arena, ast::NodeValue::AddressOf(ast::AddressOf {
727 740
        target: arrayLit, mutable: false,
728 741
    }));
729 742
730 743
    // Build: `testing::runAllTests(&[...])`.
731 744
    let runFn = synthScopeAccess(arena, &["testing", "runAllTests"]);
732 -
    let callArgs = ast::nodeSlice(arena, 1).append(testsRef, a);
745 +
    let mut callArgs = ast::nodeVec(arena, 1);
746 +
    vec::append⟨*ast::Node⟩(&mut callArgs, testsRef, a);
733 747
    let callExpr = ast::synthNode(arena, ast::NodeValue::Call(ast::Call {
734 -
        callee: runFn, args: callArgs,
748 +
        callee: runFn, args: vec::viewMut⟨*ast::Node⟩(&mut callArgs),
735 749
    }));
736 750
737 751
    // Build: `return testing::runAllTests(&[...]);`
738 752
    let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr });
739 -
    let bodyStmts = ast::nodeSlice(arena, 1).append(retStmt, a);
740 -
    let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts }));
753 +
    let mut bodyStmts = ast::nodeVec(arena, 1);
754 +
    vec::append⟨*ast::Node⟩(&mut bodyStmts, retStmt, a);
755 +
    let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block {
756 +
        statements: vec::viewMut⟨*ast::Node⟩(&mut bodyStmts),
757 +
    }));
741 758
742 759
    // Build: `fn #testMain() -> i32`
743 760
    let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain")));
744 761
    let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer {
745 762
        width: 4, sign: ast::Signedness::Signed,
746 763
    }));
747 764
    let fnSig = ast::FnSig {
748 -
        params: ast::nodeSlice(arena, 0),
765 +
        params: &mut [],
749 766
        returnType,
750 -
        throwList: ast::nodeSlice(arena, 0),
767 +
        throwList: &mut [],
751 768
    };
752 769
753 770
    // `@default` attribute.
754 771
    let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default));
755 -
    let attrList = ast::nodeSlice(arena, 1).append(attrNode, a);
756 -
    let fnAttrs = ast::Attributes { list: attrList };
772 +
    let mut attrList = ast::nodeVec(arena, 1);
773 +
    vec::append⟨*ast::Node⟩(&mut attrList, attrNode, a);
774 +
    let fnAttrs = ast::Attributes {
775 +
        list: vec::viewMut⟨*ast::Node⟩(&mut attrList),
776 +
    };
757 777
758 778
    return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl {
759 -
        name: fnName, params: ast::nodeSlice(arena, 0), sig: fnSig, body: fnBody, attrs: fnAttrs,
779 +
        name: fnName,
780 +
        params: &mut [],
781 +
        sig: fnSig,
782 +
        body: fnBody,
783 +
        attrs: fnAttrs,
760 784
    }));
761 785
}
762 786
763 787
/// Append a declaration to a block node's statement list.
764 788
fn injectIntoBlock(
767 791
    decl: *ast::Node
768 792
) {
769 793
    let case ast::NodeValue::Block(block) = blockNode.value else {
770 794
        panic "injectIntoBlock: expected Block node";
771 795
    };
772 -
    let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena));
773 -
    set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts });
796 +
    let a = alloc::arenaAllocator(&mut arena.arena);
797 +
    let mut stmts = ast::nodeVec(arena, block.statements.len as u32 + 1);
798 +
    for stmt in block.statements {
799 +
        vec::append⟨*ast::Node⟩(&mut stmts, stmt, a);
800 +
    }
801 +
    vec::append⟨*ast::Node⟩(&mut stmts, decl, a);
802 +
    set blockNode.value = ast::NodeValue::Block(ast::Block {
803 +
        statements: vec::viewMut⟨*ast::Node⟩(&mut stmts),
804 +
    });
774 805
}
775 806
776 807
/// Write a self-contained RV64 image containing text and data sections.
777 808
fn writeImage(
778 809
    code: *[u32],
871 902
    }
872 903
873 904
    // Resolve all packages.
874 905
    // TODO: Fix this error printing dance.
875 906
    let diags = try resolver::resolve(&mut res, &ctx.graph, &resolverPkgs[..resolverPackageCount]) catch {
876 -
        let diags = resolver::Diagnostics { errors: res.errors };
907 +
        let diags = resolver::Diagnostics {
908 +
            errors: vec::viewMut⟨resolver::Error⟩(&mut res.errors),
909 +
        };
877 910
        resolver::printer::printDiagnostics(&diags, &res);
878 911
        throw Error::Other;
879 912
    };
880 913
    if not resolver::success(&diags) {
881 914
        resolver::printer::printDiagnostics(&diags, &res);
1026 1059
        else => {}
1027 1060
    }
1028 1061
    if let path = codegenOptions.logPath {
1029 1062
        pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1030 1063
    }
1031 -
    return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]);
1064 +
    return rv64::finishProgram(
1065 +
        &mut generator,
1066 +
        vec::view⟨il::Data⟩(&low.data),
1067 +
        storage,
1068 +
        asmData,
1069 +
        &mut RO_DATA_BUF[..],
1070 +
        &mut RW_DATA_BUF[..],
1071 +
    );
1032 1072
}
1033 1073
1034 1074
/// Lower, optionally dump, and optionally generate binary output.
1035 1075
fn compile(
1036 1076
    ctx: *mut CompileContext,
lib/std.rad +3 -2
1 1
//! The Radiance Standard Library.
2 2
3 +
export mod lang;
4 +
export mod slice;
5 +
export mod vec;
3 6
export mod io;
4 7
export mod collections;
5 8
export mod char;
6 -
export mod lang;
7 9
export mod sys;
8 10
export mod arch;
9 11
export mod fmt;
10 12
export mod mem;
11 -
export mod vec;
12 13
export mod intrinsics;
13 14
14 15
// Test modules.
15 16
@test export mod testing;
16 17
@test export mod tests;
lib/std/arch/rv64.rad +1 -1
346 346
    let rwDataSize = data::emitSection(
347 347
        globalData, &dataSymMap, &generator.e.labels, codeBase, rwDataBuf, false
348 348
    );
349 349
    return Program {
350 350
        code: emit::getCode(&generator.e),
351 -
        funcs: generator.e.funcs,
351 +
        funcs: emit::getFuncs(&generator.e),
352 352
        roDataSize,
353 353
        rwDataSize,
354 354
        debugEntries: emit::getDebugEntries(&generator.e),
355 355
    };
356 356
}
lib/std/arch/rv64/asm.rad +37 -19
40 40
use std::lang::strings;
41 41
use std::lang::gen;
42 42
use std::collections::dict;
43 43
use std::arch::rv64::encode;
44 44
use std::arch::rv64;
45 +
use std::vec;
46 +
47 +
instantiate vec::view⟨u32⟩;
48 +
49 +
instantiate vec::view⟨u8⟩;
50 +
51 +
instantiate vec::view⟨Symbol⟩;
52 +
53 +
instantiate vec::view⟨Fixup⟩;
45 54
46 55
/// Assembler scanner module.
47 56
export mod scanner;
48 57
/// Assembler parser module.
49 58
export mod parser;
455 464
export record Assembler {
456 465
    /// Allocation arena for temporary assembler state.
457 466
    arena: *mut alloc::Arena,
458 467
    /// Assembler lexical scanner.
459 468
    scan: scanner::Scanner,
460 -
    /// Output text buffer.
461 -
    text: *mut [u32],
462 -
    /// Output data buffer.
463 -
    data: *mut [u8],
469 +
    /// Append-backed output text storage.
470 +
    text: vec::Vec⟨u32⟩,
471 +
    /// Append-backed output data storage.
472 +
    data: vec::Vec⟨u8⟩,
464 473
    /// Current output section.
465 474
    section: Section,
466 -
    /// Defined symbols.
467 -
    symbols: *mut [Symbol],
475 +
    /// Append-backed defined symbols.
476 +
    symbols: vec::Vec⟨Symbol⟩,
468 477
    /// Name-to-symbol index map.
469 478
    symbolMap: dict::Dict,
470 479
    /// Name-to-integer map.
471 480
    constMap: dict::Dict,
472 481
    /// Names marked by `.export`.
473 482
    exportMap: dict::Dict,
474 -
    /// Pending fixups.
475 -
    fixups: *mut [Fixup],
476 -
    /// Fixups that reference text outside this assembly fragment.
477 -
    externalFixups: *mut [Fixup],
483 +
    /// Append-backed pending fixups.
484 +
    fixups: vec::Vec⟨Fixup⟩,
485 +
    /// Append-backed fixups that reference text outside this assembly fragment.
486 +
    externalFixups: vec::Vec⟨Fixup⟩,
478 487
    /// Absolute runtime address of data-section offset zero.
479 488
    dataBase: u32,
480 489
}
481 490
482 491
/// Assemble source using `dataBase` as the runtime address of the data-section.
500 509
    let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
501 510
502 511
    let mut a = Assembler {
503 512
        arena,
504 513
        scan: scanner::scanner(sourceKind, source, pool),
505 -
        text: @sliceOf(textBuf.ptr, 0, textBuf.len),
506 -
        data: @sliceOf(dataBuf.ptr, 0, dataBuf.len),
514 +
        text: vec::Vec⟨u32{ data: textBuf, len: 0 },
515 +
        data: vec::Vec⟨u8{ data: dataBuf, len: 0 },
507 516
        section: Section::Text,
508 -
        symbols: @sliceOf((symbols as *mut [Symbol]).ptr, 0, (symbols as *mut [Symbol]).len),
517 +
        symbols: vec::Vec⟨Symbol⟩ {
518 +
            data: symbols as *mut [Symbol],
519 +
            len: 0,
520 +
        },
509 521
        symbolMap: dict::init(entries as *mut [dict::Entry]),
510 522
        constMap: dict::init(constEntries as *mut [dict::Entry]),
511 523
        exportMap: dict::init(exportEntries as *mut [dict::Entry]),
512 -
        fixups: @sliceOf((fixups as *mut [Fixup]).ptr, 0, (fixups as *mut [Fixup]).len),
513 -
        externalFixups: @sliceOf((externalFixups as *mut [Fixup]).ptr, 0, (externalFixups as *mut [Fixup]).len),
524 +
        fixups: vec::Vec⟨Fixup⟩ {
525 +
            data: fixups as *mut [Fixup],
526 +
            len: 0,
527 +
        },
528 +
        externalFixups: vec::Vec⟨Fixup⟩ {
529 +
            data: externalFixups as *mut [Fixup],
530 +
            len: 0,
531 +
        },
514 532
        dataBase,
515 533
    };
516 534
    // Parse assembly source and emit instructions.
517 535
    try parser::parseProgram(&mut a);
518 536
    // Resolve fixups and finalize program.
519 537
    try emit::finishProgram(&mut a);
520 538
521 539
    return Program {
522 -
        text: a.text,
523 -
        data: a.data,
524 -
        symbols: a.symbols,
525 -
        externalFixups: a.externalFixups,
540 +
        text: vec::view⟨u32(&a.text),
541 +
        data: vec::view⟨u8(&a.data),
542 +
        symbols: vec::view⟨Symbol⟩(&a.symbols),
543 +
        externalFixups: vec::view⟨Fixup⟩(&a.externalFixups),
526 544
    };
527 545
}
528 546
529 547
/// Return the next power of two at least as large as `value`.
530 548
fn nextPowerOfTwo(value: u32) -> u32 {
lib/std/arch/rv64/asm/emit.rad +28 -13
5 5
use std::fmt;
6 6
7 7
use std::collections::dict;
8 8
use std::lang::alloc;
9 9
use std::lang::gen;
10 +
use std::vec;
11 +
12 +
instantiate vec::append⟨super::Symbol⟩,
13 +
            vec::view⟨super::Symbol⟩;
14 +
15 +
instantiate vec::append⟨u32⟩,
16 +
            vec::viewMut⟨u32⟩;
17 +
18 +
instantiate vec::append⟨u8⟩,
19 +
            vec::viewMut⟨u8⟩;
20 +
21 +
instantiate vec::append⟨super::Fixup⟩,
22 +
            vec::view⟨super::Fixup⟩;
10 23
11 24
/// Define a symbol at the current text or data offset.
12 25
export fn defineSymbol(a: *mut super::Assembler, name: *[u8]) {
13 26
    let idx = a.symbols.len;
14 27
    let offset: i32 = a.data.len as i32
15 28
        if a.section == super::Section::Data
16 29
        else a.text.len as i32 * rv64::INSTR_SIZE;
17 30
18 -
    a.symbols.append(super::Symbol {
31 +
    vec::append⟨super::Symbol⟩(&mut a.symbols, super::Symbol {
19 32
        name,
20 33
        section: a.section,
21 34
        offset,
22 35
        isExported: dict::get(&a.exportMap, name) <> nil,
23 36
    }, alloc::arenaAllocator(a.arena));
24 37
    dict::insert(&mut a.symbolMap, name, idx as i32);
25 38
}
26 39
27 40
/// Append one encoded instruction word to the text section.
28 41
export fn emitText(a: *mut super::Assembler, word: u32) throws (super::Error) {
29 -
    a.text.append(word, alloc::arenaAllocator(a.arena));
42 +
    vec::append⟨u32(&mut a.text, word, alloc::arenaAllocator(a.arena));
30 43
}
31 44
32 45
/// Append `words` no-op instructions to the text section.
33 46
export fn emitTextPadding(a: *mut super::Assembler, words: u32) throws (super::Error) {
34 47
    for _ in 0..words {
36 49
    }
37 50
}
38 51
39 52
/// Append one byte to the data section.
40 53
export fn emitByte(a: *mut super::Assembler, byte: u8) throws (super::Error) {
41 -
    a.data.append(byte, alloc::arenaAllocator(a.arena));
54 +
    vec::append⟨u8(&mut a.data, byte, alloc::arenaAllocator(a.arena));
42 55
}
43 56
44 57
/// Emit a little-endian integer with `bytes` bytes.
45 58
fn emitDataInt(a: *mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
46 59
    for i in 0..bytes {
48 61
    }
49 62
}
50 63
51 64
/// Patch a little-endian integer with `bytes` bytes.
52 65
fn patchDataInt(a: *mut super::Assembler, offset: u32, bits: u64, bytes: u32) {
66 +
    let data = vec::viewMut⟨u8(&mut a.data);
53 67
    for i in 0..bytes {
54 -
        set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8;
68 +
        set data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8;
55 69
    }
56 70
}
57 71
58 72
/// Emit an integer data directive value.
59 73
export fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
78 92
    }
79 93
}
80 94
81 95
/// Record a pending symbol fixup.
82 96
fn recordFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
83 -
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
97 +
    vec::append⟨super::Fixup⟩(&mut a.fixups, super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
84 98
}
85 99
86 100
/// Record a text fixup that must be resolved after all program text is known.
87 101
fn recordExternalFixup(a: *mut super::Assembler, fixup: super::Fixup) {
88 -
    a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
102 +
    vec::append⟨super::Fixup⟩(&mut a.externalFixups, fixup, alloc::arenaAllocator(a.arena));
89 103
}
90 104
91 105
/// Record a text-section symbol fixup and reserve its instruction words.
92 106
export fn recordTextFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
93 107
    recordFixup(a, symbol, info);
96 110
97 111
/// Find a previously defined symbol by name.
98 112
fn findSymbol(a: *super::Assembler, name: *[u8]) -> ?super::Symbol {
99 113
    let idx = dict::get(&a.symbolMap, name)
100 114
        else return nil;
101 -
    return a.symbols[idx as u32];
115 +
    return vec::view⟨super::Symbol⟩(&a.symbols)[idx as u32];
102 116
}
103 117
104 118
/// Return the final address for a data symbol.
105 119
fn dataSymbolAddr(a: *super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) {
106 120
    if symbol.section <> super::Section::Data {
109 123
    return symbol.offset + (a.dataBase as i32);
110 124
}
111 125
112 126
/// Resolve final symbol references and patch all delayed output.
113 127
export fn finishProgram(a: *mut super::Assembler) throws (super::Error) {
114 -
    for i in 0..a.fixups.len {
115 -
        let fixup = a.fixups[i];
128 +
    let fixups = vec::view⟨super::Fixup⟩(&a.fixups);
129 +
    let text = vec::viewMut⟨u32(&mut a.text);
130 +
    for fixup in fixups {
116 131
        let symbol = findSymbol(a, fixup.symbol) else {
117 132
            match fixup.info {
118 133
                case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => {
119 134
                    recordExternalFixup(a, fixup);
120 135
                    continue;
133 148
                if not encode::isBranchImm(rel) {
134 149
                    throw super::Error::Invalid { offset: 0, message: "branch target out of range" };
135 150
                }
136 151
                let word = encodeBranch(op, rs1, rs2, rel);
137 152
138 -
                set a.text[index] = word;
153 +
                set text[index] = word;
139 154
            }
140 155
            case super::FixupInfo::Jal { rd, index } => {
141 156
                if symbol.section <> super::Section::Text {
142 157
                    throw super::Error::Invalid { offset: 0, message: "jump target must be in text section" };
143 158
                }
145 160
                let rel = symbol.offset - srcOffset;
146 161
147 162
                if not encode::isJumpImm(rel) {
148 163
                    throw super::Error::Invalid { offset: 0, message: "jump target out of range" };
149 164
                }
150 -
                set a.text[index] = encode::jal(rd, rel);
165 +
                set text[index] = encode::jal(rd, rel);
151 166
            }
152 167
            case super::FixupInfo::Addr { rd, index } => {
153 168
                let mut addr = symbol.offset - (index as i32 * rv64::INSTR_SIZE);
154 169
                if symbol.section == super::Section::Data {
155 170
                    set addr = symbol.offset + (a.dataBase as i32);
156 171
                }
157 172
                let split = emit::splitImm(addr);
158 -
                set a.text[index] = encode::lui(rd, split.hi)
173 +
                set text[index] = encode::lui(rd, split.hi)
159 174
                    if symbol.section == super::Section::Data
160 175
                    else encode::auipc(rd, split.hi);
161 -
                set a.text[index + 1] = encode::addi(rd, rd, split.lo);
176 +
                set text[index + 1] = encode::addi(rd, rd, split.lo);
162 177
            }
163 178
            case super::FixupInfo::Word { offset } => {
164 179
                let addr = try dataSymbolAddr(a, symbol);
165 180
                patchDataInt(a, offset, addr as u64, rv64::WORD_SIZE as u32);
166 181
            }
lib/std/arch/rv64/asm/parser.rad +5 -1
6 6
use std::lang::parser;
7 7
use std::lang::gen;
8 8
use std::collections::dict;
9 9
use std::arch::rv64::encode;
10 10
use std::arch::rv64;
11 +
use std::vec;
11 12
12 13
use super::emit;
13 14
use super::scanner;
14 15
16 +
instantiate vec::viewMut⟨super::Symbol⟩;
17 +
15 18
/// Parsed memory operand with base register and signed byte offset.
16 19
record MemOperand {
17 20
    /// Base register inside the memory operand parentheses.
18 21
    base: gen::Reg,
19 22
    /// Signed byte offset preceding the base register.
556 559
/// Parse a `.export` directive.
557 560
fn parseExportDirective(a: *mut super::Assembler) throws (super::Error) {
558 561
    let name = try parseLabelName(a);
559 562
    dict::insert(&mut a.exportMap, name, 1);
560 563
    if let idx = dict::get(&a.symbolMap, name) {
561 -
        set a.symbols[idx as u32].isExported = true;
564 +
        let symbols = vec::viewMut⟨super::Symbol⟩(&mut a.symbols);
565 +
        set symbols[idx as u32].isExported = true;
562 566
    }
563 567
}
564 568
565 569
/// Parse a `.space` directive.
566 570
fn parseSpaceDirective(a: *mut super::Assembler) throws (super::Error) {
lib/std/arch/rv64/emit.rad +61 -25
8 8
use std::lang::gen::data;
9 9
use std::lang::gen::labels;
10 10
use std::lang::gen::types;
11 11
use std::collections::dict;
12 12
use std::mem;
13 +
use std::vec;
13 14
14 15
use super::encode;
15 16
17 +
instantiate vec::append⟨PendingBranch⟩,
18 +
            vec::view⟨PendingBranch⟩;
19 +
20 +
instantiate vec::append⟨PendingCall⟩,
21 +
            vec::view⟨PendingCall⟩;
22 +
23 +
instantiate vec::append⟨PendingJump⟩,
24 +
            vec::view⟨PendingJump⟩;
25 +
26 +
instantiate vec::append⟨PendingAddrLoad⟩,
27 +
            vec::view⟨PendingAddrLoad⟩;
28 +
29 +
instantiate vec::append⟨types::FuncAddr⟩,
30 +
            vec::view⟨types::FuncAddr⟩;
31 +
16 32
/// Maximum number of instructions in code buffer.
17 33
constant MAX_INSTRS: u32 = 2097152;
18 34
/// Maximum code length before byte offset overflows signed 32-bits.
19 35
constant MAX_CODE_LEN: u32 = 0x7FFFFFFF / super::INSTR_SIZE as u32;
20 36
/// Maximum positive value encodable by a signed 32-bit address calculation.
103 119
    /// Emitted instructions storage.
104 120
    code: *mut [u32],
105 121
    /// Current number of emitted instructions.
106 122
    codeLen: u32,
107 123
    /// Local branches needing offset patching.
108 -
    pendingBranches: *mut [PendingBranch],
124 +
    pendingBranches: vec::Vec⟨PendingBranch⟩,
109 125
    /// Function calls needing offset patching.
110 -
    pendingCalls: *mut [PendingCall],
126 +
    pendingCalls: vec::Vec⟨PendingCall⟩,
111 127
    /// Assembly jumps needing offset patching.
112 -
    pendingJumps: *mut [PendingJump],
128 +
    pendingJumps: vec::Vec⟨PendingJump⟩,
113 129
    /// Function address loads needing offset patching.
114 -
    pendingAddrLoads: *mut [PendingAddrLoad],
130 +
    pendingAddrLoads: vec::Vec⟨PendingAddrLoad⟩,
115 131
    /// Block label tracking.
116 132
    labels: labels::Labels,
117 133
    /// Function start positions for printing.
118 -
    funcs: *mut [types::FuncAddr],
134 +
    funcs: vec::Vec⟨types::FuncAddr⟩,
119 135
    /// Debug entries mapping PCs to source locations.
120 136
    debugEntries: *mut [types::DebugEntry],
121 137
    /// Number of debug entries recorded.
122 138
    debugEntriesLen: u32,
123 139
}
200 216
    }
201 217
    return Emitter {
202 218
        allocator: alloc::arenaAllocator(arena),
203 219
        code: code as *mut [u32],
204 220
        codeLen: 0,
205 -
        pendingBranches: @sliceOf((pendingBranches as *mut [PendingBranch]).ptr, 0, MAX_PENDING),
206 -
        pendingCalls: @sliceOf((pendingCalls as *mut [PendingCall]).ptr, 0, MAX_PENDING),
207 -
        pendingJumps: @sliceOf((pendingJumps as *mut [PendingJump]).ptr, 0, MAX_PENDING),
208 -
        pendingAddrLoads: @sliceOf((pendingAddrLoads as *mut [PendingAddrLoad]).ptr, 0, MAX_PENDING),
221 +
        pendingBranches: vec::Vec⟨PendingBranch⟩ {
222 +
            data: pendingBranches as *mut [PendingBranch],
223 +
            len: 0,
224 +
        },
225 +
        pendingCalls: vec::Vec⟨PendingCall⟩ {
226 +
            data: pendingCalls as *mut [PendingCall],
227 +
            len: 0,
228 +
        },
229 +
        pendingJumps: vec::Vec⟨PendingJump⟩ {
230 +
            data: pendingJumps as *mut [PendingJump],
231 +
            len: 0,
232 +
        },
233 +
        pendingAddrLoads: vec::Vec⟨PendingAddrLoad⟩ {
234 +
            data: pendingAddrLoads as *mut [PendingAddrLoad],
235 +
            len: 0,
236 +
        },
209 237
        labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]),
210 -
        funcs: @sliceOf((funcs as *mut [types::FuncAddr]).ptr, 0, MAX_FUNCS),
238 +
        funcs: vec::Vec⟨types::FuncAddr⟩ {
239 +
            data: funcs as *mut [types::FuncAddr],
240 +
            len: 0,
241 +
        },
211 242
        debugEntries,
212 243
        debugEntriesLen: 0,
213 244
    };
214 245
}
215 246
256 287
    recordFuncAt(e, name, e.codeLen);
257 288
}
258 289
259 290
/// Record a function's start position at `index` for printing.
260 291
export fn recordFuncAt(e: *mut Emitter, name: *[u8], index: u32) {
261 -
    e.funcs.append(types::FuncAddr { name, index }, e.allocator);
292 +
    vec::append⟨types::FuncAddr⟩(&mut e.funcs, types::FuncAddr { name, index }, e.allocator);
262 293
}
263 294
264 295
/// Record a local branch needing later patching.
265 296
/// Unconditional jumps use a single slot (J-type, +-1MB range).
266 297
/// Conditional branches use two slots (B-type has only +-4KB range,
267 298
/// so large functions may need the inverted-branch + JAL fallback).
268 299
export fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) {
269 -
    e.pendingBranches.append(PendingBranch {
300 +
    vec::append⟨PendingBranch⟩(&mut e.pendingBranches, PendingBranch {
270 301
        index: e.codeLen,
271 302
        target: targetBlock,
272 303
        kind: kind,
273 304
    }, e.allocator);
274 305
282 313
283 314
/// Record a function call needing later patching.
284 315
/// Emits placeholder instructions that will be patched later.
285 316
/// Uses two slots to support long-distance calls.
286 317
export fn recordCall(e: *mut Emitter, target: *[u8]) {
287 -
    e.pendingCalls.append(PendingCall {
318 +
    vec::append⟨PendingCall⟩(&mut e.pendingCalls, PendingCall {
288 319
        index: e.codeLen,
289 320
        target,
290 321
    }, e.allocator);
291 322
292 323
    emit(e, encode::nop()); // Placeholder for AUIPC.
293 324
    emit(e, encode::nop()); // Placeholder for JALR.
294 325
}
295 326
296 327
/// Record a jump emitted by assembly that needs whole-program patching.
297 328
export fn recordJumpAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
298 -
    e.pendingJumps.append(PendingJump {
329 +
    vec::append⟨PendingJump⟩(&mut e.pendingJumps, PendingJump {
299 330
        index,
300 331
        target,
301 332
        rd,
302 333
    }, e.allocator);
303 334
}
312 343
    emit(e, encode::nop()); // Placeholder for ADDI.
313 344
}
314 345
315 346
/// Record a function address load already reserved by assembly.
316 347
export fn recordAddrLoadAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
317 -
    e.pendingAddrLoads.append(PendingAddrLoad {
348 +
    vec::append⟨PendingAddrLoad⟩(&mut e.pendingAddrLoads, PendingAddrLoad {
318 349
        index,
319 350
        target,
320 351
        rd: rd,
321 352
        isData: false,
322 353
    }, e.allocator);
323 354
}
324 355
325 356
/// Record a data address load needing later patching.
326 357
/// Uses an absolute 32-bit load sequence matching the current data memory map.
327 358
export fn recordDataAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) {
328 -
    e.pendingAddrLoads.append(PendingAddrLoad {
359 +
    vec::append⟨PendingAddrLoad⟩(&mut e.pendingAddrLoads, PendingAddrLoad {
329 360
        index: e.codeLen,
330 361
        target,
331 362
        rd: rd,
332 363
        isData: true,
333 364
    }, e.allocator);
341 372
/// Called after each function.
342 373
///
343 374
/// Uses two-instruction sequences: short branches use `branch` and `nop`,
344 375
/// long branches use inverted branch  and `jal` or `auipc` and `jalr`.
345 376
export fn patchLocalBranches(e: *mut Emitter) {
346 -
    for i in 0..e.pendingBranches.len {
347 -
        let p = e.pendingBranches[i];
377 +
    let pendingBranches = vec::view⟨PendingBranch⟩(&e.pendingBranches);
378 +
    for p in pendingBranches {
348 379
        let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE);
349 380
        match p.kind {
350 381
            case BranchKind::Cond { op, rs1, rs2 } => {
351 382
                if encode::isBranchImm(offset) {
352 383
                    patch(e, p.index, encodeCondBranch(op, rs1, rs2, offset));
372 403
                assert encode::isJumpImm(offset), "patchLocalBranches: jump offset too large";
373 404
                patch(e, p.index, encode::jal(super::ZERO, offset));
374 405
            },
375 406
        }
376 407
    }
377 -
    set e.pendingBranches = @sliceOf(e.pendingBranches.ptr, 0, e.pendingBranches.cap);
408 +
    set e.pendingBranches.len = 0;
378 409
}
379 410
380 411
/// Encode a conditional branch instruction.
381 412
fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 {
382 413
    match op {
398 429
}
399 430
400 431
/// Patch all pending function calls.
401 432
/// Called after all functions have been generated.
402 433
export fn patchCalls(e: *mut Emitter) {
403 -
    for i in 0..e.pendingCalls.len {
404 -
        let p = e.pendingCalls[i];
434 +
    let pendingCalls = vec::view⟨PendingCall⟩(&e.pendingCalls);
435 +
    for p in pendingCalls {
405 436
        let offset = branchOffsetToFunc(e, p.index, p.target);
406 437
        let s = splitImm(offset);
407 438
408 439
        // `AUIPC scratch, hi(offset)`.
409 440
        patch(e, p.index, encode::auipc(super::SCRATCH1, s.hi));
412 443
    }
413 444
}
414 445
415 446
/// Patch all pending assembly jumps.
416 447
export fn patchJumps(e: *mut Emitter) {
417 -
    for i in 0..e.pendingJumps.len {
418 -
        let p = e.pendingJumps[i];
448 +
    let pendingJumps = vec::view⟨PendingJump⟩(&e.pendingJumps);
449 +
    for p in pendingJumps {
419 450
        let offset = branchOffsetToFunc(e, p.index, p.target);
420 451
421 452
        assert encode::isJumpImm(offset), "patchJumps: jump offset too large";
422 453
        patch(e, p.index, encode::jal(p.rd, offset));
423 454
    }
424 455
}
425 456
426 457
/// Patch all pending function and data address loads.
427 458
/// Called after all functions have been generated and data layout is known.
428 459
export fn patchAddrLoads(e: *mut Emitter, dataSymMap: *data::DataSymMap) {
429 -
    for i in 0..e.pendingAddrLoads.len {
430 -
        let p = e.pendingAddrLoads[i];
460 +
    let pendingAddrLoads = vec::view⟨PendingAddrLoad⟩(&e.pendingAddrLoads);
461 +
    for p in pendingAddrLoads {
431 462
        if p.isData {
432 463
            let addr = data::lookupAddr(dataSymMap, p.target) else {
433 464
                panic "patchAddrLoads: data symbol not found";
434 465
            };
435 466
            assert addr <= MAX_I32_ADDR, "patchAddrLoads: data address too large";
703 734
704 735
//////////////////
705 736
// Code Access  //
706 737
//////////////////
707 738
739 +
/// Get function start positions as an initialized slice.
740 +
export fn getFuncs(e: *Emitter) -> *[types::FuncAddr] {
741 +
    return vec::view⟨types::FuncAddr⟩(&e.funcs);
742 +
}
743 +
708 744
/// Get emitted code as a slice.
709 745
export fn getCode(e: *Emitter) -> *[u32] {
710 746
    return &e.code[..e.codeLen];
711 747
}
712 748
lib/std/arch/rv64/tests.rad +1 -1
21 21
@test fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) {
22 22
    let mut arena = alloc::new(&mut ASSEMBLY_ARENA_STORAGE[..]);
23 23
    let symbols = try alloc::allocSlice(&mut arena, @sizeOf(asm::Symbol), @alignOf(asm::Symbol), 2) catch {
24 24
        throw testing::TestError::Failed;
25 25
    };
26 -
    let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2, 2);
26 +
    let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2);
27 27
    set symbolSlice[0] = asm::Symbol {
28 28
        name: "local",
29 29
        section: asm::Section::Text,
30 30
        offset: 0,
31 31
        isExported: false,
lib/std/lang.rad +2 -0
1 1
//! Radiance language implementation.
2 2
export mod alloc;
3 +
export mod slice;
4 +
export mod vec;
3 5
export mod sexpr;
4 6
export mod types;
5 7
export mod strings;
6 8
export mod scanner;
7 9
export mod ast;
lib/std/lang/ast.rad +15 -8
1 1
//! Radiance AST modules.
2 2
export mod printer;
3 3
4 4
use std::io;
5 5
use std::fmt;
6 +
use std::lang::vec;
7 +
use std::lang::slice;
6 8
use std::lang::alloc;
7 9
use std::lang::types;
8 10
9 11
/// Maximum number of trait methods.
10 12
export constant MAX_TRAIT_METHODS: u32 = 8;
51 53
        arena: alloc::new(data),
52 54
        nextId: 0,
53 55
    };
54 56
}
55 57
56 -
/// Create an empty `*mut [*Node]` slice with the given capacity.
57 -
export fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] {
58 +
/// Create an empty node vector with arena-backed storage of the given capacity.
59 +
export fn nodeVec(arena: *mut NodeArena, capacity: u32) -> vec::Vec⟨*Node⟩ {
58 60
    if capacity == 0 {
59 -
        return &mut [];
61 +
        return vec::Vec⟨*Node⟩ { data: &mut [], len: 0 };
60 62
    }
61 63
    let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity);
64 +
    let data = slice::fromRawPartsMut⟨*Node⟩(ptr.ptr as *mut *Node, capacity);
62 65
63 -
    return @sliceOf(ptr.ptr as *mut *Node, 0, capacity);
66 +
    return vec::Vec⟨*Node⟩ { data, len: 0 };
64 67
}
65 68
66 69
/// Attribute bit set applied to declarations or fields.
67 70
export union Attribute {
68 71
    /// Public visibility attribute.
165 168
export union Builtin {
166 169
    /// Size of type in bytes (`@sizeOf`).
167 170
    SizeOf,
168 171
    /// Alignment requirement of type (`@alignOf`).
169 172
    AlignOf,
170 -
    /// Construct a slice from pointer, length, and optional capacity (`@sliceOf`).
173 +
    /// Construct a slice from a pointer and initialized length (`@sliceOf`).
171 174
    SliceOf,
175 +
    /// Move typed values between mutable pointers (`@relocate`).
176 +
    Relocate,
172 177
}
173 178
174 179
/// Source extent for a node measured in bytes.
175 180
export record Span {
176 181
    /// Byte offset from the start of the source file.
893 898
    let fnSig = FnSig { params, returnType: nil, throwList };
894 899
    let fnBody = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts }));
895 900
    let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl {
896 901
        name: fnName, params: &mut [], sig: fnSig, body: fnBody, attrs: nil,
897 902
    }));
898 -
    let mut rootStmts: *mut [*Node] = &mut [];
899 -
    rootStmts.append(fnDecl, a);
900 -
    let modBody = synthNode(arena, NodeValue::Block(Block { statements: rootStmts }));
903 +
    let mut rootStmts = nodeVec(arena, 1);
904 +
    vec::append⟨*Node⟩(&mut rootStmts, fnDecl, a);
905 +
    let statements = vec::viewMut⟨*Node⟩(&mut rootStmts);
906 +
    let modBody = synthNode(arena, NodeValue::Block(Block { statements }));
901 907
902 908
    return SynthFnMod { modBody, fnBody };
903 909
}
910 +
lib/std/lang/ast/printer.rad +1 -0
43 43
fn builtinName(kind: super::Builtin) -> *[u8] {
44 44
    match kind {
45 45
        case super::Builtin::SizeOf => return "@sizeOf",
46 46
        case super::Builtin::AlignOf => return "@alignOf",
47 47
        case super::Builtin::SliceOf => return "@sliceOf",
48 +
        case super::Builtin::Relocate => return "@relocate",
48 49
    }
49 50
}
50 51
51 52
/// Return the name for an integer type.
52 53
fn intTypeName(width: u8, sign: super::Signedness) -> *[u8] {
lib/std/lang/lower.rad +337 -329
97 97
use std::mem;
98 98
use std::lang::ast;
99 99
use std::lang::il;
100 100
use std::lang::module;
101 101
use std::lang::resolver;
102 +
use std::lang::vec;
103 +
104 +
instantiate vec::append⟨ErrTagEntry⟩,
105 +
            vec::append⟨*il::Fn⟩,
106 +
            vec::append⟨il::DataValue⟩,
107 +
            vec::append⟨u8⟩,
108 +
            vec::append⟨FnSymEntry⟩,
109 +
            vec::append⟨DataSymEntry⟩,
110 +
            vec::append⟨il::Data⟩,
111 +
            vec::append⟨BlockData⟩,
112 +
            vec::append⟨il::Param⟩,
113 +
            vec::append⟨il::SrcLoc⟩,
114 +
            vec::append⟨il::Instr⟩,
115 +
            vec::append⟨u32⟩,
116 +
            vec::append⟨VarData⟩,
117 +
            vec::append⟨FnParamBinding⟩,
118 +
            vec::append⟨il::SwitchCase⟩,
119 +
            vec::view⟨ErrTagEntry⟩,
120 +
            vec::view⟨il::DataValue⟩,
121 +
            vec::view⟨*il::Fn⟩,
122 +
            vec::view⟨il::Data⟩,
123 +
            vec::view⟨u8⟩,
124 +
            vec::view⟨FnSymEntry⟩,
125 +
            vec::view⟨resolver::TraitMethod⟩,
126 +
            vec::view⟨u32⟩,
127 +
            vec::view⟨il::Param⟩,
128 +
            vec::view⟨il::SrcLoc⟩,
129 +
            vec::view⟨FnParamBinding⟩,
130 +
            vec::viewMut⟨il::Instr⟩,
131 +
            vec::viewMut⟨il::SwitchCase⟩;
102 132
103 133
// TODO: Search for all `_ as i32` to ensure that casts from u32 to i32 don't
104 134
// happen, since they are potentially truncating values.
105 135
106 136
// TODO: Support constant union lowering.
169 199
}
170 200
171 201
/// Incrementally build deterministic internal symbol names.
172 202
record NameBuilder {
173 203
    /// Accumulated name bytes.
174 -
    bytes: *mut [u8],
204 +
    bytes: vec::Vec⟨u8⟩,
175 205
    /// Allocator used to grow the name.
176 206
    allocator: alloc::Allocator,
177 207
}
178 208
179 209
/// Print a LowerError for debugging.
217 247
/// Maximum number of `catch` clauses per `try`.
218 248
constant MAX_CATCH_CLAUSES: u32 = 32;
219 249
220 250
// Slice Layout
221 251
//
222 -
// A slice is a fat pointer consisting of a data pointer, a length and a capacity.
223 -
// `{ ptr: u32, len: u32, cap: u32 }`.
252 +
// A slice is a 16-byte fat pointer consisting of a data pointer, a length and
253 +
// four bytes of padding. `{ ptr: u64, len: u32, padding: u32 }`.
224 254
225 255
/// Slice data pointer offset.
226 256
constant SLICE_PTR_OFFSET: i32 = 0;
227 257
/// Offset of slice length in slice data structure.
228 258
constant SLICE_LEN_OFFSET: i32 = resolver::PTR_SIZE as i32;
229 -
/// Offset of slice capacity in slice data structure.
230 -
constant SLICE_CAP_OFFSET: i32 = resolver::PTR_SIZE as i32 + 4;
231 259
232 260
// Trait Object Layout
233 261
//
234 262
// A trait object is a fat pointer consisting of a data pointer and a
235 263
// v-table pointer. `{ data: *T, vtable: *VTable }`.
283 311
    emitFn: fn(*mut opaque, *il::Fn, FnRole),
284 312
}
285 313
286 314
/// Destination for functions produced by the lowerer.
287 315
export union FnOutput {
288 -
    /// Store lowered functions in the provided slice.
289 -
    Accumulate(*mut [*il::Fn]),
316 +
    /// Accumulate lowered functions in arena-backed vector storage.
317 +
    Accumulate(vec::Vec⟨*il::Fn⟩),
290 318
    /// Send lowered functions to an external consumer immediately.
291 319
    Stream(FnSink),
292 320
}
293 321
294 322
/// Module-level lowering context shared across all function lowerings.
304 332
    allocator: alloc::Allocator,
305 333
    /// Fully resolved semantic metadata queried without mutation.
306 334
    resolver: *resolver::Resolver,
307 335
    /// Module graph for cross-module symbol resolution.
308 336
    moduleGraph: ?*module::ModuleGraph,
309 -
    /// Package name for qualified symbol names.
337 +
    /// Fallback package name for declarations without a module path.
310 338
    pkgName: *[u8],
311 339
    /// Current module being lowered.
312 340
    currentMod: ?u16,
313 341
    /// Rigid-to-concrete mapping for the function currently being lowered.
314 342
    specialization: ?*resolver::Substitution,
315 343
    /// Concrete generic function whose body is currently being lowered.
316 344
    genericSpecialization: ?*resolver::GenericFnSpecialization,
317 345
    /// Global data items (string literals, constants, static arrays).
318 346
    /// These become the data sections in the final binary.
319 -
    data: *mut [il::Data],
347 +
    data: vec::Vec⟨il::Data⟩,
320 348
    /// Destination for lowered functions.
321 349
    output: FnOutput,
322 350
    /// Map of function symbols to qualified names.
323 -
    fnSyms: *mut [FnSymEntry],
351 +
    fnSyms: vec::Vec⟨FnSymEntry⟩,
324 352
    /// Global error tag table keyed by persistent concrete types.
325 -
    errTags: *mut [ErrTagEntry],
353 +
    errTags: vec::Vec⟨ErrTagEntry⟩,
326 354
    /// Next error tag to assign (starts at 1; 0 = success).
327 355
    errTagCounter: u32,
328 356
    /// Lowering options.
329 357
    options: LowerOptions,
330 358
}
367 395
fn getOrAssignErrorTag(
368 396
    self: *mut Lowerer,
369 397
    errType: resolver::Type,
370 398
) -> u32 {
371 399
    assert not resolver::containsGenericParameter(errType);
372 -
    for entry in self.errTags {
400 +
    for entry in vec::view⟨ErrTagEntry⟩(&self.errTags) {
373 401
        if resolver::typesEqual(entry.ty, errType) {
374 402
            return entry.tag;
375 403
        }
376 404
    }
377 405
    let tag = self.errTagCounter;
378 406
    set self.errTagCounter += 1;
379 -
    self.errTags.append(ErrTagEntry {
407 +
    vec::append⟨ErrTagEntry⟩(&mut self.errTags, ErrTagEntry {
380 408
        ty: resolver::copyStructuralTypeToArena(errType, self.arena),
381 409
        tag,
382 410
    }, self.allocator);
383 411
    return tag;
384 412
}
386 414
/// Emit one function to the active output.
387 415
fn emitFunction(self: *mut Lowerer, func: *il::Fn, role: FnRole) {
388 416
    match self.output {
389 417
        case FnOutput::Accumulate(accumulated) => {
390 418
            let mut fns = accumulated;
391 -
            fns.append(func, self.allocator);
419 +
            vec::append⟨*il::Fn⟩(&mut fns, func, self.allocator);
392 420
            set self.output = FnOutput::Accumulate(fns);
393 421
        }
394 422
        case FnOutput::Stream(sink) => {
395 423
            sink.emitFn(sink.ctx, func, role);
396 424
        }
405 433
    return FnRole::Normal;
406 434
}
407 435
408 436
/// Builder for accumulating data values during constant lowering.
409 437
record DataValueBuilder {
438 +
    /// Allocator used to grow the value storage.
410 439
    allocator: alloc::Allocator,
411 -
    values: *mut [il::DataValue],
440 +
    /// Values accumulated in initialization order.
441 +
    values: vec::Vec⟨il::DataValue⟩,
412 442
    /// Whether all pushed values can be represented by zero-filled memory.
413 443
    zeroInit: bool,
414 444
}
415 445
416 446
/// Result of lowering constant data.
419 449
    zeroInit: bool,
420 450
}
421 451
422 452
/// Create a new builder.
423 453
fn dataBuilder(allocator: alloc::Allocator) -> DataValueBuilder {
424 -
    return DataValueBuilder { allocator, values: &mut [], zeroInit: true };
454 +
    return DataValueBuilder {
455 +
        allocator,
456 +
        values: vec::Vec⟨il::DataValue⟩ { data: &mut [], len: 0 },
457 +
        zeroInit: true,
458 +
    };
425 459
}
426 460
427 461
/// Return whether a data item can be omitted from a zero-filled image.
428 462
fn dataIsZero(item: il::DataItem) -> bool {
429 463
    match item {
443 477
    return true;
444 478
}
445 479
446 480
/// Append a data value to the builder.
447 481
fn dataBuilderPush(b: *mut DataValueBuilder, value: il::DataValue) {
448 -
    b.values.append(value, b.allocator);
482 +
    vec::append⟨il::DataValue⟩(&mut b.values, value, b.allocator);
449 483
450 484
    if value.count > 0 and not dataIsZero(value.item) {
451 485
        set b.zeroInit = false;
452 486
    }
453 487
}
454 488
455 489
/// Return the accumulated values.
456 490
fn dataBuilderFinish(b: *DataValueBuilder) -> ConstDataResult {
457 491
    return ConstDataResult {
458 -
        values: &b.values[..],
492 +
        values: vec::view⟨il::DataValue⟩(&b.values),
459 493
        zeroInit: b.zeroInit,
460 494
    };
461 495
}
462 496
463 497
///////////////////////////
532 566
record BlockData {
533 567
    /// Block label for debugging and IL printing.
534 568
    label: *[u8],
535 569
    /// Block parameters for merging values at control flow joins. These
536 570
    /// receive values from predecessor edges when control flow merges.
537 -
    params: *mut [il::Param],
571 +
    params: vec::Vec⟨il::Param⟩,
538 572
    /// Variable ids corresponding to each parameter. Used to map block params
539 573
    /// back to source variables when building argument lists for jumps.
540 -
    paramVars: *mut [u32],
574 +
    paramVars: vec::Vec⟨u32⟩,
541 575
    /// Instructions accumulated so far. The last instruction should eventually
542 576
    /// be a terminator.
543 -
    instrs: *mut [il::Instr],
577 +
    instrs: vec::Vec⟨il::Instr⟩,
544 578
    /// Debug source locations, one per instruction. Only populated when
545 579
    /// debug info is enabled.
546 -
    locs: *mut [il::SrcLoc],
580 +
    locs: vec::Vec⟨il::SrcLoc⟩,
547 581
    /// Predecessor block ids. Used for SSA construction to propagate values
548 582
    /// from predecessors when a variable is used before being defined locally.
549 -
    preds: *mut [u32],
583 +
    preds: vec::Vec⟨u32⟩,
550 584
    /// The current SSA value of each variable in this block. Indexed by variable
551 585
    /// id. A `nil` means the variable wasn't assigned in this block. Updated by
552 586
    /// [`defVar`], queried by [`useVarInBlock`].
553 587
    vars: *mut [?il::Val],
554 588
    /// Sealing state. Once sealed, all predecessors are known and we can resolve
564 598
/// During this time, variables used before being defined locally are tracked.
565 599
/// Once all predecessors are known, the block is sealed and those variables
566 600
/// are resolved via [`resolveBlockArgs`].
567 601
union Sealed {
568 602
    /// Block is unsealed; predecessors may still be added.
569 -
    No { incompleteVars: *mut [u32] },
603 +
    No {
604 +
        /// Variables whose block parameters must be resolved when sealed.
605 +
        incompleteVars: vec::Vec⟨u32⟩,
606 +
    },
570 607
    /// Block is sealed; all predecessors are known.
571 608
    Yes,
572 609
}
573 610
574 611
///////////////////////////////////
707 744
    /// Type signature of the function being lowered.
708 745
    fnType: *resolver::FnType,
709 746
    /// Function name, used as prefix for generated data symbols.
710 747
    fnName: *[u8],
711 748
    /// Names assigned to data declarations in this function.
712 -
    dataSyms: *mut [DataSymEntry],
749 +
    dataSyms: vec::Vec⟨DataSymEntry⟩,
713 750
714 751
    // ~ SSA variable tracking ~ //
715 752
716 753
    /// Metadata (name, type, mutability) for each variable. Indexed by variable
717 754
    /// id. Doesn't change after declaration. For the SSA value of a variable in
718 755
    /// a specific block, see [`BlockData::vars`].
719 -
    vars: *mut [VarData],
756 +
    vars: vec::Vec⟨VarData⟩,
720 757
    /// Parameter-to-variable bindings, initialized in the entry block.
721 -
    params: *mut [FnParamBinding],
758 +
    params: vec::Vec⟨FnParamBinding⟩,
722 759
723 760
    // ~ Basic block management ~ //
724 761
725 762
    /// Block storage array, indexed by block id.
726 -
    blockData: *mut [BlockData],
763 +
    blockData: vec::Vec⟨BlockData⟩,
727 764
    /// The entry block for this function.
728 765
    entryBlock: ?BlockId,
729 766
    /// The block currently receiving new instructions.
730 767
    currentBlock: ?BlockId,
731 768
781 818
        moduleGraph: nil,
782 819
        pkgName,
783 820
        currentMod: nil,
784 821
        specialization: nil,
785 822
        genericSpecialization: nil,
786 -
        data: &mut [],
787 -
        output: FnOutput::Accumulate(&mut []),
788 -
        fnSyms: &mut [],
789 -
        errTags: &mut [],
823 +
        data: vec::Vec⟨il::Data⟩ { data: &mut [], len: 0 },
824 +
        output: FnOutput::Accumulate(vec::Vec⟨*il::Fn⟩ { data: &mut [], len: 0 }),
825 +
        fnSyms: vec::Vec⟨FnSymEntry⟩ { data: &mut [], len: 0 },
826 +
        errTags: vec::Vec⟨ErrTagEntry⟩ { data: &mut [], len: 0 },
790 827
        errTagCounter: 1,
791 828
        options: LowerOptions { debug: false, buildTest: false },
792 829
    };
793 830
    try lowerDecls(&mut low, root, true);
794 831
816 853
        moduleGraph: graph,
817 854
        pkgName,
818 855
        currentMod: nil,
819 856
        specialization: nil,
820 857
        genericSpecialization: nil,
821 -
        data: &mut [],
822 -
        output: FnOutput::Accumulate(&mut []),
823 -
        fnSyms: &mut [],
824 -
        errTags: &mut [],
858 +
        data: vec::Vec⟨il::Data⟩ { data: &mut [], len: 0 },
859 +
        output: FnOutput::Accumulate(vec::Vec⟨*il::Fn⟩ { data: &mut [], len: 0 }),
860 +
        fnSyms: vec::Vec⟨FnSymEntry⟩ { data: &mut [], len: 0 },
861 +
        errTags: vec::Vec⟨ErrTagEntry⟩ { data: &mut [], len: 0 },
825 862
        errTagCounter: 1,
826 863
        options,
827 864
    };
828 865
}
829 866
926 963
    }
927 964
}
928 965
929 966
/// Finalize lowering and return the unified IL program.
930 967
export fn finalize(low: *Lowerer) -> il::Program {
931 -
    let mut fns: *mut [*il::Fn] = undefined;
968 +
    let mut fns: *[*il::Fn] = &[];
932 969
    match low.output {
933 -
        case FnOutput::Accumulate(accumulated) => {
934 -
            set fns = accumulated;
935 -
        }
936 -
        case FnOutput::Stream(_) => {
937 -
            panic "finalize: cannot finalize streaming lowerer";
938 -
        }
970 +
        case FnOutput::Accumulate(accumulated) =>
971 +
            set fns = vec::view⟨*il::Fn⟩(&accumulated),
972 +
        case FnOutput::Stream(_) =>
973 +
            panic "finalize: cannot finalize streaming lowerer",
939 974
    }
940 975
    return il::Program {
941 -
        data: &low.data[..],
942 -
        fns: &fns[..],
976 +
        data: vec::view⟨il::Data⟩(&low.data),
977 +
        fns,
943 978
    };
944 979
}
945 980
946 981
/////////////////////////////////
947 982
// Qualified Name Construction //
977 1012
}
978 1013
979 1014
/// Append bytes to a symbol name.
980 1015
fn namePush(builder: *mut NameBuilder, text: *[u8]) {
981 1016
    for byte in text {
982 -
        builder.bytes.append(byte, builder.allocator);
1017 +
        vec::append⟨u8(&mut builder.bytes, byte, builder.allocator);
983 1018
    }
984 1019
}
985 1020
986 1021
/// Append a decimal `u32` to a symbol name.
987 1022
fn namePushU32(builder: *mut NameBuilder, value: u32) {
993 1028
fn namePushU64(builder: *mut NameBuilder, value: u64) {
994 1029
    let mut digits: [u8; 20] = undefined;
995 1030
    namePush(builder, fmt::formatU64(value, &mut digits[..]));
996 1031
}
997 1032
998 -
/// Append the stable, source-qualified name of a module-level declaration.
1033 +
/// Append the canonical source-qualified name of a module-level declaration.
1034 +
/// Non-empty module paths already begin with the defining package.
999 1035
fn namePushQualified(
1000 1036
    self: *mut Lowerer,
1001 1037
    builder: *mut NameBuilder,
1002 1038
    modId: ?u16,
1003 1039
    name: *[u8],
1004 1040
) {
1005 1041
    let path = getModulePath(self, modId);
1006 -
    if path.len == 0 or path[0] <> self.pkgName {
1042 +
    if path.len == 0 {
1007 1043
        namePush(builder, self.pkgName);
1008 1044
        namePush(builder, "::");
1009 1045
    }
1010 1046
    for segment in path {
1011 1047
        namePush(builder, segment);
1064 1100
        }
1065 1101
        case resolver::Type::Array(array) => {
1066 1102
            namePush(builder, "[");
1067 1103
            namePushType(self, builder, *array.item);
1068 1104
            namePush(builder, "; ");
1069 -
            namePushU32(builder, array.length);
1105 +
            namePushType(self, builder, *array.length);
1070 1106
            namePush(builder, "]");
1071 1107
        }
1072 1108
        case resolver::Type::ConstArgument { value, .. } => {
1073 1109
            if value.negative { namePush(builder, "-"); }
1074 1110
            namePushU64(builder, value.magnitude);
1150 1186
fn specializationName(
1151 1187
    self: *mut Lowerer,
1152 1188
    specialization: *resolver::GenericFnSpecialization,
1153 1189
) -> *[u8] {
1154 1190
    let template = specialization.template;
1155 -
    let mut builder = NameBuilder { bytes: &mut [], allocator: self.allocator };
1191 +
    let mut builder = NameBuilder {
1192 +
        bytes: vec::Vec⟨u8{ data: &mut [], len: 0 },
1193 +
        allocator: self.allocator,
1194 +
    };
1156 1195
    namePushQualified(self, &mut builder, template.moduleId, template.name);
1157 1196
    namePush(&mut builder, "⟨");
1158 1197
    for arg, i in specialization.args {
1159 1198
        if i > 0 { namePush(&mut builder, ", "); }
1160 1199
        namePushType(self, &mut builder, *arg);
1161 1200
    }
1162 1201
    namePush(&mut builder, "⟩");
1163 -
    return &builder.bytes[..];
1202 +
    return vec::view⟨u8(&builder.bytes);
1164 1203
}
1165 1204
1166 1205
/// Register a function symbol with its qualified name.
1167 1206
/// Called when lowering function declarations, so cross-package calls can find
1168 1207
/// the function by name.
1169 1208
fn registerFnSym(self: *mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) {
1170 -
    self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
1209 +
    vec::append⟨FnSymEntry⟩(
1210 +
        &mut self.fnSyms,
1211 +
        FnSymEntry { sym, qualName },
1212 +
        self.allocator,
1213 +
    );
1171 1214
}
1172 1215
1173 1216
/// Look up a function's qualified name by its symbol.
1174 1217
/// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered).
1175 1218
// TODO: This is kind of dubious as an optimization, if it depends on the order
1176 1219
// in which modules are lowered.
1177 1220
// TODO: Use a hash table here?
1178 1221
fn lookupFnSym(self: *Lowerer, sym: *resolver::Symbol) -> ?*[u8] {
1179 -
    for entry in self.fnSyms {
1222 +
    for entry in vec::view⟨FnSymEntry⟩(&self.fnSyms) {
1180 1223
        if entry.sym == sym {
1181 1224
            return entry.qualName;
1182 1225
        }
1183 1226
    }
1184 1227
    return nil;
1188 1231
fn registerDataSym(
1189 1232
    self: *mut FnLowerer,
1190 1233
    sym: *resolver::Symbol,
1191 1234
    qualName: *[u8],
1192 1235
) {
1193 -
    self.dataSyms.append(DataSymEntry { sym, qualName }, self.allocator);
1236 +
    vec::append⟨DataSymEntry⟩(
1237 +
        &mut self.dataSyms,
1238 +
        DataSymEntry { sym, qualName },
1239 +
        self.allocator,
1240 +
    );
1194 1241
}
1195 1242
1196 1243
/// Look up the current function's most recently registered local data name.
1197 1244
fn lookupDataSym(self: *FnLowerer, sym: *resolver::Symbol) -> ?*[u8] {
1198 1245
    let mut i = self.dataSyms.len;
1199 1246
    while i > 0 {
1200 1247
        set i -= 1;
1201 -
        let entry = &self.dataSyms[i];
1248 +
        let entry = &self.dataSyms.data[i];
1202 1249
        if entry.sym == sym {
1203 1250
            return entry.qualName;
1204 1251
        }
1205 1252
    }
1206 1253
    return nil;
1226 1273
    let mut fnLow = FnLowerer {
1227 1274
        low: self,
1228 1275
        allocator: alloc::arenaAllocator(self.fnArena),
1229 1276
        fnType: fnType,
1230 1277
        fnName: qualName,
1231 -
        dataSyms: &mut [],
1232 -
        vars: &mut [],
1233 -
        params: &mut [],
1234 -
        blockData: &mut [],
1278 +
        dataSyms: vec::Vec⟨DataSymEntry⟩ { data: &mut [], len: 0 },
1279 +
        vars: vec::Vec⟨VarData⟩ { data: &mut [], len: 0 },
1280 +
        params: vec::Vec⟨FnParamBinding⟩ { data: &mut [], len: 0 },
1281 +
        blockData: vec::Vec⟨BlockData⟩ { data: &mut [], len: 0 },
1235 1282
        entryBlock: nil,
1236 1283
        currentBlock: nil,
1237 1284
        loopStack,
1238 1285
        loopDepth: 0,
1239 1286
        labelCounter: 0,
1331 1378
    self: *mut Lowerer,
1332 1379
    concreteType: resolver::Type,
1333 1380
    traitInfo: ?*resolver::TraitType,
1334 1381
    methodName: *[u8],
1335 1382
) -> *[u8] {
1336 -
    let mut builder = NameBuilder { bytes: &mut [], allocator: self.allocator };
1383 +
    let mut builder = NameBuilder {
1384 +
        bytes: vec::Vec⟨u8{ data: &mut [], len: 0 },
1385 +
        allocator: self.allocator,
1386 +
    };
1337 1387
    namePushType(self, &mut builder, concreteType);
1338 1388
    if let traitValue = traitInfo {
1339 1389
        namePush(&mut builder, " ");
1340 1390
        namePushTrait(self, &mut builder, traitValue);
1341 1391
    }
1342 1392
    namePush(&mut builder, "::");
1343 1393
    namePush(&mut builder, methodName);
1344 -
    return &builder.bytes[..];
1394 +
    return vec::view⟨u8(&builder.bytes);
1345 1395
}
1346 1396
1347 1397
/// Build a readable v-table name from its concrete type and trait.
1348 1398
fn vtableName(
1349 1399
    self: *mut Lowerer,
1350 1400
    concreteType: resolver::Type,
1351 1401
    traitInfo: *resolver::TraitType,
1352 1402
) -> *[u8] {
1353 -
    let mut builder = NameBuilder { bytes: &mut [], allocator: self.allocator };
1403 +
    let mut builder = NameBuilder {
1404 +
        bytes: vec::Vec⟨u8{ data: &mut [], len: 0 },
1405 +
        allocator: self.allocator,
1406 +
    };
1354 1407
    namePush(&mut builder, "vtable::");
1355 1408
    namePushType(self, &mut builder, concreteType);
1356 1409
    namePush(&mut builder, " ");
1357 1410
    namePushTrait(self, &mut builder, traitInfo);
1358 -
    return &builder.bytes[..];
1411 +
    return vec::view⟨u8(&builder.bytes);
1359 1412
}
1360 1413
1361 1414
/// Lower an instance declaration (`instance Trait for Type { ... }`).
1362 1415
///
1363 1416
/// Each method in the instance block is lowered as a standalone function
1410 1463
        set methodNameSet[method.index] = true;
1411 1464
    }
1412 1465
1413 1466
    // Fill inherited method slots from their declaring supertraits. Their
1414 1467
    // declaring-trait identity selects the already lowered implementation.
1415 -
    for method, i in traitInfo.methods {
1468 +
    for method, i in vec::view⟨resolver::TraitMethod⟩(&traitInfo.methods) {
1416 1469
        if not methodNameSet[i] {
1417 1470
            let inheritedInst = resolver::findInstance(
1418 1471
                self.resolver, method.owner, instEntry.concreteType
1419 1472
            ) else throw LowerError::MissingMetadata;
1420 1473
            set methodNames[i] = instanceMethodName(
1436 1489
        set values[i] = il::DataValue {
1437 1490
            item: il::DataItem::Fn(methodNames[i]),
1438 1491
            count: 1,
1439 1492
        };
1440 1493
    }
1441 -
    self.data.append(il::Data {
1494 +
    vec::append⟨il::Data⟩(&mut self.data, il::Data {
1442 1495
        name: vName,
1443 1496
        size: traitInfo.methods.len as u32 * resolver::PTR_SIZE,
1444 1497
        alignment: resolver::PTR_SIZE,
1445 1498
        readOnly: true,
1446 1499
        isZeroInit: false,
1652 1705
    }
1653 1706
    let mut b = dataBuilder(self.allocator);
1654 1707
    try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b);
1655 1708
    let result = dataBuilderFinish(&b);
1656 1709
1657 -
    self.data.append(il::Data {
1710 +
    vec::append⟨il::Data⟩(&mut self.data, il::Data {
1658 1711
        name: qualName,
1659 1712
        size: layout.size,
1660 1713
        alignment: layout.alignment,
1661 1714
        readOnly,
1662 1715
        isZeroInit: not readOnly and result.zeroInit,
1663 1716
        values: result.values,
1664 1717
    }, self.allocator);
1665 1718
}
1666 1719
1667 -
/// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`.
1720 +
/// Emit a 16-byte slice header containing a pointer, length, and padding.
1668 1721
fn dataSliceHeader(b: *mut DataValueBuilder, dataSym: *[u8], len: u32) {
1669 1722
    dataBuilderPush(b, il::DataValue {
1670 1723
        item: il::DataItem::Sym(dataSym),
1671 1724
        count: 1
1672 1725
    });
1676 1729
            val: len as i64
1677 1730
        },
1678 1731
        count: 1
1679 1732
    });
1680 1733
    dataBuilderPush(b, il::DataValue {
1681 -
        item: il::DataItem::Val {
1682 -
            typ: il::Type::W32,
1683 -
            val: len as i64
1684 -
        },
1685 -
        count: 1
1734 +
        item: il::DataItem::Undef,
1735 +
        count: 4,
1686 1736
    });
1687 1737
}
1688 1738
1689 1739
/// Lower a compile-time `&[...]` expression to a concrete slice header.
1690 1740
fn lowerConstAddressSliceInto(
1715 1765
            set dataName = try pushDeclData(self, layout.size, layout.alignment, readOnly, backing.values, dataPrefix);
1716 1766
        }
1717 1767
    } else {
1718 1768
        set dataName = try pushDeclData(self, layout.size, layout.alignment, readOnly, backing.values, dataPrefix);
1719 1769
    }
1720 -
    dataSliceHeader(b, dataName, arrInfo.length);
1770 +
    let length = resolver::concreteArrayLength(arrInfo.length)
1771 +
        else throw LowerError::MissingMetadata;
1772 +
    dataSliceHeader(b, dataName, length);
1721 1773
}
1722 1774
1723 1775
/// Lower a constant expression payload into a builder without slot padding.
1724 1776
fn lowerConstDataPayloadInto(
1725 1777
    self: *mut Lowerer,
1848 1900
    dataPrefix: *[u8],
1849 1901
    b: *mut DataValueBuilder
1850 1902
) throws (LowerError) {
1851 1903
    let case resolver::Type::Array(arrInfo) = ty
1852 1904
        else throw LowerError::ExpectedArray;
1853 -
    let length = arrInfo.length;
1905 +
    let length = resolver::concreteArrayLength(arrInfo.length)
1906 +
        else throw LowerError::MissingMetadata;
1854 1907
    let elemTy = *arrInfo.item;
1855 1908
    let elemLayout = resolver::getTypeLayout(elemTy);
1856 1909
1857 1910
    if let case ast::NodeValue::Undef = repeat.item.value {
1858 1911
        dataBuilderPush(b, il::DataValue {
1992 2045
}
1993 2046
1994 2047
/// Find an existing string data entry with matching content.
1995 2048
// TODO: Optimize with hash table or remove?
1996 2049
fn findStringData(self: *Lowerer, s: *[u8]) -> ?*[u8] {
1997 -
    for d in self.data {
2050 +
    for d in vec::view⟨il::Data⟩(&self.data) {
1998 2051
        if d.values.len == 1 {
1999 2052
            if let case il::DataItem::Str(existing) = d.values[0].item {
2000 2053
                if mem::eq(existing, s) {
2001 2054
                    return d.name;
2002 2055
                }
2058 2111
    readOnly: bool,
2059 2112
    values: *[il::DataValue],
2060 2113
    dataPrefix: *[u8]
2061 2114
) -> *[u8] throws (LowerError) {
2062 2115
    let name = try nextDeclDataName(self, dataPrefix, self.data.len, "literal");
2063 -
    self.data.append(il::Data {
2116 +
    vec::append⟨il::Data⟩(&mut self.data, il::Data {
2064 2117
        name,
2065 2118
        size,
2066 2119
        alignment,
2067 2120
        readOnly,
2068 2121
        isZeroInit: not readOnly and dataValuesAreZeroInit(values),
2146 2199
}
2147 2200
2148 2201
/// Find an existing read-only slice data entry with matching values.
2149 2202
// TODO: Optimize with hash table or remove?
2150 2203
fn findSliceData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
2151 -
    for d in self.data {
2204 +
    for d in vec::view⟨il::Data⟩(&self.data) {
2152 2205
        if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) {
2153 2206
            return d.name;
2154 2207
        }
2155 2208
    }
2156 2209
    return nil;
2186 2239
    if readOnly {
2187 2240
        if let found = findConstData(self.low, values, alignment) {
2188 2241
            set dataName = found;
2189 2242
        } else {
2190 2243
            set dataName = try nextDataName(self);
2191 -
            self.low.data.append(il::Data {
2244 +
            vec::append⟨il::Data⟩(&mut self.low.data, il::Data {
2192 2245
                name: dataName,
2193 2246
                size,
2194 2247
                alignment,
2195 2248
                readOnly,
2196 2249
                isZeroInit: false,
2197 2250
                values,
2198 2251
            }, self.low.allocator);
2199 2252
        }
2200 2253
    } else {
2201 2254
        set dataName = try nextDataName(self);
2202 -
        self.low.data.append(il::Data {
2255 +
        vec::append⟨il::Data⟩(&mut self.low.data, il::Data {
2203 2256
            name: dataName,
2204 2257
            size,
2205 2258
            alignment,
2206 2259
            readOnly,
2207 2260
            isZeroInit: dataValuesAreZeroInit(values),
2212 2265
    // Get data address.
2213 2266
    let ptrReg = nextReg(self);
2214 2267
    emit(self, il::Instr::Copy { dst: ptrReg, val: il::Val::DataSym(dataName) });
2215 2268
2216 2269
    return try buildSliceValue(
2217 -
        self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64)
2270 +
        self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64)
2218 2271
    );
2219 2272
}
2220 2273
2221 2274
/// Generate a unique data name for inline literals, eg. `fnName$literal$N`.
2222 2275
fn nextDataName(self: *mut FnLowerer) -> *[u8] throws (LowerError) {
2284 2337
/// Remove the last block parameter and its associated variable.
2285 2338
/// Used when detecting a trivial phi that can be eliminated.
2286 2339
fn removeLastBlockParam(self: *mut FnLowerer, block: BlockId) {
2287 2340
    let blk = getBlockMut(self, block);
2288 2341
    if blk.params.len > 0 {
2289 -
        // TODO: Use `pop`?
2290 -
        set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap);
2342 +
        set blk.params.len -= 1;
2291 2343
    }
2292 2344
    if blk.paramVars.len > 0 {
2293 -
        // TODO: Use `pop`?
2294 -
        set blk.paramVars = @sliceOf(blk.paramVars.ptr, blk.paramVars.len - 1, blk.paramVars.cap);
2345 +
        set blk.paramVars.len -= 1;
2295 2346
    }
2296 2347
}
2297 2348
2298 2349
/// Rewrite cached SSA values for a variable across all blocks, and also
2299 2350
/// rewrite any terminator arguments that reference the provisional register.
2306 2357
        if blk.vars[*v] == from {
2307 2358
            set blk.vars[*v] = to;
2308 2359
        }
2309 2360
        if blk.instrs.len > 0 {
2310 2361
            let ix = blk.instrs.len - 1;
2311 -
            match &mut blk.instrs[ix] {
2362 +
            match &mut blk.instrs.data[ix] {
2312 2363
                case il::Instr::Jmp { args, .. } =>
2313 2364
                    rewriteValInSlice(*args, from, to),
2314 2365
                case il::Instr::Br { thenArgs, elseArgs, .. } => {
2315 2366
                    rewriteValInSlice(*thenArgs, from, to);
2316 2367
                    rewriteValInSlice(*elseArgs, from, to);
2364 2415
    let vars = try! alloc::allocSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val];
2365 2416
2366 2417
    for i in 0..varCount {
2367 2418
        set vars[i] = nil;
2368 2419
    }
2369 -
    self.blockData.append(BlockData {
2420 +
    vec::append⟨BlockData⟩(&mut self.blockData, BlockData {
2370 2421
        label,
2371 -
        params: &mut [],
2372 -
        paramVars: &mut [],
2373 -
        instrs: &mut [],
2374 -
        locs: &mut [],
2375 -
        preds: &mut [],
2422 +
        params: vec::Vec⟨il::Param⟩ { data: &mut [], len: 0 },
2423 +
        paramVars: vec::Vec⟨u32{ data: &mut [], len: 0 },
2424 +
        instrs: vec::Vec⟨il::Instr⟩ { data: &mut [], len: 0 },
2425 +
        locs: vec::Vec⟨il::SrcLoc⟩ { data: &mut [], len: 0 },
2426 +
        preds: vec::Vec⟨u32{ data: &mut [], len: 0 },
2376 2427
        vars,
2377 -
        sealState: Sealed::No { incompleteVars: &mut [] },
2428 +
        sealState: Sealed::No {
2429 +
            incompleteVars: vec::Vec⟨u32{ data: &mut [], len: 0 },
2430 +
        },
2378 2431
        loopDepth: self.loopDepth,
2379 2432
    }, self.allocator);
2380 2433
2381 2434
    return id;
2382 2435
}
2387 2440
    labelBase: *[u8],
2388 2441
    param: il::Param
2389 2442
) -> BlockId throws (LowerError) {
2390 2443
    let block = try createBlock(self, labelBase);
2391 2444
    let blk = getBlockMut(self, block);
2392 -
    blk.params.append(param, self.allocator);
2445 +
    vec::append⟨il::Param⟩(&mut blk.params, param, self.allocator);
2393 2446
2394 2447
    return block;
2395 2448
}
2396 2449
2397 2450
/// Switch to building a different block.
2411 2464
        return; // Already sealed.
2412 2465
    };
2413 2466
    set blk.sealState = Sealed::Yes;
2414 2467
2415 2468
    // Complete all incomplete block parameters.
2416 -
    for varId in incompleteVars {
2469 +
    for varId in vec::view⟨u32(&incompleteVars) {
2417 2470
        try resolveBlockArgs(self, block, Var(varId));
2418 2471
    }
2419 2472
}
2420 2473
2421 2474
/// Seal a block and switch to it.
2424 2477
    switchToBlock(self, block);
2425 2478
}
2426 2479
2427 2480
/// Get block data by block id.
2428 2481
fn getBlock(self: *FnLowerer, block: BlockId) -> *BlockData {
2429 -
    return &self.blockData[*block];
2482 +
    return &self.blockData.data[*block];
2430 2483
}
2431 2484
2432 2485
/// Get mutable block data by block id.
2433 2486
fn getBlockMut(self: *mut FnLowerer, block: BlockId) -> *mut BlockData {
2434 -
    return &mut self.blockData[*block];
2487 +
    return &mut self.blockData.data[*block];
2435 2488
}
2436 2489
2437 2490
/// Get the current block being built.
2438 2491
fn currentBlock(self: *FnLowerer) -> BlockId {
2439 2492
    let block = self.currentBlock else {
2459 2512
            else => {},
2460 2513
        }
2461 2514
    }
2462 2515
    // Record source location alongside instruction when enabled.
2463 2516
    if self.low.options.debug {
2464 -
        block.locs.append(self.srcLoc, self.allocator);
2517 +
        vec::append⟨il::SrcLoc⟩(&mut block.locs, self.srcLoc, self.allocator);
2465 2518
    }
2466 -
    block.instrs.append(instr, self.allocator);
2519 +
    vec::append⟨il::Instr⟩(&mut block.instrs, instr, self.allocator);
2467 2520
}
2468 2521
2469 2522
/// Emit an unconditional jump to `target`.
2470 2523
fn emitJmp(self: *mut FnLowerer, target: BlockId) throws (LowerError) {
2471 2524
    emit(self, il::Instr::Jmp { target: *target, args: &mut [] });
2621 2674
    let condReg = emitValToReg(self, condVal);
2622 2675
2623 2676
    try emitBr(self, condReg, thenBlock, elseBlock);
2624 2677
}
2625 2678
2626 -
/// Emit a 32-bit store instruction at the given offset.
2627 -
fn emitStoreW32At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2628 -
    emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset });
2629 -
}
2630 -
2631 2679
/// Emit a 32-bit load instruction at the given offset.
2632 2680
fn emitLoadW32At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2633 2681
    emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset });
2634 2682
}
2635 2683
2672 2720
    let lenReg = nextReg(self);
2673 2721
    emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET);
2674 2722
    return il::Val::Reg(lenReg);
2675 2723
}
2676 2724
2677 -
/// Load the capacity from a slice value.
2678 -
fn loadSliceCap(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2679 -
    let capReg = nextReg(self);
2680 -
    emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET);
2681 -
    return il::Val::Reg(capReg);
2682 -
}
2683 -
2684 2725
/// Emit a load instruction for a scalar value at `src` plus `offset`.
2685 2726
/// For reading values that may be aggregates, use `emitRead` instead.
2686 2727
fn emitLoad(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2687 2728
    let dst = nextReg(self);
2688 2729
    let ilTyp = ilType(self.low, typ);
2884 2925
fn blockHasTerminator(self: *FnLowerer) -> bool {
2885 2926
    let blk = getBlock(self, currentBlock(self));
2886 2927
    if blk.instrs.len == 0 {
2887 2928
        return false;
2888 2929
    }
2889 -
    match blk.instrs[blk.instrs.len - 1] {
2930 +
    match blk.instrs.data[blk.instrs.len - 1] {
2890 2931
        case il::Instr::Ret { .. },
2891 2932
             il::Instr::Jmp { .. },
2892 2933
             il::Instr::Br { .. },
2893 2934
             il::Instr::Switch { .. },
2894 2935
             il::Instr::Unreachable =>
2930 2971
/// Add a predecessor edge from `pred` to `target`.
2931 2972
/// Must be called before the target block is sealed. Duplicates are ignored.
2932 2973
fn addPredecessor(self: *mut FnLowerer, target: BlockId, pred: BlockId) {
2933 2974
    let blk = getBlockMut(self, target);
2934 2975
    assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block";
2935 -
    let preds = &mut blk.preds;
2936 -
    for i in 0..preds.len {
2937 -
        if preds[i] == *pred { // Avoid duplicate predecessor entries.
2976 +
    for i in 0..blk.preds.len {
2977 +
        if blk.preds.data[i] == *pred { // Avoid duplicate predecessor entries.
2938 2978
            return;
2939 2979
        }
2940 2980
    }
2941 -
    preds.append(*pred, self.allocator);
2981 +
    vec::append⟨u32(&mut blk.preds, *pred, self.allocator);
2942 2982
}
2943 2983
2944 2984
/// Finalize all blocks and return the block array.
2945 2985
fn finalizeBlocks(self: *mut FnLowerer) -> *[il::Block] throws (LowerError) {
2946 2986
    let blocks = try! alloc::allocSlice(
2947 2987
        self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), self.blockData.len
2948 2988
    ) as *mut [il::Block];
2949 2989
2950 2990
    for i in 0..self.blockData.len {
2951 -
        let data = &self.blockData[i];
2991 +
        let data = &mut self.blockData.data[i];
2952 2992
2953 2993
        set blocks[i] = il::Block {
2954 2994
            label: data.label,
2955 -
            params: &data.params[..],
2956 -
            instrs: data.instrs,
2957 -
            locs: &data.locs[..],
2958 -
            preds: &data.preds[..],
2995 +
            params: vec::view⟨il::Param⟩(&data.params),
2996 +
            instrs: vec::viewMut⟨il::Instr⟩(&mut data.instrs),
2997 +
            locs: vec::view⟨il::SrcLoc⟩(&data.locs),
2998 +
            preds: vec::view⟨u32(&data.preds),
2959 2999
            loopDepth: data.loopDepth,
2960 3000
        };
2961 3001
    }
2962 3002
    return &blocks[..self.blockData.len];
2963 3003
}
3112 3152
    type: il::Type,
3113 3153
    mutable: bool,
3114 3154
    val: il::Val
3115 3155
) -> Var {
3116 3156
    let id = self.vars.len;
3117 -
    self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator);
3157 +
    vec::append⟨VarData⟩(
3158 +
        &mut self.vars,
3159 +
        VarData { name, type, mutable, addressTaken: false },
3160 +
        self.allocator,
3161 +
    );
3118 3162
3119 3163
    let v = Var(id);
3120 3164
    if self.currentBlock <> nil {
3121 3165
        defVar(self, v, val);
3122 3166
    }
3162 3206
            throw LowerError::InvalidUse;
3163 3207
        }
3164 3208
        // Single predecessor means no merge needed, variable is implicitly
3165 3209
        // available without a block parameter.
3166 3210
        if blk.preds.len == 1 {
3167 -
            let pred = BlockId(blk.preds[0]);
3211 +
            let pred = BlockId(blk.preds.data[0]);
3168 3212
            if *pred <> *block {
3169 3213
                let val = try useVarInBlock(self, pred, v);
3170 3214
                set blk.vars[*v] = val; // Cache.
3171 3215
                return val;
3172 3216
            }
3181 3225
/// Searches from most recently declared to first, enabling shadowing.
3182 3226
fn lookupVarByName(self: *FnLowerer, name: *[u8]) -> ?Var {
3183 3227
    let mut id = self.vars.len;
3184 3228
    while id > 0 {
3185 3229
        set id -= 1;
3186 -
        if let varName = self.vars[id].name {
3230 +
        if let varName = self.vars.data[id].name {
3187 3231
            // Names are interned strings, so pointer comparison suffices.
3188 3232
            if varName == name {
3189 3233
                return Var(id);
3190 3234
            }
3191 3235
        }
3206 3250
    return self.vars.len;
3207 3251
}
3208 3252
3209 3253
/// Restore lexical variable scope depth.
3210 3254
fn exitVarScope(self: *mut FnLowerer, savedVarsLen: u32) {
3211 -
    set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap);
3255 +
    set self.vars.len = savedVarsLen;
3212 3256
}
3213 3257
3214 3258
/// Get the metadata for a variable.
3215 3259
fn getVar(self: *FnLowerer, v: Var) -> *VarData {
3216 3260
    assert *v < self.vars.len;
3217 -
    return &self.vars[*v];
3261 +
    return &self.vars.data[*v];
3218 3262
}
3219 3263
3220 3264
/// Create a block parameter to merge a variable's value from multiple
3221 3265
/// control flow paths.
3222 3266
///
3241 3285
    let type = getVar(self, v).type;
3242 3286
3243 3287
    // Create block parameter and add it to the block.
3244 3288
    let param = il::Param { value: reg, type };
3245 3289
    let blk = getBlockMut(self, block);
3246 -
    blk.params.append(param, self.allocator);
3247 -
    blk.paramVars.append(*v, self.allocator); // Associate variable with parameter.
3290 +
    vec::append⟨il::Param⟩(&mut blk.params, param, self.allocator);
3291 +
    vec::append⟨u32(&mut blk.paramVars, *v, self.allocator); // Associate variable with parameter.
3248 3292
3249 3293
    // Record that this variable's value in this block is now the parameter register.
3250 3294
    // This must happen before the predecessor loop to handle self-referential loops.
3251 3295
    set blk.vars[*v] = il::Val::Reg(reg);
3252 3296
3253 3297
    match &mut blk.sealState {
3254 3298
        case Sealed::No { incompleteVars } => {
3255 3299
            // Block unsealed: defer until sealing.
3256 -
            incompleteVars.append(*v, self.allocator);
3300 +
            vec::append⟨u32(incompleteVars, *v, self.allocator);
3257 3301
        },
3258 3302
        case Sealed::Yes => {
3259 3303
            // Block sealed: check for trivial phi before committing. If all
3260 3304
            // predecessors provide the same value, we can remove the param we
3261 3305
            // just created and use that value directly.
3289 3333
3290 3334
    // Find the parameter index corresponding to this variable.
3291 3335
    // Each variable that needs merging gets its own block parameter slot.
3292 3336
    let mut paramIdx: u32 = 0;
3293 3337
    for i in 0..blk.paramVars.len {
3294 -
        if blk.paramVars[i] == *v {
3338 +
        if blk.paramVars.data[i] == *v {
3295 3339
            set paramIdx = i;
3296 3340
            break;
3297 3341
        }
3298 3342
    }
3299 3343
3300 3344
    // For each predecessor, recursively look up the variable's reaching definition
3301 3345
    // in that block, then patch the predecessor's terminator to pass that value
3302 3346
    // as an argument to this block's parameter.
3303 -
    for predId in blk.preds {
3347 +
    for predId in vec::view⟨u32(&blk.preds) {
3304 3348
        let pred = BlockId(predId);
3305 3349
        // This may recursively trigger more block arg resolution if the
3306 3350
        // predecessor also needs to look up the variable from its predecessors.
3307 3351
        let val = try useVarInBlock(self, pred, v);
3308 3352
        assert val <> il::Val::Undef, "createBlockParam: predecessor provides undef value for block parameter";
3317 3361
    // Get the block parameter register.
3318 3362
    let paramReg = blk.vars[*v];
3319 3363
    // Check if all predecessors provide the same value.
3320 3364
    let mut sameVal: ?il::Val = nil;
3321 3365
3322 -
    for predId in blk.preds {
3366 +
    for predId in vec::view⟨u32(&blk.preds) {
3323 3367
        let pred = BlockId(predId);
3324 3368
        let val = try useVarInBlock(self, pred, v);
3325 3369
3326 3370
        // Check if this is a self-reference.
3327 3371
        // This happens in cycles where the loop back-edge passes the phi to
3356 3400
    let data = getBlockMut(self, from);
3357 3401
    let ix = data.instrs.len - 1; // The terminator is always the last instruction.
3358 3402
3359 3403
    // TODO: We shouldn't need to use a mutable subscript here, given that the
3360 3404
    // fields are already mutable.
3361 -
    match &mut data.instrs[ix] {
3405 +
    match &mut data.instrs.data[ix] {
3362 3406
        case il::Instr::Jmp { args, .. } => {
3363 3407
            set *args = growArgs(self, *args, paramIdx + 1);
3364 3408
            set args[paramIdx] = val;
3365 3409
        }
3366 3410
        case il::Instr::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => {
3466 3510
        } else {
3467 3511
            set name = try paramName(&astParams[i].value);
3468 3512
        }
3469 3513
        let v = newVar(self, name, type, false, il::Val::Undef);
3470 3514
3471 -
        self.params.append(FnParamBinding { var: v, reg }, self.allocator);
3515 +
        vec::append⟨FnParamBinding⟩(
3516 +
            &mut self.params,
3517 +
            FnParamBinding { var: v, reg },
3518 +
            self.allocator,
3519 +
        );
3472 3520
    }
3473 3521
    return params;
3474 3522
}
3475 3523
3476 3524
/// Resolve match subject.
3875 3923
    let entry = try createBlock(self, "entry");
3876 3924
    set self.entryBlock = entry;
3877 3925
    switchToBlock(self, entry);
3878 3926
3879 3927
    /// Bind parameter registers to variables in the entry block.
3880 -
    for def in self.params {
3928 +
    for def in vec::view⟨FnParamBinding⟩(&self.params) {
3881 3929
        defVar(self, def.var, il::Val::Reg(def.reg));
3882 3930
    }
3883 3931
    /// Lower function body.
3884 3932
    try lowerBlock(self, body);
3885 3933
3903 3951
}
3904 3952
3905 3953
/// Lower a scalar match as a switch instruction.
3906 3954
fn lowerMatchSwitch(self: *mut FnLowerer, prongs: *mut [*ast::Node], subject: *MatchSubject, mergeBlock: *mut ?BlockId) throws (LowerError) {
3907 3955
    let mut blocks: [BlockId; 32] = undefined;
3908 -
    let mut cases: *mut [il::SwitchCase] = &mut [];
3956 +
    let mut cases = vec::Vec⟨il::SwitchCase⟩ { data: &mut [], len: 0 };
3909 3957
    let mut defaultIdx: u32 = 0;
3910 3958
    let entry = currentBlock(self);
3911 3959
3912 3960
    for p, i in prongs {
3913 3961
        let case ast::NodeValue::MatchProng(prong) = p.value
3922 3970
                set blocks[i] = try createBlock(self, "case");
3923 3971
                for pat in pats {
3924 3972
                    let cv = resolver::constValueEntry(self.low.resolver, pat)
3925 3973
                        else throw LowerError::MissingConst(pat);
3926 3974
3927 -
                    cases.append(il::SwitchCase {
3975 +
                    vec::append⟨il::SwitchCase⟩(&mut cases, il::SwitchCase {
3928 3976
                        value: constToScalar(cv),
3929 3977
                        target: *blocks[i],
3930 3978
                        args: &mut []
3931 3979
                    }, self.allocator);
3932 3980
                }
3936 3984
    }
3937 3985
    emit(self, il::Instr::Switch {
3938 3986
        val: subject.val,
3939 3987
        defaultTarget: *blocks[defaultIdx],
3940 3988
        defaultArgs: &mut [],
3941 -
        cases: &mut cases[..]
3989 +
        cases: vec::viewMut⟨il::SwitchCase⟩(&mut cases),
3942 3990
    });
3943 3991
3944 3992
    for p, i in prongs {
3945 3993
        let case ast::NodeValue::MatchProng(prong) = p.value
3946 3994
            else throw LowerError::UnexpectedNodeValue(p);
4590 4638
    if let case resolver::Type::Pointer(_) = *inner {
4591 4639
        return il::Val::Imm(0);
4592 4640
    }
4593 4641
    if let case resolver::Type::Slice(slice) = *inner {
4594 4642
        return try buildSliceValue(
4595 -
            self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
4643 +
            self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0)
4596 4644
        );
4597 4645
    }
4598 4646
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4599 4647
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4600 4648
}
4611 4659
        successType, self.fnType.throwList
4612 4660
    );
4613 4661
    return try buildTagged(self, layout, tag, payload, payloadType, resolver::PTR_SIZE as i32, RESULT_VAL_OFFSET);
4614 4662
}
4615 4663
4616 -
/// Build a slice aggregate from a data pointer, length and capacity.
4664 +
/// Build a 16-byte slice aggregate from a data pointer and length.
4617 4665
fn buildSliceValue(
4618 4666
    self: *mut FnLowerer,
4619 4667
    elemTy: *resolver::Type,
4620 4668
    mutable: bool,
4621 4669
    ptrVal: il::Val,
4622 -
    lenVal: il::Val,
4623 -
    capVal: il::Val
4670 +
    lenVal: il::Val
4624 4671
) -> il::Val throws (LowerError) {
4625 4672
    let sliceType = resolver::Type::Slice(resolver::SliceType {
4626 4673
        class: types::PointerClass::Unsafe,
4627 4674
        item: elemTy,
4628 4675
        mutable,
4634 4681
        mutable,
4635 4682
    });
4636 4683
4637 4684
    try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal);
4638 4685
    try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal);
4639 -
    try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal);
4640 4686
4641 4687
    return il::Val::Reg(dst);
4642 4688
}
4643 4689
4644 4690
/// Build a trait object fat pointer from a data pointer and a v-table.
5051 5097
    offset: i32
5052 5098
) -> il::Val throws (LowerError) {
5053 5099
    let elemLayout = resolver::getTypeLayout(*arr.item);
5054 5100
    let stride = elemLayout.size as i32;
5055 5101
    let mut result: ?il::Val = nil;
5102 +
    let length = resolver::concreteArrayLength(arr.length)
5103 +
        else throw LowerError::MissingMetadata;
5056 5104
5057 -
    for i in 0..arr.length {
5105 +
    for i in 0..length {
5058 5106
        let elemOffset = offset + (i as i32) * stride;
5059 5107
        let cmp = try emitEqAtOffset(self, a, b, elemOffset, *arr.item);
5060 5108
        set result = emitLogicalAnd(self, result, cmp);
5061 5109
    }
5062 5110
    if let r = result {
5216 5264
    let typ = try typeOf(self, node);
5217 5265
    let case resolver::Type::Array(arrInfo) = typ else {
5218 5266
        throw LowerError::ExpectedArray;
5219 5267
    };
5220 5268
    let elemTy = *arrInfo.item;
5221 -
    let length = arrInfo.length;
5269 +
    let length = resolver::concreteArrayLength(arrInfo.length)
5270 +
        else throw LowerError::MissingMetadata;
5222 5271
    let elemLayout = resolver::getTypeLayout(elemTy);
5223 5272
    let dst = try emitReserve(self, typ);
5224 5273
5225 5274
    // Evaluate the repeated item once.
5226 5275
    let repeatVal = try lowerExpr(self, repeat.item);
5295 5344
    let itemType = try specializeType(self, *info.itemType);
5296 5345
5297 5346
    // Extract data pointer and container length.
5298 5347
    let mut dataReg = baseReg;
5299 5348
    let mut containerLen: il::Val = undefined;
5300 -
    if let cap = info.capacity { // Slice from array.
5301 -
        set containerLen = il::Val::Imm(cap as i64);
5349 +
    if let descriptor = info.capacity { // Slice from array.
5350 +
        let specialized = try specializeType(self, *descriptor);
5351 +
        let capacity = resolver::concreteArrayLength(&specialized)
5352 +
            else throw LowerError::MissingMetadata;
5353 +
        set containerLen = il::Val::Imm(capacity as i64);
5302 5354
    } else { // Slice from slice.
5303 5355
        set dataReg = loadSlicePtr(self, baseReg);
5304 5356
        set containerLen = loadSliceLen(self, baseReg);
5305 5357
    }
5306 5358
5361 5413
        throw LowerError::MissingMetadata;
5362 5414
    };
5363 5415
    let r = try resolveSliceRangePtr(self, container, range, info);
5364 5416
    let itemType = try specializeType(self, *info.itemType);
5365 5417
    return try buildSliceValue(
5366 -
        self, &itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count
5418 +
        self, &itemType, info.mutable, il::Val::Reg(r.dataReg), r.count
5367 5419
    );
5368 5420
}
5369 5421
5370 5422
/// Lower an address-of (`&x`) expression.
5371 5423
fn lowerAddressOf(self: *mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) {
5394 5446
            if isAggregateType(typ) {
5395 5447
                return val;
5396 5448
            }
5397 5449
            // For scalars, if we've already materialized a stack slot for this
5398 5450
            // variable, the SSA value is that slot pointer.
5399 -
            if self.vars[*v].addressTaken {
5451 +
            if self.vars.data[*v].addressTaken {
5400 5452
                // Already address-taken; return existing stack pointer.
5401 5453
                return val;
5402 5454
            }
5403 5455
            // Materialize a stack slot using the declaration's resolved
5404 5456
            // layout so `align(N)` on locals is honored.
5405 5457
            let layout = resolver::getLayout(self.low.resolver, addr.target, typ);
5406 5458
            let slot = emitReserveLayout(self, layout);
5407 5459
            try emitStore(self, slot, 0, typ, val);
5408 5460
            let stackVal = il::Val::Reg(slot);
5409 5461
5410 -
            set self.vars[*v].addressTaken = true;
5462 +
            set self.vars.data[*v].addressTaken = true;
5411 5463
            defVar(self, v, stackVal);
5412 5464
5413 5465
            return stackVal;
5414 5466
        }
5415 5467
        // Fall back to symbol lookup for constants/statics.
5447 5499
    };
5448 5500
    let arrayTy = try typeOf(self, arrayNode);
5449 5501
    let case resolver::Type::Array(arrayInfo) = arrayTy else {
5450 5502
        throw LowerError::ExpectedArray;
5451 5503
    };
5452 -
    let length = arrayInfo.length;
5504 +
    let length = resolver::concreteArrayLength(arrayInfo.length)
5505 +
        else throw LowerError::MissingMetadata;
5453 5506
    if length == 0 {
5454 5507
        return try buildSliceValue(
5455 -
            self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
5508 +
            self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0)
5456 5509
        );
5457 5510
    }
5458 5511
    if resolver::isConstExpr(self.low.resolver, arrayNode) {
5459 5512
        let mut b = dataBuilder(self.low.allocator);
5460 5513
        match arrayNode.value {
5471 5524
            slice.item, slice.mutable, length
5472 5525
        );
5473 5526
    }
5474 5527
    let data = try lowerExpr(self, arrayNode);
5475 5528
    let count = il::Val::Imm(length as i64);
5476 -
    return try buildSliceValue(self, slice.item, slice.mutable, data, count, count);
5529 +
    return try buildSliceValue(self, slice.item, slice.mutable, data, count);
5477 5530
}
5478 5531
5479 5532
/// Lower the common element pointer computation for subscript operations.
5480 5533
/// Handles both arrays and slices by resolving the container type, extracting
5481 5534
/// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute
5505 5558
            set elemType = *arrInfo.item;
5506 5559
            // Runtime safety check: index must be strictly less than array length.
5507 5560
            // Skip when the index is a compile-time constant, since we check
5508 5561
            // that in the resolver.
5509 5562
            if not resolver::isConstExpr(self.low.resolver, index) {
5510 -
                let arrLen = il::Val::Imm(arrInfo.length as i64);
5563 +
                let length = resolver::concreteArrayLength(arrInfo.length)
5564 +
                    else throw LowerError::MissingMetadata;
5565 +
                let arrLen = il::Val::Imm(length as i64);
5511 5566
                try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen);
5512 5567
            }
5513 5568
        }
5514 5569
        else => throw LowerError::ExpectedSliceOrArray,
5515 5570
    }
5582 5637
                let layout = resolver::getLayout(self.low.resolver, node, typ);
5583 5638
                let slot = emitReserveLayout(self, layout);
5584 5639
                try emitStore(self, slot, 0, typ, varVal);
5585 5640
5586 5641
                let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot));
5587 -
                set self.vars[*v].addressTaken = true;
5642 +
                set self.vars.data[*v].addressTaken = true;
5588 5643
5589 5644
                return;
5590 5645
            }
5591 5646
        }
5592 5647
    }
6036 6091
            let containerVal = try lowerExpr(self, f.iterable);
6037 6092
            let containerReg = emitValToReg(self, containerVal);
6038 6093
6039 6094
            let mut dataReg = containerReg;
6040 6095
            let mut lengthVal: il::Val = undefined;
6041 -
            if let len = length {
6096 +
            if let descriptor = length {
6097 +
                let specialized = try specializeType(self, *descriptor);
6098 +
                let len = resolver::concreteArrayLength(&specialized)
6099 +
                    else throw LowerError::MissingMetadata;
6042 6100
                set lengthVal = il::Val::Imm(len as i64);
6043 6101
            } else {
6044 6102
                set lengthVal = loadSliceLen(self, containerReg);
6045 6103
                set dataReg = loadSlicePtr(self, containerReg);
6046 6104
            }
6555 6613
6556 6614
/// Lower a builtin call expression.
6557 6615
fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6558 6616
    match kind {
6559 6617
        case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args),
6618 +
        case ast::Builtin::Relocate => return try lowerRelocate(self, args),
6560 6619
        case ast::Builtin::SizeOf, ast::Builtin::AlignOf => {
6561 -
            let constVal = resolver::constValueEntry(self.low.resolver, node) else {
6562 -
                throw LowerError::MissingConst(node);
6563 -
            };
6564 -
            return try constValueToVal(self, constVal, node);
6620 +
            if let constVal = resolver::constValueEntry(self.low.resolver, node) {
6621 +
                return try constValueToVal(self, constVal, node);
6622 +
            }
6623 +
            if args.len <> 1 {
6624 +
                throw LowerError::InvalidArgCount;
6625 +
            }
6626 +
            let ty = try typeOf(self, args[0]);
6627 +
            let layout = resolver::getTypeLayout(ty);
6628 +
            let mut value = layout.size;
6629 +
            if kind == ast::Builtin::AlignOf {
6630 +
                set value = layout.alignment;
6631 +
            }
6632 +
            return il::Val::Imm(value as i64);
6565 6633
        }
6566 6634
    }
6567 6635
}
6568 6636
6569 -
/// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call.
6637 +
/// Lower a two-argument `@sliceOf(ptr, len)` builtin call.
6570 6638
fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6571 -
    if args.len <> 2 and args.len <> 3 {
6639 +
    if args.len <> 2 {
6572 6640
        throw LowerError::InvalidArgCount;
6573 6641
    }
6574 6642
    let sliceTy = try typeOf(self, node);
6575 6643
    let case resolver::Type::Slice(slice) = sliceTy
6576 6644
        else throw LowerError::ExpectedSliceOrArray;
6577 6645
    let ptrVal = try lowerExpr(self, args[0]);
6578 6646
    let lenVal = try lowerExpr(self, args[1]);
6579 -
    let mut capVal = lenVal;
6580 -
    if args.len == 3 {
6581 -
        set capVal = try lowerExpr(self, args[2]);
6582 -
    }
6583 -
    if not isLtEq(lenVal, capVal) {
6584 -
        try emitTrapIfLt(self, il::Type::W32, capVal, lenVal);
6647 +
    return try buildSliceValue(self, slice.item, slice.mutable, ptrVal, lenVal);
6648 +
}
6649 +
6650 +
/// Lower `@relocate(destination, source)` as an overlap-safe byte move.
6651 +
fn lowerRelocate(
6652 +
    self: *mut FnLowerer,
6653 +
    args: *mut [*ast::Node],
6654 +
) -> il::Val throws (LowerError) {
6655 +
    if args.len <> 2 {
6656 +
        throw LowerError::InvalidArgCount;
6585 6657
    }
6586 -
    return try buildSliceValue(self, slice.item, slice.mutable, ptrVal, lenVal, capVal);
6658 +
6659 +
    let destinationSlice = emitValToReg(self, try lowerExpr(self, args[0]));
6660 +
    let destination = loadSlicePtr(self, destinationSlice);
6661 +
    let destinationLen = loadSliceLen(self, destinationSlice);
6662 +
    let sourceSlice = emitValToReg(self, try lowerExpr(self, args[1]));
6663 +
    let source = loadSlicePtr(self, sourceSlice);
6664 +
    let sourceLen = loadSliceLen(self, sourceSlice);
6665 +
6666 +
    // The destination must hold every source byte.
6667 +
    try emitTrapIfLt(self, il::Type::W32, destinationLen, sourceLen);
6668 +
6669 +
    let forwardBlock = try createBlock(self, "relocate.forward");
6670 +
    let backwardBlock = try createBlock(self, "relocate.backward");
6671 +
    let doneBlock = try createBlock(self, "relocate.done");
6672 +
    try emitBrCmp(
6673 +
        self,
6674 +
        il::CmpOp::Ult,
6675 +
        il::Type::W64,
6676 +
        il::Val::Reg(destination),
6677 +
        il::Val::Reg(source),
6678 +
        forwardBlock,
6679 +
        backwardBlock,
6680 +
    );
6681 +
6682 +
    // Moving toward a lower address cannot overwrite unread source bytes.
6683 +
    try switchToAndSeal(self, forwardBlock);
6684 +
    try emitByteCopyLoop(
6685 +
        self,
6686 +
        destination,
6687 +
        source,
6688 +
        sourceLen,
6689 +
        "relocate.forward",
6690 +
    );
6691 +
    try emitJmp(self, doneBlock);
6692 +
6693 +
    // Moving toward a higher address must copy from the end for overlap safety.
6694 +
    try switchToAndSeal(self, backwardBlock);
6695 +
    let remainingReg = nextReg(self);
6696 +
    let backwardHeader = try createBlockWithParam(
6697 +
        self,
6698 +
        "relocate.backward",
6699 +
        il::Param { value: remainingReg, type: il::Type::W32 },
6700 +
    );
6701 +
    let backwardBody = try createBlock(self, "relocate.backward");
6702 +
    let backwardDone = try createBlock(self, "relocate.backward");
6703 +
    try emitJmpWithArg(self, backwardHeader, sourceLen);
6704 +
6705 +
    switchToBlock(self, backwardHeader);
6706 +
    try emitBrCmp(
6707 +
        self,
6708 +
        il::CmpOp::Ult,
6709 +
        il::Type::W32,
6710 +
        il::Val::Imm(0),
6711 +
        il::Val::Reg(remainingReg),
6712 +
        backwardBody,
6713 +
        backwardDone,
6714 +
    );
6715 +
6716 +
    try switchToAndSeal(self, backwardBody);
6717 +
    let byteIndex = emitTypedBinOp(
6718 +
        self,
6719 +
        il::BinOp::Sub,
6720 +
        il::Type::W32,
6721 +
        il::Val::Reg(remainingReg),
6722 +
        il::Val::Imm(1),
6723 +
    );
6724 +
    let sourceByte = emitElem(self, 1, source, byteIndex);
6725 +
    let byteReg = nextReg(self);
6726 +
    emit(self, il::Instr::Load {
6727 +
        typ: il::Type::W8,
6728 +
        dst: byteReg,
6729 +
        src: sourceByte,
6730 +
        offset: 0,
6731 +
    });
6732 +
    let destinationByte = emitElem(self, 1, destination, byteIndex);
6733 +
    emit(self, il::Instr::Store {
6734 +
        typ: il::Type::W8,
6735 +
        src: il::Val::Reg(byteReg),
6736 +
        dst: destinationByte,
6737 +
        offset: 0,
6738 +
    });
6739 +
    try emitJmpWithArg(self, backwardHeader, byteIndex);
6740 +
6741 +
    try sealBlock(self, backwardHeader);
6742 +
    try switchToAndSeal(self, backwardDone);
6743 +
    try emitJmp(self, doneBlock);
6744 +
6745 +
    try switchToAndSeal(self, doneBlock);
6746 +
    return il::Val::Undef;
6587 6747
}
6588 6748
6589 6749
/// Lower a `try` expression.
6590 6750
fn lowerTry(self: *mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) {
6591 6751
    let case ast::NodeValue::Call(callExpr) = t.expr.value else {
6748 6908
    let entry = currentBlock(self);
6749 6909
6750 6910
    // First pass: create blocks, resolve error types, and build switch cases.
6751 6911
    let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined;
6752 6912
    let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined;
6753 -
    let mut cases: *mut [il::SwitchCase] = &mut [];
6913 +
    let mut cases = vec::Vec⟨il::SwitchCase⟩ { data: &mut [], len: 0 };
6754 6914
    let mut defaultIdx: ?u32 = nil;
6755 6915
6756 6916
    for clauseNode, i in catches {
6757 6917
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value
6758 6918
            else panic "lowerMultiCatch: expected CatchClause";
6762 6922
6763 6923
        if let typeNode = clause.typeNode {
6764 6924
            let errTy = try typeOf(self, typeNode);
6765 6925
            set errTypes[i] = errTy;
6766 6926
6767 -
            cases.append(il::SwitchCase {
6927 +
            vec::append⟨il::SwitchCase⟩(&mut cases, il::SwitchCase {
6768 6928
                value: getOrAssignErrorTag(self.low, errTy) as i64,
6769 6929
                target: *blocks[i],
6770 6930
                args: &mut []
6771 6931
            }, self.allocator);
6772 6932
        } else {
6785 6945
    }
6786 6946
    emit(self, il::Instr::Switch {
6787 6947
        val: il::Val::Reg(tagReg),
6788 6948
        defaultTarget: *defaultTarget,
6789 6949
        defaultArgs: &mut [],
6790 -
        cases
6950 +
        cases: vec::viewMut⟨il::SwitchCase⟩(&mut cases),
6791 6951
    });
6792 6952
6793 6953
    // Second pass: emit each catch clause body.
6794 6954
    for clauseNode, i in catches {
6795 6955
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value
6863 7023
    // Now all predecessors of header are known, seal it.
6864 7024
    try sealBlock(self, header);
6865 7025
    try switchToAndSeal(self, done);
6866 7026
}
6867 7027
6868 -
/// Lower `slice.append(val, allocator)`.
6869 -
///
6870 -
/// Emits inline grow-if-needed logic:
6871 -
///
6872 -
///     load len, cap from slice header
6873 -
///     if len < cap: jmp @store
6874 -
///     else:         jmp @grow
6875 -
///
6876 -
///     @grow:
6877 -
///       newCap = max(cap * 2, 1)
6878 -
///       call allocator.func(allocator.ctx, newCap * stride, alignment)
6879 -
///       copy old data to new pointer
6880 -
///       update slice ptr and cap
6881 -
///       jmp @store
6882 -
///
6883 -
///     @store:
6884 -
///       store element at ptr + len * stride
6885 -
///       increment len
6886 -
///
6887 -
fn lowerSliceAppend(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) {
6888 -
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6889 -
        else throw LowerError::MissingMetadata;
6890 -
6891 -
    // Get the address of the slice header.
6892 -
    let sliceVal = try lowerExpr(self, access.parent);
6893 -
    let sliceReg = emitValToReg(self, sliceVal);
6894 -
6895 -
    // Lower the value to append and the allocator.
6896 -
    let elemVal = try lowerExpr(self, call.args[0]);
6897 -
    let allocVal = try lowerExpr(self, call.args[1]);
6898 -
    let allocReg = emitValToReg(self, allocVal);
6899 -
6900 -
    let elemLayout = resolver::getTypeLayout(*elemType);
6901 -
    let stride = elemLayout.size;
6902 -
    let alignment = elemLayout.alignment;
6903 -
6904 -
    // Load current length and capacity.
6905 -
    let lenVal = loadSliceLen(self, sliceReg);
6906 -
    let capVal = loadSliceCap(self, sliceReg);
6907 -
6908 -
    // Branch: if length is smaller than capacity, go to @store else @grow.
6909 -
    let storeBlock = try createBlock(self, "append.store");
6910 -
    let growBlock = try createBlock(self, "append.grow");
6911 -
    try emitBrCmp(self, il::CmpOp::Ult, il::Type::W32, lenVal, capVal, storeBlock, growBlock);
6912 -
    try switchToAndSeal(self, growBlock);
6913 -
6914 -
    // -- @grow block ----------------------------------------------------------
6915 -
6916 -
    // `newCap = max(cap * 2, 1)`.
6917 -
    // We are only here when at capacity, so we use `or` with `1` to ensure at least capacity `1`.
6918 -
    let doubledVal = emitTypedBinOp(self, il::BinOp::Shl, il::Type::W32, capVal, il::Val::Imm(1));
6919 -
    let newCapVal = emitTypedBinOp(self, il::BinOp::Or, il::Type::W32, doubledVal, il::Val::Imm(1));
6920 -
6921 -
    // Call allocator: `a.func(a.ctx, newCap * stride, alignment)`.
6922 -
    let allocFnReg = nextReg(self);
6923 -
    emitLoadW64At(self, allocFnReg, allocReg, 0);
6924 -
6925 -
    let allocCtxReg = nextReg(self);
6926 -
    emitLoadW64At(self, allocCtxReg, allocReg, 8);
6927 -
6928 -
    let byteSize = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, newCapVal, il::Val::Imm(stride as i64));
6929 -
    let args = try allocVals(self, 3);
6930 -
6931 -
    set args[0] = il::Val::Reg(allocCtxReg);
6932 -
    set args[1] = byteSize;
6933 -
    set args[2] = il::Val::Imm(alignment as i64);
6934 -
6935 -
    let newPtrReg = nextReg(self);
6936 -
    emit(self, il::Instr::Call {
6937 -
        retTy: il::Type::W64,
6938 -
        dst: newPtrReg,
6939 -
        func: il::Val::Reg(allocFnReg),
6940 -
        args,
6941 -
    });
6942 -
6943 -
    // Copy old data byte-by-byte.
6944 -
    let oldPtrReg = loadSlicePtr(self, sliceReg);
6945 -
    let copyBytes = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, lenVal, il::Val::Imm(stride as i64));
6946 -
    try emitByteCopyLoop(self, newPtrReg, oldPtrReg, copyBytes, "append");
6947 -
6948 -
    // Update slice header.
6949 -
    emitStoreW64At(self, il::Val::Reg(newPtrReg), sliceReg, SLICE_PTR_OFFSET);
6950 -
    emitStoreW32At(self, newCapVal, sliceReg, SLICE_CAP_OFFSET);
6951 -
6952 -
    try emitJmp(self, storeBlock);
6953 -
    try switchToAndSeal(self, storeBlock);
6954 -
6955 -
    // -- @store block ---------------------------------------------------------
6956 -
6957 -
    // Store element at `ptr + len * stride`.
6958 -
    let ptrReg = loadSlicePtr(self, sliceReg);
6959 -
    let elemDst = emitElem(self, stride, ptrReg, lenVal);
6960 -
    try emitStore(self, elemDst, 0, *elemType, elemVal);
6961 -
6962 -
    // Increment len.
6963 -
    let newLen = emitTypedBinOp(self, il::BinOp::Add, il::Type::W32, lenVal, il::Val::Imm(1));
6964 -
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
6965 -
6966 -
    return il::Val::Reg(sliceReg);
6967 -
}
6968 -
6969 -
/// Lower `slice.delete(index)`.
6970 -
///
6971 -
/// Bounds-check the index, shift elements after it by one stride
6972 -
/// via a byte-copy loop, and decrement `len`.
6973 -
fn lowerSliceDelete(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) {
6974 -
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6975 -
        else throw LowerError::MissingMetadata;
6976 -
6977 -
    let elemLayout = resolver::getTypeLayout(*elemType);
6978 -
    let stride = elemLayout.size;
6979 -
6980 -
    // Get slice header address.
6981 -
    let sliceVal = try lowerExpr(self, access.parent);
6982 -
    let sliceReg = emitValToReg(self, sliceVal);
6983 -
6984 -
    // Lower the index argument.
6985 -
    let indexVal = try lowerExpr(self, call.args[0]);
6986 -
6987 -
    // Load len and bounds-check: index must be smaller than length.
6988 -
    let lenVal = loadSliceLen(self, sliceReg);
6989 -
    try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, lenVal);
6990 -
6991 -
    // Compute the destination and source for the shift.
6992 -
    let ptrReg = loadSlicePtr(self, sliceReg);
6993 -
    let dst = emitElem(self, stride, ptrReg, indexVal);
6994 -
6995 -
    // `src = dst + stride`.
6996 -
    let src = emitPtrOffset(self, dst, stride as i32);
6997 -
6998 -
    // Move `(len - index - 1) * stride`.
6999 -
    let tailLen = emitTypedBinOp(self, il::BinOp::Sub, il::Type::W32, lenVal, indexVal);
7000 -
    let tailLenMinusOne = emitTypedBinOp(self, il::BinOp::Sub, il::Type::W32, tailLen, il::Val::Imm(1));
7001 -
    let moveBytes = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, tailLenMinusOne, il::Val::Imm(stride as i64));
7002 -
7003 -
    // Shift elements left via byte-copy loop.
7004 -
    // When deleting the last element, the loop is a no-op.
7005 -
    try emitByteCopyLoop(self, dst, src, moveBytes, "delete");
7006 -
    // Decrement length.
7007 -
    let newLen = emitTypedBinOp(self, il::BinOp::Sub, il::Type::W32, lenVal, il::Val::Imm(1));
7008 -
7009 -
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
7010 -
}
7011 -
7012 7028
/// Lower a call expression, which may be a function call or type constructor.
7013 7029
fn lowerCallOrCtor(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
7014 7030
    let nodeData = resolver::nodeData(self.low.resolver, node).extra;
7015 7031
7016 -
    // Check for slice method dispatch.
7017 -
    if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData {
7018 -
        let concrete = try specializeType(self, *elemType);
7019 -
        return try lowerSliceAppend(self, call, &concrete);
7020 -
    }
7021 -
    if let case resolver::NodeExtra::SliceDelete { elemType } = nodeData {
7022 -
        let concrete = try specializeType(self, *elemType);
7023 -
        try lowerSliceDelete(self, call, &concrete);
7024 -
        return il::Val::Undef;
7025 -
    }
7026 7032
    // Check for trait method dispatch.
7027 7033
    if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData {
7028 7034
        return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex);
7029 7035
    }
7030 7036
    if let case resolver::NodeExtra::GenericBoundMethodCall {
7102 7108
        typ: il::Type::W64,
7103 7109
        dst: fnPtrReg,
7104 7110
        src: vtableReg,
7105 7111
        offset: slotOffset,
7106 7112
    });
7107 -
    let methodFnType = traitInfo.methods[methodIndex].fnType;
7113 +
    let methodFnType = traitInfo.methods.data[methodIndex].fnType;
7108 7114
7109 7115
    // Build args: optional return param slot + data pointer (receiver) + user args.
7110 7116
    let argOffset: u32 = 1 if requiresReturnParam(methodFnType) else 0;
7111 7117
    let args = try allocVals(self, call.args.len + 1 + argOffset);
7112 7118
    set args[argOffset] = il::Val::Reg(dataReg);
7246 7252
    }
7247 7253
    let concreteType = try specializeType(self, resolver::Type::Parameter(param));
7248 7254
    let inst = resolver::findInstance(
7249 7255
        self.low.resolver, traitInfo, concreteType
7250 7256
    ) else throw LowerError::MissingMetadata;
7251 -
    let method = &traitInfo.methods[methodIndex];
7257 +
    let method = &traitInfo.methods.data[methodIndex];
7252 7258
    let _ = resolver::findInstance(
7253 7259
        self.low.resolver, method.owner, concreteType
7254 7260
    ) else throw LowerError::MissingMetadata;
7255 7261
    let methodSym = inst.methods[methodIndex];
7256 7262
    let case resolver::SymbolData::Value {
7642 7648
            } else if let generic = try lowerGenericFnValue(self, node) {
7643 7649
                set val = generic;
7644 7650
            // First try local variable lookup, then global symbol lookup.
7645 7651
            } else if let v = lookupLocalVar(self, node) {
7646 7652
                set val = try useVar(self, v);
7647 -
                if self.vars[*v].addressTaken {
7653 +
                if self.vars.data[*v].addressTaken {
7648 7654
                    let typ = try typeOf(self, node);
7649 7655
                    let ptr = emitValToReg(self, val);
7650 7656
                    set val = emitRead(self, ptr, 0, typ);
7651 7657
                }
7652 7658
            } else {
7727 7733
                let mut handled = false;
7728 7734
                if let case resolver::Type::Array(array) = parentTy {
7729 7735
                    if let case ast::NodeValue::Ident(name) = access.child.value;
7730 7736
                       mem::eq(name, "len")
7731 7737
                    {
7732 -
                        set val = il::Val::Imm(array.length as i64);
7738 +
                        let length = resolver::concreteArrayLength(array.length)
7739 +
                            else throw LowerError::MissingMetadata;
7740 +
                        set val = il::Val::Imm(length as i64);
7733 7741
                        set handled = true;
7734 7742
                    }
7735 7743
                }
7736 7744
                if not handled {
7737 7745
                    set val = try lowerFieldAccess(self, access);
lib/std/lang/parser.rad +127 -87
2 2
@test export mod tests;
3 3
4 4
use std::mem;
5 5
use std::io;
6 6
use std::fmt;
7 +
use std::lang::vec;
7 8
use std::lang::alloc;
8 9
use std::lang::types;
9 10
use std::lang::ast;
10 11
use std::lang::strings;
11 12
use std::lang::scanner;
12 13
14 +
instantiate vec::append⟨*ast::Node⟩,
15 +
            vec::viewMut⟨*ast::Node⟩;
16 +
13 17
/// Maximum `u32` value.
14 18
export constant U32_MAX: u32 = 0xFFFFFFFF;
15 19
/// Minimum `i64` value.
16 20
export constant I64_MIN: i64 = -0x8000000000000000;
17 21
/// Largest magnitude representable by a negative `i64`.
276 280
        return node(p, ast::NodeValue::ArrayRepeatLit(
277 281
            ast::ArrayRepeatLit { item: firstExpr, count }
278 282
        ));
279 283
    }
280 284
    // Regular array literal: `[a, b, ...]`.
281 -
    let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator);
285 +
    let mut items = ast::nodeVec(p.arena, 64);
286 +
    vec::append⟨*ast::Node⟩(&mut items, firstExpr, p.allocator);
282 287
283 288
    while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) {
284 289
        let elem = try parseNormalExpr(p);
285 -
        items.append(elem, p.allocator);
290 +
        vec::append⟨*ast::Node⟩(&mut items, elem, p.allocator);
286 291
    }
287 292
    try expect(p, scanner::TokenKind::RBracket, "expected `]` after array elements");
288 293
289 -
    return node(p, ast::NodeValue::ArrayLit(items));
294 +
    return node(p, ast::NodeValue::ArrayLit(vec::viewMut⟨*ast::Node⟩(&mut items)));
290 295
}
291 296
292 297
/// Parse a function call expression.
293 298
fn parseCall(p: *mut Parser, callee: *ast::Node) -> *ast::Node
294 299
    throws (ParseError)
392 397
fn parseGenericArgs(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) {
393 398
    try expect(p, scanner::TokenKind::LAngle, "expected left angle before generic arguments");
394 399
    if check(p, scanner::TokenKind::RAngle) {
395 400
        throw failParsing(p, "generic argument list cannot be empty");
396 401
    }
397 -
    let mut args = ast::nodeSlice(p.arena, 4);
402 +
    let mut args = ast::nodeVec(p.arena, 4);
398 403
    loop {
399 -
        args.append(try parseGenericArg(p), p.allocator);
404 +
        vec::append⟨*ast::Node⟩(&mut args, try parseGenericArg(p), p.allocator);
400 405
        if not consume(p, scanner::TokenKind::Comma) {
401 406
            break;
402 407
        }
403 408
        if check(p, scanner::TokenKind::RAngle) {
404 409
            throw failParsing(p, "expected generic argument after `,`");
405 410
        }
406 411
    }
407 412
    try expect(p, scanner::TokenKind::RAngle, "expected right angle after generic arguments");
408 -
    return args;
413 +
    return vec::viewMut⟨*ast::Node⟩(&mut args);
409 414
}
410 415
411 416
/// Parse generic arguments following a target.
412 417
fn parseGenericApply(p: *mut Parser, target: *ast::Node) -> *ast::Node
413 418
    throws (ParseError)
762 767
{
763 768
    let leaf = try parseLeaf(p);
764 769
    return try parsePostfix(p, leaf);
765 770
}
766 771
767 -
/// Parse a builtin function call like `@sizeOf(T)` or `@alignOf(T)`.
772 +
/// Parse a builtin function call.
768 773
fn parseBuiltin(p: *mut Parser) -> *ast::Node
769 774
    throws (ParseError)
770 775
{
771 776
    // Skip the '@' to get the name.
772 777
    let ident = p.current.source;
778 783
        set kind = ast::Builtin::SizeOf;
779 784
    } else if ident == "@alignOf" {
780 785
        set kind = ast::Builtin::AlignOf;
781 786
    } else if ident == "@sliceOf" {
782 787
        set kind = ast::Builtin::SliceOf;
788 +
    } else if ident == "@relocate" {
789 +
        set kind = ast::Builtin::Relocate;
783 790
    } else {
784 791
        throw failParsing(p, "unknown builtin");
785 792
    }
786 793
    try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name");
787 794
788 795
    // Parse arguments into a list. Use capacity 4 to handle any valid argument count
789 796
    // plus some extra for error recovery.
790 -
    let mut args = ast::nodeSlice(p.arena, 4);
797 +
    let mut args = ast::nodeVec(p.arena, 4);
791 798
792 -
    if kind == ast::Builtin::SliceOf {
799 +
    if kind == ast::Builtin::SliceOf or kind == ast::Builtin::Relocate {
793 800
        // Parse comma-separated expressions until closing paren.
794 801
        // Argument count validation is done in semantic analysis.
795 802
        while not check(p, scanner::TokenKind::RParen) {
796 -
            args.append(try parseExpr(p), p.allocator);
803 +
            vec::append⟨*ast::Node⟩(&mut args, try parseExpr(p), p.allocator);
797 804
            if not consume(p, scanner::TokenKind::Comma) {
798 805
                break;
799 806
            }
800 807
        }
801 808
    } else {
802 -
        args.append(try parseType(p), p.allocator);
809 +
        vec::append⟨*ast::Node⟩(&mut args, try parseType(p), p.allocator);
803 810
    }
804 811
    try expect(p, scanner::TokenKind::RParen, "expected `)` after builtin argument");
805 812
806 -
    return node(p, ast::NodeValue::BuiltinCall { kind, args });
813 +
    return node(p, ast::NodeValue::BuiltinCall {
814 +
        kind, args: vec::viewMut⟨*ast::Node⟩(&mut args),
815 +
    });
807 816
}
808 817
809 818
/// Parse a single expression.
810 819
///
811 820
/// Parses unary and binary operators using precedence climbing.
871 880
    throw failParsing(p, "expected assignment after `set`");
872 881
}
873 882
874 883
/// Parse leading attributes and declaration modifiers.
875 884
fn parseAttributes(p: *mut Parser) -> ?ast::Attributes {
876 -
    let mut attrs = ast::nodeSlice(p.arena, 4);
885 +
    let mut attrs = ast::nodeVec(p.arena, 4);
877 886
878 887
    if let attr = tryParseAnnotation(p) {
879 -
        attrs.append(attr, p.allocator);
888 +
        vec::append⟨*ast::Node⟩(&mut attrs, attr, p.allocator);
880 889
    }
881 890
    if consume(p, scanner::TokenKind::Export) {
882 -
        attrs.append(nodeAttribute(p, ast::Attribute::Export), p.allocator);
891 +
        vec::append⟨*ast::Node⟩(&mut attrs, nodeAttribute(p, ast::Attribute::Export), p.allocator);
883 892
    }
884 893
    if consume(p, scanner::TokenKind::Unsafe) {
885 -
        attrs.append(nodeAttribute(p, ast::Attribute::Unsafe), p.allocator);
894 +
        vec::append⟨*ast::Node⟩(&mut attrs, nodeAttribute(p, ast::Attribute::Unsafe), p.allocator);
886 895
    }
887 896
    if attrs.len > 0 {
888 -
        return ast::Attributes { list: attrs };
897 +
        return ast::Attributes { list: vec::viewMut⟨*ast::Node⟩(&mut attrs) };
889 898
    }
890 899
    return nil;
891 900
}
892 901
893 902
/// Try to parse an annotation like `@default`.
1036 1045
    }
1037 1046
}
1038 1047
1039 1048
/// Parse statements until the specified ending token is encountered.
1040 1049
///
1041 -
/// Adds each parsed statement to the given block's statement list.
1042 -
export fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block)
1043 -
    throws (ParseError)
1044 -
{
1050 +
/// Adds each parsed statement to the given transient statement vector.
1051 +
export fn parseStmtsUntil(
1052 +
    p: *mut Parser,
1053 +
    end: scanner::TokenKind,
1054 +
    statements: *mut vec::Vec⟨*ast::Node⟩,
1055 +
) throws (ParseError) {
1045 1056
    while not check(p, end) {
1046 1057
        let stmt = try parseStmt(p);
1047 -
        blk.statements.append(stmt, p.allocator);
1058 +
        vec::append⟨*ast::Node⟩(statements, stmt, p.allocator);
1048 1059
1049 1060
        if check(p, end) or check(p, scanner::TokenKind::Eof) {
1050 1061
            break;
1051 1062
        }
1052 1063
        if not consume(p, scanner::TokenKind::Semicolon) {
1061 1072
/// Parse a block of statements enclosed in curly braces.
1062 1073
export fn parseBlock(p: *mut Parser) -> *ast::Node
1063 1074
    throws (ParseError)
1064 1075
{
1065 1076
    let start = p.current;
1066 -
    let mut blk = mkBlock(p, 64);
1077 +
    let mut statements = mkStatementVec(p, 64);
1067 1078
1068 1079
    if not consume(p, scanner::TokenKind::LBrace) {
1069 1080
        throw failParsing(p, "expected `{`");
1070 1081
    }
1071 -
    try parseStmtsUntil(p, scanner::TokenKind::RBrace, &mut blk);
1082 +
    try parseStmtsUntil(p, scanner::TokenKind::RBrace, &mut statements);
1072 1083
    try expect(p, scanner::TokenKind::RBrace, "expected `}`");
1073 1084
1085 +
    let blk = ast::Block { statements: vec::viewMut⟨*ast::Node⟩(&mut statements) };
1074 1086
    return node(p, ast::NodeValue::Block(blk));
1075 1087
}
1076 1088
1077 -
/// Create an empty block with no statements.
1078 -
fn mkBlock(p: *mut Parser, cap: u32) -> ast::Block {
1079 -
    return ast::Block { statements: ast::nodeSlice(p.arena, cap) };
1089 +
/// Create an empty transient statement vector with the requested capacity.
1090 +
fn mkStatementVec(p: *mut Parser, capacity: u32) -> vec::Vec⟨*ast::Node⟩ {
1091 +
    return ast::nodeVec(p.arena, capacity);
1080 1092
}
1081 1093
1082 1094
/// Create a block containing a single statement node.
1083 1095
fn mkBlockWith(p: *mut Parser, node: *ast::Node) -> ast::Block {
1084 -
    let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator);
1085 -
    return ast::Block { statements: stmts };
1096 +
    let mut statements = ast::nodeVec(p.arena, 1);
1097 +
    vec::append⟨*ast::Node⟩(&mut statements, node, p.allocator);
1098 +
    return ast::Block { statements: vec::viewMut⟨*ast::Node⟩(&mut statements) };
1086 1099
}
1087 1100
1088 1101
/// Parse the branch that follows `else` in let-else style constructs.
1089 1102
///
1090 1103
/// Allows either a block, a single statement like `return`,
1433 1446
    try expect(p, scanner::TokenKind::Try, "expected `try`");
1434 1447
1435 1448
    let shouldPanic = consume(p, scanner::TokenKind::Bang);
1436 1449
    let returnsOptional = consume(p, scanner::TokenKind::Question);
1437 1450
    let expr = try parseUnaryExpr(p);
1438 -
    let mut catches = ast::nodeSlice(p.arena, 4);
1451 +
    let mut catches = ast::nodeVec(p.arena, 4);
1439 1452
1440 1453
    while consume(p, scanner::TokenKind::Catch) {
1441 1454
        let mut binding: ?*ast::Node = nil;
1442 1455
        let mut typeNode: ?*ast::Node = nil;
1443 1456
1454 1467
        }
1455 1468
        let body = try parseBlock(p);
1456 1469
        let clause = node(p, ast::NodeValue::CatchClause(
1457 1470
            ast::CatchClause { binding, typeNode, body }
1458 1471
        ));
1459 -
        catches.append(clause, p.allocator);
1472 +
        vec::append⟨*ast::Node⟩(&mut catches, clause, p.allocator);
1460 1473
    }
1461 -
    return node(p, ast::NodeValue::Try(
1462 -
        ast::Try { expr, catches, shouldPanic, returnsOptional }
1463 -
    ));
1474 +
    return node(p, ast::NodeValue::Try(ast::Try {
1475 +
        expr,
1476 +
        catches: vec::viewMut⟨*ast::Node⟩(&mut catches),
1477 +
        shouldPanic,
1478 +
        returnsOptional,
1479 +
    }));
1464 1480
}
1465 1481
1466 1482
/// Parse an `if` expression, with optional `else` or `else if` clauses.
1467 1483
///
1468 1484
/// The `else if` construct is handled by creating a recursive structure:
1536 1552
    try expect(p, scanner::TokenKind::Match, "expected `match`");
1537 1553
1538 1554
    let subject = try parseCond(p);
1539 1555
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs");
1540 1556
1541 -
    let mut prongs = ast::nodeSlice(p.arena, 128);
1557 +
    let mut prongs = ast::nodeVec(p.arena, 128);
1542 1558
    while not check(p, scanner::TokenKind::RBrace) and
1543 1559
          not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF.
1544 1560
    {
1545 1561
        let prongNode = try parseMatchProng(p);
1546 -
        prongs.append(prongNode, p.allocator);
1562 +
        vec::append⟨*ast::Node⟩(&mut prongs, prongNode, p.allocator);
1547 1563
        consume(p, scanner::TokenKind::Comma);
1548 1564
    }
1549 1565
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after match prongs");
1550 1566
1551 -
    return node(p, ast::NodeValue::Match(
1552 -
        ast::Match { subject, prongs }
1553 -
    ));
1567 +
    return node(p, ast::NodeValue::Match(ast::Match {
1568 +
        subject, prongs: vec::viewMut⟨*ast::Node⟩(&mut prongs),
1569 +
    }));
1554 1570
}
1555 1571
1556 1572
/// Parse a single `match` prong.
1557 1573
fn parseMatchProng(p: *mut Parser) -> *ast::Node
1558 1574
    throws (ParseError)
1559 1575
{
1560 1576
    let mut guard: ?*ast::Node = nil;
1561 1577
1562 1578
    // Case prong: `case <pattern>, ... if <guard> => <body>`.
1563 1579
    if consume(p, scanner::TokenKind::Case) {
1564 -
        let mut patterns = ast::nodeSlice(p.arena, 16);
1580 +
        let mut patterns = ast::nodeVec(p.arena, 16);
1565 1581
        loop {
1566 1582
            let pattern = try parseMatchPattern(p);
1567 -
            patterns.append(pattern, p.allocator);
1583 +
            vec::append⟨*ast::Node⟩(&mut patterns, pattern, p.allocator);
1568 1584
1569 1585
            if not consume(p, scanner::TokenKind::Comma) {
1570 1586
                break;
1571 1587
            }
1572 1588
            // After a comma, check for tokens that start a new prong.
1579 1595
            set guard = try parseCond(p);
1580 1596
        }
1581 1597
        try expect(p, scanner::TokenKind::FatArrow, "expected `=>` after case pattern");
1582 1598
        let body = try parseStmt(p);
1583 1599
1584 -
        return node(p, ast::NodeValue::MatchProng(
1585 -
            ast::MatchProng { arm: ast::ProngArm::Case(patterns), guard, body }
1586 -
        ));
1600 +
        return node(p, ast::NodeValue::MatchProng(ast::MatchProng {
1601 +
            arm: ast::ProngArm::Case(vec::viewMut⟨*ast::Node⟩(&mut patterns)),
1602 +
            guard,
1603 +
            body,
1604 +
        }));
1587 1605
    }
1588 1606
    // Else prong: `else if <guard> => <body>`.
1589 1607
    if consume(p, scanner::TokenKind::Else) {
1590 1608
        if consume(p, scanner::TokenKind::If) {
1591 1609
            set guard = try parseCond(p);
1663 1681
) -> *mut [*ast::Node]
1664 1682
    throws (ParseError)
1665 1683
{
1666 1684
    let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled
1667 1685
        else scanner::TokenKind::RParen;
1668 -
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1686 +
    let mut fields = ast::nodeVec(p.arena, MAX_RECORD_FIELDS);
1669 1687
    while not check(p, terminator) {
1670 1688
        let mut recordField: ast::NodeValue = undefined;
1671 1689
        match mode {
1672 1690
            case RecordFieldMode::Labeled => {
1673 1691
                // Allow optional `let` keyword before field name.
1692 1710
                set recordField = ast::NodeValue::RecordField {
1693 1711
                    field: nil, type, value: nil,
1694 1712
                };
1695 1713
            }
1696 1714
        }
1697 -
        fields.append(node(p, recordField), p.allocator);
1715 +
        vec::append⟨*ast::Node⟩(&mut fields, node(p, recordField), p.allocator);
1698 1716
1699 1717
        if not consume(p, scanner::TokenKind::Comma) {
1700 1718
            break;
1701 1719
        }
1702 1720
    }
1703 1721
    try expect(p, terminator, "expected closing delimiter after record fields");
1704 1722
1705 -
    return fields;
1723 +
    return vec::viewMut⟨*ast::Node⟩(&mut fields);
1706 1724
}
1707 1725
1708 1726
/// Parse an optional generic parameter list.
1709 1727
fn parseGenericParams(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1710 -
    let mut params = ast::nodeSlice(p.arena, 4);
1728 +
    let mut params = ast::nodeVec(p.arena, 4);
1711 1729
    if not consume(p, scanner::TokenKind::LAngle) {
1712 -
        return params;
1730 +
        return vec::viewMut⟨*ast::Node⟩(&mut params);
1713 1731
    }
1714 1732
    if check(p, scanner::TokenKind::RAngle) {
1715 1733
        throw failParsing(p, "generic parameter list cannot be empty");
1716 1734
    }
1717 1735
    loop {
1731 1749
            let bounds = try parseDerives(p);
1732 1750
            set param = node(p, ast::NodeValue::GenericParam(
1733 1751
                ast::GenericParam::Type { name, bounds }
1734 1752
            ));
1735 1753
        }
1736 -
        params.append(param, p.allocator);
1754 +
        vec::append⟨*ast::Node⟩(&mut params, param, p.allocator);
1737 1755
        if not consume(p, scanner::TokenKind::Comma) {
1738 1756
            break;
1739 1757
        }
1740 1758
        if check(p, scanner::TokenKind::RAngle) {
1741 1759
            throw failParsing(p, "expected generic parameter after `,`");
1742 1760
        }
1743 1761
    }
1744 1762
    try expect(p, scanner::TokenKind::RAngle, "expected right angle after generic parameters");
1745 -
    return params;
1763 +
    return vec::viewMut⟨*ast::Node⟩(&mut params);
1746 1764
}
1747 1765
1748 1766
/// Parse an optional derives list (`: Trait + Trait`).
1749 1767
fn parseDerives(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1750 -
    let mut derives = ast::nodeSlice(p.arena, 4);
1768 +
    let mut derives = ast::nodeVec(p.arena, 4);
1751 1769
1752 1770
    if not consume(p, scanner::TokenKind::Colon) {
1753 -
        return derives;
1771 +
        return vec::viewMut⟨*ast::Node⟩(&mut derives);
1754 1772
    }
1755 1773
    loop {
1756 1774
        let t = try parseTypePath(p);
1757 -
        derives.append(t, p.allocator);
1775 +
        vec::append⟨*ast::Node⟩(&mut derives, t, p.allocator);
1758 1776
1759 1777
        if not consume(p, scanner::TokenKind::Plus) {
1760 1778
            break;
1761 1779
        }
1762 1780
    }
1763 -
    return derives;
1781 +
    return vec::viewMut⟨*ast::Node⟩(&mut derives);
1764 1782
}
1765 1783
1766 1784
/// Parse a single record literal field.
1767 1785
/// Can be either labeled, or shorthand.
1768 1786
fn parseRecordLitField(p: *mut Parser) -> *ast::Node
1786 1804
/// Eg. `{ x: 1, y: 2 }`
1787 1805
/// Eg. `{ x: 1, .. }`
1788 1806
fn parseRecordLit(p: *mut Parser, typeName: ?*ast::Node) -> *ast::Node
1789 1807
    throws (ParseError)
1790 1808
{
1791 -
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1809 +
    let mut fields = ast::nodeVec(p.arena, MAX_RECORD_FIELDS);
1792 1810
    let mut ignoreRest = false;
1793 1811
    try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal");
1794 1812
1795 1813
    while not check(p, scanner::TokenKind::RBrace) {
1796 1814
        // Check for `..` to ignore remaining fields.
1797 1815
        if consume(p, scanner::TokenKind::DotDot) {
1798 1816
            set ignoreRest = true;
1799 1817
            break;
1800 1818
        }
1801 1819
        let field = try parseRecordLitField(p);
1802 -
        fields.append(field, p.allocator);
1820 +
        vec::append⟨*ast::Node⟩(&mut fields, field, p.allocator);
1803 1821
1804 1822
        if not consume(p, scanner::TokenKind::Comma) {
1805 1823
            break;
1806 1824
        }
1807 1825
    }
1808 1826
    try expect(p, scanner::TokenKind::RBrace, "expected `}` to end record literal");
1809 1827
1810 -
    return node(p, ast::NodeValue::RecordLit(
1811 -
        ast::RecordLit { typeName, fields, ignoreRest }
1812 -
    ));
1828 +
    return node(p, ast::NodeValue::RecordLit(ast::RecordLit {
1829 +
        typeName,
1830 +
        fields: vec::viewMut⟨*ast::Node⟩(&mut fields),
1831 +
        ignoreRest,
1832 +
    }));
1813 1833
}
1814 1834
1815 1835
/// Parse a named record declaration.
1816 1836
/// `record Point { x: i32, y: i32 }`, or `record Pair(i32, i32);`
1817 1837
fn parseRecordDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1849 1869
    let params = try parseGenericParams(p);
1850 1870
    let derives = try parseDerives(p);
1851 1871
1852 1872
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body");
1853 1873
1854 -
    let mut variants = ast::nodeSlice(p.arena, 128);
1874 +
    let mut variants = ast::nodeVec(p.arena, 128);
1855 1875
    while not check(p, scanner::TokenKind::RBrace) {
1856 1876
        // Allow optional `case` keyword before variant name.
1857 1877
        consume(p, scanner::TokenKind::Case);
1858 1878
1859 1879
        let variantName = try parseIdent(p, "expected variant name");
1877 1897
        let variant = node(p, ast::NodeValue::UnionDeclVariant(
1878 1898
            ast::UnionDeclVariant {
1879 1899
                name: variantName, index: variants.len as u32, value: explicitValue, type: payloadType,
1880 1900
            }
1881 1901
        ));
1882 -
        variants.append(variant, p.allocator);
1902 +
        vec::append⟨*ast::Node⟩(&mut variants, variant, p.allocator);
1883 1903
1884 1904
        if not consume(p, scanner::TokenKind::Comma) {
1885 1905
            break;
1886 1906
        }
1887 1907
    }
1888 1908
    try expect(p, scanner::TokenKind::RBrace, "expected `}`");
1889 1909
1890 -
    return node(p, ast::NodeValue::UnionDecl(
1891 -
        ast::UnionDecl { name, params, variants, attrs, derives }
1892 -
    ));
1910 +
    return node(p, ast::NodeValue::UnionDecl(ast::UnionDecl {
1911 +
        name,
1912 +
        params,
1913 +
        variants: vec::viewMut⟨*ast::Node⟩(&mut variants),
1914 +
        attrs,
1915 +
        derives,
1916 +
    }));
1893 1917
}
1894 1918
1895 1919
/// Parse a function parameter.
1896 1920
fn parseFnParam(p: *mut Parser) -> *ast::Node
1897 1921
    throws (ParseError)
1908 1932
/// Parse an optional `throws` clause and return the collected type list.
1909 1933
fn parseThrowList(p: *mut Parser) -> *mut [*ast::Node]
1910 1934
    throws (ParseError)
1911 1935
{
1912 1936
    if not consume(p, scanner::TokenKind::Throws) {
1913 -
        return ast::nodeSlice(p.arena, 0);
1937 +
        return &mut [];
1914 1938
    }
1915 1939
    return try parseList(
1916 1940
        p,
1917 1941
        scanner::TokenKind::LParen,
1918 1942
        scanner::TokenKind::RParen,
1946 1970
/// Parse a function signature following the function name.
1947 1971
fn parseFnTypeSig(p: *mut Parser) -> ast::FnSig
1948 1972
    throws (ParseError)
1949 1973
{
1950 1974
    try expect(p, scanner::TokenKind::LParen, "expected `(` after function name");
1951 -
    let mut params = ast::nodeSlice(p.arena, 8);
1975 +
    let mut params = ast::nodeVec(p.arena, 8);
1952 1976
1953 1977
    while not check(p, scanner::TokenKind::RParen) {
1954 1978
        let param = try parseFnParam(p);
1955 -
        params.append(param, p.allocator);
1979 +
        vec::append⟨*ast::Node⟩(&mut params, param, p.allocator);
1956 1980
1957 1981
        if not consume(p, scanner::TokenKind::Comma) {
1958 1982
            break;
1959 1983
        }
1960 1984
    }
1964 1988
    if consume(p, scanner::TokenKind::Arrow) {
1965 1989
        set returnType = try parseType(p);
1966 1990
    }
1967 1991
    let throwList = try parseThrowList(p);
1968 1992
1969 -
    return ast::FnSig { params, returnType, throwList };
1993 +
    return ast::FnSig {
1994 +
        params: vec::viewMut⟨*ast::Node⟩(&mut params),
1995 +
        returnType,
1996 +
        throwList,
1997 +
    };
1970 1998
}
1971 1999
1972 2000
/// Parse a function declaration.
1973 2001
fn parseFnDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1974 2002
    throws (ParseError)
1987 2015
1988 2016
    if consume(p, scanner::TokenKind::Semicolon) {
1989 2017
        if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) {
1990 2018
            // Keep existing attributes unchanged.
1991 2019
        } else {
1992 -
            let mut list = ast::nodeSlice(p.arena, 4);
2020 +
            let mut list = ast::nodeVec(p.arena, 4);
1993 2021
            if let a = attrs {
1994 2022
                for i in 0..a.list.len {
1995 -
                    list.append(a.list[i], p.allocator);
2023 +
                    vec::append⟨*ast::Node⟩(&mut list, a.list[i], p.allocator);
1996 2024
                }
1997 2025
            }
1998 2026
            let attrNode = nodeAttribute(p, ast::Attribute::Extern);
1999 -
            list.append(attrNode, p.allocator);
2000 -
            set fnAttrs = ast::Attributes { list };
2027 +
            vec::append⟨*ast::Node⟩(&mut list, attrNode, p.allocator);
2028 +
            set fnAttrs = ast::Attributes { list: vec::viewMut⟨*ast::Node⟩(&mut list) };
2001 2029
        }
2002 2030
    } else {
2003 2031
        set body = try parseBlock(p);
2004 2032
    }
2005 2033
    return node(p, ast::NodeValue::FnDecl(
2343 2371
export fn parseModule(p: *mut Parser) -> *mut ast::Node
2344 2372
    throws (ParseError)
2345 2373
{
2346 2374
    advance(p); // Set the parser up with a first token.
2347 2375
2348 -
    let mut blk = mkBlock(p, 512);
2349 -
    try parseStmtsUntil(p, scanner::TokenKind::Eof, &mut blk);
2376 +
    let mut statements = mkStatementVec(p, 512);
2377 +
    try parseStmtsUntil(p, scanner::TokenKind::Eof, &mut statements);
2350 2378
    consume(p, scanner::TokenKind::Eof);
2351 2379
2380 +
    let blk = ast::Block { statements: vec::viewMut⟨*ast::Node⟩(&mut statements) };
2352 2381
    return node(p, ast::NodeValue::Block(blk));
2353 2382
}
2354 2383
2355 2384
/// Consume a token of the given kind if present.
2356 2385
export fn consume(p: *mut Parser, kind: scanner::TokenKind) -> bool {
2386 2415
}
2387 2416
2388 2417
/// Parse one or more explicit specialization roots.
2389 2418
fn parseInstantiate(p: *mut Parser) -> *ast::Node throws (ParseError) {
2390 2419
    try expect(p, scanner::TokenKind::Instantiate, "expected `instantiate`");
2391 -
    let mut applications = ast::nodeSlice(p.arena, 4);
2420 +
    let mut applications = ast::nodeVec(p.arena, 4);
2392 2421
    loop {
2393 2422
        let target = try parseTypePath(p);
2394 2423
        if not check(p, scanner::TokenKind::LAngle) {
2395 2424
            throw failParsing(p, "`instantiate` requires a generic application");
2396 2425
        }
2397 -
        applications.append(try parseGenericApply(p, target), p.allocator);
2426 +
        vec::append⟨*ast::Node⟩(&mut applications, try parseGenericApply(p, target), p.allocator);
2398 2427
        if not consume(p, scanner::TokenKind::Comma) {
2399 2428
            break;
2400 2429
        }
2401 2430
    }
2402 -
    return node(p, ast::NodeValue::Instantiate(applications));
2431 +
    return node(p, ast::NodeValue::Instantiate(
2432 +
        vec::viewMut⟨*ast::Node⟩(&mut applications)
2433 +
    ));
2403 2434
}
2404 2435
2405 2436
/// Parse a trait declaration.
2406 2437
/// Syntax: `trait Name { fn (*Trait) method(...) -> T; ... }`
2407 2438
fn parseTraitDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2410 2441
    try expect(p, scanner::TokenKind::Trait, "expected `trait`");
2411 2442
    let name = try parseIdent(p, "expected trait name");
2412 2443
    let supertraits = try parseDerives(p);
2413 2444
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name");
2414 2445
2415 -
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2446 +
    let mut methods = ast::nodeVec(p.arena, ast::MAX_TRAIT_METHODS);
2416 2447
    while not check(p, scanner::TokenKind::RBrace) and
2417 2448
          not check(p, scanner::TokenKind::Eof)
2418 2449
    {
2419 2450
        let method = try parseTraitMethodSig(p);
2420 -
        methods.append(method, p.allocator);
2451 +
        vec::append⟨*ast::Node⟩(&mut methods, method, p.allocator);
2421 2452
    }
2422 2453
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after trait methods");
2423 2454
2424 -
    return node(p, ast::NodeValue::TraitDecl { name, supertraits, methods, attrs });
2455 +
    return node(p, ast::NodeValue::TraitDecl {
2456 +
        name,
2457 +
        supertraits,
2458 +
        methods: vec::viewMut⟨*ast::Node⟩(&mut methods),
2459 +
        attrs,
2460 +
    });
2425 2461
}
2426 2462
2427 2463
/// Parse a trait method signature.
2428 2464
/// Syntax: `fn (*Trait) fnord(<params>) -> ReturnType;`
2429 2465
fn parseTraitMethodSig(p: *mut Parser) -> *ast::Node
2456 2492
    let traitName = try parseTypePath(p);
2457 2493
    try expect(p, scanner::TokenKind::For, "expected `for` after trait name");
2458 2494
    let targetType = try parseType(p);
2459 2495
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type");
2460 2496
2461 -
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2497 +
    let mut methods = ast::nodeVec(p.arena, ast::MAX_TRAIT_METHODS);
2462 2498
2463 2499
    while not check(p, scanner::TokenKind::RBrace) and
2464 2500
          not check(p, scanner::TokenKind::Eof)
2465 2501
    {
2466 2502
        let attrs = parseAttributes(p);
2467 2503
        try expect(p, scanner::TokenKind::Fn, "expected `fn`");
2468 2504
        let method = try parseMethodDecl(p, attrs);
2469 2505
2470 -
        methods.append(method, p.allocator);
2506 +
        vec::append⟨*ast::Node⟩(&mut methods, method, p.allocator);
2471 2507
    }
2472 2508
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods");
2473 2509
2474 -
    return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods });
2510 +
    return node(p, ast::NodeValue::InstanceDecl {
2511 +
        traitName,
2512 +
        targetType,
2513 +
        methods: vec::viewMut⟨*ast::Node⟩(&mut methods),
2514 +
    });
2475 2515
}
2476 2516
2477 2517
/// Parse a method declaration with a receiver.
2478 2518
/// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }`
2479 2519
///
2505 2545
    open: scanner::TokenKind,
2506 2546
    close: scanner::TokenKind,
2507 2547
    parseItem: fn (*mut Parser) -> *ast::Node throws (ParseError)
2508 2548
) -> *mut [*ast::Node] throws (ParseError) {
2509 2549
    try expect(p, open, listExpectMessage(open));
2510 -
    let mut items = ast::nodeSlice(p.arena, 8);
2550 +
    let mut items = ast::nodeVec(p.arena, 8);
2511 2551
2512 2552
    while not check(p, close) {
2513 2553
        let item = try parseItem(p);
2514 -
        items.append(item, p.allocator);
2554 +
        vec::append⟨*ast::Node⟩(&mut items, item, p.allocator);
2515 2555
2516 2556
        if not consume(p, scanner::TokenKind::Comma) {
2517 2557
            break;
2518 2558
        }
2519 2559
    }
2520 2560
    try expect(p, close, listExpectMessage(close));
2521 2561
2522 -
    return items;
2562 +
    return vec::viewMut⟨*ast::Node⟩(&mut items);
2523 2563
}
lib/std/lang/resolver.rad +394 -359
13 13
// TODO: `ensureNominalResolved` should just run when you call `typeFor`.
14 14
// TODO: Have different types for positional vs. named field records.
15 15
16 16
use std::mem;
17 17
use std::io;
18 +
use std::lang::vec;
18 19
use std::lang::alloc;
19 20
use std::lang::types;
20 21
use std::lang::ast;
21 22
use std::lang::parser;
22 23
use std::lang::module;
76 77
    name: *[u8],
77 78
    /// Module-local identity used by semantic tables.
78 79
    moduleId: u16,
79 80
    nodeId: u32,
80 81
    /// Method signatures, including from supertraits.
81 -
    methods: *mut [TraitMethod],
82 +
    methods: vec::Vec⟨TraitMethod⟩,
82 83
    /// Supertraits that must also be implemented.
83 -
    supertraits: *mut [*TraitType],
84 +
    supertraits: vec::Vec⟨*TraitType⟩,
84 85
    /// Rigid `Self` type used by static signatures.
85 86
    selfType: *GenericParamType,
86 87
    /// Whether signature resolution has started or completed.
87 88
    state: TraitState,
88 89
    /// Whether every vtable-exposed method is object-safe.
135 136
136 137
/// Identifier for the synthetic `len` field.
137 138
export constant LEN_FIELD: *[u8] = "len";
138 139
/// Identifier for the synthetic `ptr` field.
139 140
export constant PTR_FIELD: *[u8] = "ptr";
140 -
/// Identifier for the synthetic `cap` field.
141 -
export constant CAP_FIELD: *[u8] = "cap";
142 141
143 142
/// Maximum `u16` value.
144 143
constant U16_MAX: u16 = 0xFFFF;
145 144
/// Maximum `u8` value.
146 145
constant U8_MAX: u16 = 0xFF;
183 182
    symbol: *mut Symbol,
184 183
}
185 184
186 185
/// Array type payload.
187 186
export record ArrayType {
187 +
    /// Element type.
188 188
    item: *Type,
189 -
    length: u32,
189 +
    /// Typed constant descriptor for the array length.
190 +
    length: *Type,
190 191
}
191 192
192 193
/// Anonymous record whose field layout depends on rigid parameters.
193 194
export record GenericRecordType {
194 195
    /// Fields in declaration order.
276 277
export record SliceRangeInfo {
277 278
    /// Element type of the resulting slice.
278 279
    itemType: *Type,
279 280
    /// Whether the resulting slice is mutable.
280 281
    mutable: bool,
281 -
    /// Static capacity if container is an array.
282 -
    capacity: ?u32,
282 +
    /// Typed array length descriptor, or `nil` when slicing a slice.
283 +
    capacity: ?*Type,
283 284
}
284 285
285 286
/// Pre-computed metadata for `for` loop iteration.
286 287
/// Used by the lowerer to avoid re-analyzing the iterable type.
287 288
export union ForLoopInfo {
290 291
        valType: *Type,
291 292
        range: ast::Range,
292 293
        bindingName: ?*[u8],
293 294
        indexName: ?*[u8]
294 295
    },
295 -
    /// Iterating over an array or slice. For arrays, the length field is set.
296 +
    /// Iterating over an array or slice.
296 297
    Collection {
298 +
        /// Element type.
297 299
        elemType: *Type,
298 -
        length: ?u32,
300 +
        /// Typed array length descriptor, or `nil` for a slice.
301 +
        length: ?*Type,
302 +
        /// Optional element binding name.
299 303
        bindingName: ?*[u8],
304 +
        /// Optional index binding name.
300 305
        indexName: ?*[u8]
301 306
    },
302 307
}
303 308
304 309
/// A rigid type parameter belonging to one generic declaration.
342 347
    /// Whether the declaration explicitly carries the `Linear` marker.
343 348
    declaredLinear: bool,
344 349
    /// Whether a generic function body has already been checked.
345 350
    bodyResolved: bool,
346 351
    /// Symbolic body types consumed by lowering, recorded during body analysis.
347 -
    typeUses: *mut [GenericTypeUse],
352 +
    typeUses: vec::Vec⟨GenericTypeUse⟩,
348 353
    /// Next generic template.
349 354
    next: ?*mut GenericTemplate,
350 355
}
351 356
352 357
/// Canonical concrete specialization of a generic record or union.
516 521
    Pointer(PointerType),
517 522
    /// Pointer-like slice.
518 523
    Slice(SliceType),
519 524
    /// Eg. `[i32; 32]`.
520 525
    Array(ArrayType),
521 -
    /// Array type whose length depends on a rigid constant parameter.
522 -
    GenericArray {
523 -
        item: *Type,
524 -
        length: *ast::Node,
525 -
    },
526 526
    /// Rigid integer constant parameter within a generic declaration.
527 527
    ConstParameter(*GenericParamType),
528 528
    /// Canonical typed integer generic argument.
529 529
    ConstArgument {
530 530
        type: *Type,
549 549
    GenericDataApply(*GenericDataApplyType),
550 550
    /// An erased pointer-like type with a v-table.
551 551
    TraitObject(TraitObjectType),
552 552
}
553 553
554 +
/// Return a concrete `u32` array length from its typed descriptor.
555 +
export fn concreteArrayLength(length: *Type) -> ?u32 {
556 +
    let case Type::ConstArgument { type, value } = *length else return nil;
557 +
    if *type <> Type::U32 or value.negative
558 +
        or value.magnitude > parser::U32_MAX as u64
559 +
    {
560 +
        return nil;
561 +
    }
562 +
    return value.magnitude as u32;
563 +
}
564 +
554 565
/// Structured diagnostic payload for type mismatches.
555 566
export record TypeMismatch {
556 567
    expected: Type,
557 568
    actual: Type,
558 569
}
910 921
    GenericSpecializationLimit,
911 922
}
912 923
913 924
/// Diagnostics returned by the analyzer.
914 925
export record Diagnostics {
926 +
    /// Recorded errors in analysis order.
915 927
    errors: *mut [Error],
916 928
}
917 929
918 930
/// Call context.
919 931
union CallCtx {
968 980
        /// Whether the receiver is the first explicit call argument.
969 981
        explicitReceiver: bool,
970 982
    },
971 983
    /// Standalone method call metadata.
972 984
    MethodCall { method: *MethodEntry },
973 -
    /// Slice `.append(val, allocator)` method call.
974 -
    SliceAppend { elemType: *Type },
975 -
    /// Slice `.delete(index)` method call.
976 -
    SliceDelete { elemType: *Type },
977 985
    /// Concrete specialization selected by an explicit generic function value.
978 986
    GenericFnCall(*GenericFnSpecialization),
979 987
    /// Symbolic generic call resolved under the caller's specialization.
980 988
    GenericFnDependency(*GenericFnDependency),
981 989
}
1165 1173
    /// Combined semantic metadata table indexed by node ID.
1166 1174
    nodeData: NodeDataTable,
1167 1175
    /// Linked list of interned types.
1168 1176
    types: ?*TypeNode,
1169 1177
    /// Diagnostics recorded so far.
1170 -
    errors: *mut [Error],
1178 +
    errors: vec::Vec⟨Error⟩,
1171 1179
    /// Module graph for the current package.
1172 1180
    moduleGraph: *module::ModuleGraph,
1173 1181
    /// Cache of module scopes indexed by module ID.
1174 1182
    moduleScopes: [?*mut Scope; module::MAX_MODULES],
1175 1183
    /// Trait instance registry.
1205 1213
record TypeNode {
1206 1214
    ty: Type,
1207 1215
    next: ?*TypeNode,
1208 1216
}
1209 1217
1218 +
instantiate vec::append⟨*Type⟩,
1219 +
            vec::view⟨*Type⟩;
1220 +
1221 +
instantiate vec::append⟨RecordField⟩,
1222 +
            vec::view⟨RecordField⟩;
1223 +
1224 +
instantiate vec::append⟨UnionVariant⟩,
1225 +
            vec::view⟨UnionVariant⟩;
1226 +
1227 +
instantiate vec::append⟨*GenericParamType⟩,
1228 +
            vec::view⟨*GenericParamType⟩;
1229 +
1230 +
instantiate vec::append⟨TraitMethod⟩,
1231 +
            vec::view⟨TraitMethod⟩;
1232 +
1233 +
instantiate vec::append⟨*TraitType⟩,
1234 +
            vec::view⟨*TraitType⟩;
1235 +
1236 +
instantiate vec::append⟨GenericTypeUse⟩,
1237 +
            vec::view⟨GenericTypeUse⟩;
1238 +
1239 +
instantiate vec::push⟨Error⟩,
1240 +
            vec::viewMut⟨Error⟩;
1241 +
1210 1242
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
1211 1243
export fn allocType(self: *mut Resolver, ty: Type) -> *Type {
1212 1244
    // Search existing types for a match.
1213 1245
    let mut cursor = self.types;
1214 1246
    while let node = cursor {
1226 1258
    set self.types = node;
1227 1259
1228 1260
    return &node.ty;
1229 1261
}
1230 1262
1263 +
/// Allocate the canonical typed descriptor for a concrete array length.
1264 +
fn allocConcreteArrayLength(self: *mut Resolver, length: u32) -> *Type {
1265 +
    return allocType(self, Type::ConstArgument {
1266 +
        type: allocType(self, Type::U32),
1267 +
        value: ConstInt {
1268 +
            magnitude: length as u64,
1269 +
            bits: 32,
1270 +
            signed: false,
1271 +
            negative: false,
1272 +
        },
1273 +
    });
1274 +
}
1275 +
1276 +
/// Allocate a symbolic array length descriptor for a generic constant expression.
1277 +
fn allocSymbolicArrayLength(self: *mut Resolver, expression: *ast::Node) -> *Type {
1278 +
    if let sym = symbolFor(self, expression) {
1279 +
        if let case SymbolData::ConstParameter(param) = sym.data {
1280 +
            if let declaredType = param.constType; *declaredType == Type::U32 {
1281 +
                return allocType(self, Type::ConstParameter(param));
1282 +
            }
1283 +
        }
1284 +
    }
1285 +
    return allocType(self, Type::GenericConstExpr {
1286 +
        type: allocType(self, Type::U32),
1287 +
        expr: expression,
1288 +
    });
1289 +
}
1290 +
1231 1291
/// Return whether a type contains a rigid generic parameter.
1232 1292
export fn containsGenericParameter(ty: Type) -> bool {
1233 1293
    match ty {
1234 1294
        case Type::Pointer(pointer) =>
1235 1295
            return containsGenericParameter(*pointer.target),
1236 1296
        case Type::Slice(slice) =>
1237 1297
            return containsGenericParameter(*slice.item),
1238 1298
        case Type::Parameter(_), Type::ConstParameter(_),
1239 1299
             Type::GenericConstExpr { .. } => return true,
1240 -
        case Type::Array(array) => return containsGenericParameter(*array.item),
1241 -
        case Type::GenericArray { .. } => return true,
1300 +
        case Type::Array(array) =>
1301 +
            return containsGenericParameter(*array.item)
1302 +
                or containsGenericParameter(*array.length),
1242 1303
        case Type::Optional(inner) => return containsGenericParameter(*inner),
1243 1304
        // Symbolic anonymous records require materialization even when their
1244 1305
        // own fields happen not to mention a rigid parameter.
1245 1306
        case Type::GenericRecord(_) => return true,
1246 1307
        case Type::GenericDataApply(_) => return true,
1325 1386
                mutable: slice.mutable,
1326 1387
            });
1327 1388
        }
1328 1389
        case Type::Array(array) => {
1329 1390
            let item = try materializeConcreteGenericData(self, *array.item, site);
1391 +
            let length = try materializeConcreteGenericData(
1392 +
                self, *array.length, site
1393 +
            );
1330 1394
            return Type::Array(ArrayType {
1331 1395
                item: allocType(self, item),
1332 -
                length: array.length,
1396 +
                length: allocType(self, length),
1333 1397
            });
1334 1398
        }
1335 -
        case Type::GenericArray { item, length } => {
1336 -
            let inner = try materializeConcreteGenericData(self, *item, site);
1337 -
            return Type::GenericArray { item: allocType(self, inner), length };
1338 -
        }
1339 1399
        case Type::Optional(inner) => {
1340 1400
            let value = try materializeConcreteGenericData(self, *inner, site);
1341 1401
            return Type::Optional(allocType(self, value));
1342 1402
        }
1343 1403
        case Type::GenericDataApply(app) => {
1344 1404
            let a = alloc::arenaAllocator(&mut self.arena);
1345 -
            let mut args: *mut [*Type] = &mut [];
1405 +
            let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1346 1406
            let mut concrete = true;
1347 1407
            for arg in app.args {
1348 1408
                let value = try materializeConcreteGenericData(self, *arg, site);
1349 1409
                set concrete = concrete and not containsGenericParameter(value);
1350 -
                args.append(allocType(self, value), a);
1410 +
                vec::append⟨*Type⟩(&mut args, allocType(self, value), a);
1351 1411
            }
1412 +
            let argsView = vec::view⟨*Type⟩(&args);
1352 1413
            if concrete {
1353 1414
                let nominal = try specializeGenericData(
1354 -
                    self, app.site, app.template, &args[..], false
1415 +
                    self, app.site, app.template, argsView, false
1355 1416
                );
1356 1417
                return Type::Nominal(nominal);
1357 1418
            }
1358 1419
            let application = try! alloc::alloc(
1359 1420
                &mut self.arena,
1360 1421
                @sizeOf(GenericDataApplyType),
1361 1422
                @alignOf(GenericDataApplyType),
1362 1423
            ) as *mut GenericDataApplyType;
1363 1424
            set *application = GenericDataApplyType {
1364 1425
                template: app.template,
1365 -
                args: &args[..],
1426 +
                args: argsView,
1366 1427
                site: app.site,
1367 1428
            };
1368 1429
            return Type::GenericDataApply(application);
1369 1430
        }
1370 1431
        case Type::Fn(info) => {
1371 1432
            let a = alloc::arenaAllocator(&mut self.arena);
1372 -
            let mut params: *mut [*Type] = &mut [];
1373 -
            let mut throwTypes: *mut [*Type] = &mut [];
1433 +
            let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1434 +
            let mut throwTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1374 1435
            for param in info.paramTypes {
1375 1436
                let value = try materializeConcreteGenericData(self, *param, site);
1376 -
                params.append(allocType(self, value), a);
1437 +
                vec::append⟨*Type⟩(&mut params, allocType(self, value), a);
1377 1438
            }
1378 1439
            for thrown in info.throwList {
1379 1440
                let value = try materializeConcreteGenericData(self, *thrown, site);
1380 -
                throwTypes.append(allocType(self, value), a);
1441 +
                vec::append⟨*Type⟩(&mut throwTypes, allocType(self, value), a);
1381 1442
            }
1382 1443
            let result = try materializeConcreteGenericData(
1383 1444
                self, *info.returnType, site
1384 1445
            );
1385 1446
            return Type::Fn(allocFnType(self, FnType {
1386 -
                paramTypes: &params[..],
1447 +
                paramTypes: vec::view⟨*Type⟩(&params),
1387 1448
                returnType: allocType(self, result),
1388 -
                throwList: &throwTypes[..],
1449 +
                throwList: vec::view⟨*Type⟩(&throwTypes),
1389 1450
                isUnsafe: info.isUnsafe,
1390 1451
                localCount: info.localCount,
1391 1452
            }));
1392 1453
        }
1393 1454
        case Type::GenericRecord(rec) => {
1394 1455
            let a = alloc::arenaAllocator(&mut self.arena);
1395 -
            let mut fields: *mut [RecordField] = &mut [];
1456 +
            let mut fields = vec::Vec⟨RecordField⟩ { data: &mut [], len: 0 };
1396 1457
            let mut symbolic = false;
1397 1458
            for field in rec.fields {
1398 1459
                let fieldType = try materializeConcreteGenericData(
1399 1460
                    self, field.fieldType, site
1400 1461
                );
1401 1462
                set symbolic = symbolic or containsGenericParameter(fieldType);
1402 -
                fields.append(RecordField {
1463 +
                vec::append⟨RecordField⟩(&mut fields, RecordField {
1403 1464
                    name: field.name,
1404 1465
                    fieldType,
1405 1466
                    offset: field.offset,
1406 1467
                }, a);
1407 1468
            }
1409 1470
                &mut self.arena,
1410 1471
                @sizeOf(GenericRecordType),
1411 1472
                @alignOf(GenericRecordType),
1412 1473
            ) as *mut GenericRecordType;
1413 1474
            set *updatedRec = GenericRecordType {
1414 -
                fields: &fields[..],
1475 +
                fields: vec::view⟨RecordField⟩(&fields),
1415 1476
                labeled: rec.labeled,
1416 1477
            };
1417 1478
            let updated = Type::GenericRecord(updatedRec);
1418 1479
            if symbolic {
1419 1480
                return updated;
1517 1578
                mutable: slice.mutable,
1518 1579
            });
1519 1580
        }
1520 1581
        case Type::Array(array) => {
1521 1582
            let item = copyStructuralTypeToArena(*array.item, arena);
1583 +
            let length = copyStructuralTypeToArena(*array.length, arena);
1522 1584
            return Type::Array(ArrayType {
1523 1585
                item: allocArenaType(arena, item),
1524 -
                length: array.length,
1586 +
                length: allocArenaType(arena, length),
1525 1587
            });
1526 1588
        }
1527 1589
        case Type::Optional(inner) => {
1528 1590
            let stored = copyStructuralTypeToArena(*inner, arena);
1529 1591
            return Type::Optional(allocArenaType(arena, stored));
1530 1592
        }
1531 1593
        case Type::Fn(info) => {
1532 1594
            let a = alloc::arenaAllocator(arena);
1533 -
            let mut params: *mut [*Type] = &mut [];
1534 -
            let mut throwList: *mut [*Type] = &mut [];
1595 +
            let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1596 +
            let mut throwList = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1535 1597
            for param in info.paramTypes {
1536 1598
                let stored = copyStructuralTypeToArena(*param, arena);
1537 -
                params.append(allocArenaType(arena, stored), a);
1599 +
                vec::append⟨*Type⟩(&mut params, allocArenaType(arena, stored), a);
1538 1600
            }
1539 1601
            for thrown in info.throwList {
1540 1602
                let stored = copyStructuralTypeToArena(*thrown, arena);
1541 -
                throwList.append(allocArenaType(arena, stored), a);
1603 +
                vec::append⟨*Type⟩(&mut throwList, allocArenaType(arena, stored), a);
1542 1604
            }
1543 1605
            let returnType = copyStructuralTypeToArena(*info.returnType, arena);
1544 1606
            let storedInfo = try! alloc::alloc(
1545 1607
                arena, @sizeOf(FnType), @alignOf(FnType)
1546 1608
            ) as *mut FnType;
1547 1609
            set *storedInfo = FnType {
1548 -
                paramTypes: &params[..],
1610 +
                paramTypes: vec::view⟨*Type⟩(&params),
1549 1611
                returnType: allocArenaType(arena, returnType),
1550 -
                throwList: &throwList[..],
1612 +
                throwList: vec::view⟨*Type⟩(&throwList),
1551 1613
                isUnsafe: info.isUnsafe,
1552 1614
                localCount: info.localCount,
1553 1615
            };
1554 1616
            return Type::Fn(storedInfo);
1555 1617
        }
1619 1681
                mutable: slice.mutable,
1620 1682
            });
1621 1683
        }
1622 1684
        case Type::Array(array) => {
1623 1685
            let item = try substituteTypeWithContext(context, *array.item, sub);
1686 +
            let length = try substituteTypeWithContext(
1687 +
                context, *array.length, sub
1688 +
            );
1624 1689
            return Type::Array(ArrayType {
1625 1690
                item: allocSubstitutedType(context, item),
1626 -
                length: array.length,
1627 -
            });
1628 -
        }
1629 -
        case Type::GenericArray { item, length } => {
1630 -
            let concreteItem = try substituteTypeWithContext(context, *item, sub);
1631 -
            let mut arrayLength: u32 = 0;
1632 -
            match *context {
1633 -
                case TypeSubstitutionContext::Resolution { resolver, .. } => {
1634 -
                    let value = constValueWithSubstitution(resolver, length, sub)
1635 -
                        else throw emitError(resolver, length, ErrorKind::ConstExprRequired);
1636 -
                    if not validateConstIntRange(value, Type::U32) {
1637 -
                        throw emitError(resolver, length, ErrorKind::NumericLiteralOverflow);
1638 -
                    }
1639 -
                    let case ConstValue::Int(int) = value
1640 -
                        else throw emitError(resolver, length, ErrorKind::ConstExprRequired);
1641 -
                    set arrayLength = int.magnitude as u32;
1642 -
                }
1643 -
                case TypeSubstitutionContext::ReadOnly { resolver, .. } => {
1644 -
                    let value = constIntWithSubstitution(
1645 -
                        resolver, length, Type::U32, sub
1646 -
                    ) else throw ResolveError::Failure;
1647 -
                    set arrayLength = value.magnitude as u32;
1648 -
                }
1649 -
            }
1650 -
            return Type::Array(ArrayType {
1651 -
                item: allocSubstitutedType(context, concreteItem),
1652 -
                length: arrayLength,
1691 +
                length: allocSubstitutedType(context, length),
1653 1692
            });
1654 1693
        }
1655 1694
        case Type::Optional(inner) => {
1656 1695
            let value = try substituteTypeWithContext(context, *inner, sub);
1657 1696
            return Type::Optional(allocSubstitutedType(context, value));
1658 1697
        }
1659 1698
        case Type::GenericDataApply(app) => {
1660 1699
            let a = alloc::arenaAllocator(substitutionArena(context));
1661 -
            let mut args: *mut [*Type] = &mut [];
1700 +
            let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1662 1701
            let mut symbolic = false;
1663 1702
            for arg in app.args {
1664 1703
                let replacement = try substituteTypeWithContext(context, *arg, sub);
1665 1704
                set symbolic = symbolic or containsGenericParameter(replacement);
1666 -
                args.append(allocSubstitutedType(context, replacement), a);
1705 +
                vec::append⟨*Type⟩(&mut args, allocSubstitutedType(context, replacement), a);
1667 1706
            }
1707 +
            let argsView = vec::view⟨*Type⟩(&args);
1668 1708
            match *context {
1669 1709
                case TypeSubstitutionContext::Resolution { resolver, .. } => {
1670 1710
                    if symbolic {
1671 1711
                        let application = try! alloc::alloc(
1672 1712
                            &mut resolver.arena,
1673 1713
                            @sizeOf(GenericDataApplyType),
1674 1714
                            @alignOf(GenericDataApplyType),
1675 1715
                        ) as *mut GenericDataApplyType;
1676 1716
                        set *application = GenericDataApplyType {
1677 1717
                            template: app.template,
1678 -
                            args: &args[..],
1718 +
                            args: argsView,
1679 1719
                            site: app.site,
1680 1720
                        };
1681 1721
                        return Type::GenericDataApply(application);
1682 1722
                    }
1683 1723
                    let nominal = try specializeGenericData(
1684 -
                        resolver, app.site, app.template, &args[..], false
1724 +
                        resolver, app.site, app.template, argsView, false
1685 1725
                    );
1686 1726
                    return Type::Nominal(nominal);
1687 1727
                }
1688 1728
                case TypeSubstitutionContext::ReadOnly { resolver, .. } => {
1689 1729
                    if symbolic {
1690 1730
                        throw ResolveError::Failure;
1691 1731
                    }
1692 1732
                    let specialization = findGenericDataSpecialization(
1693 -
                        resolver, app.template, &args[..]
1733 +
                        resolver, app.template, argsView
1694 1734
                    ) else throw ResolveError::Failure;
1695 1735
                    return Type::Nominal(specialization.nominal);
1696 1736
                }
1697 1737
            }
1698 1738
        }
1703 1743
                ..
1704 1744
            } = *context {
1705 1745
                set persistentArena = arena;
1706 1746
            }
1707 1747
            let a = alloc::arenaAllocator(persistentArena);
1708 -
            let mut fields: *mut [RecordField] = &mut [];
1748 +
            let mut fields = vec::Vec⟨RecordField⟩ { data: &mut [], len: 0 };
1709 1749
            let mut offset: u32 = 0;
1710 1750
            let mut alignment: u32 = 1;
1711 1751
            for field in rec.fields {
1712 1752
                let fieldType = try substituteTypeWithContext(
1713 1753
                    context, field.fieldType, sub
1732 1772
                        );
1733 1773
                    }
1734 1774
                }
1735 1775
                let fieldLayout = getTypeLayout(fieldType);
1736 1776
                set offset = mem::alignUp(offset, fieldLayout.alignment);
1737 -
                fields.append(RecordField {
1777 +
                vec::append⟨RecordField⟩(&mut fields, RecordField {
1738 1778
                    name: field.name,
1739 1779
                    fieldType: storedFieldType,
1740 1780
                    offset: offset as i32,
1741 1781
                }, a);
1742 1782
                set offset += fieldLayout.size;
1744 1784
            }
1745 1785
            let nominal = try! alloc::alloc(
1746 1786
                persistentArena, @sizeOf(NominalType), @alignOf(NominalType)
1747 1787
            ) as *mut NominalType;
1748 1788
            set *nominal = NominalType::Record(RecordType {
1749 -
                fields: &fields[..],
1789 +
                fields: vec::view⟨RecordField⟩(&fields),
1750 1790
                labeled: rec.labeled,
1751 1791
                layout: Layout {
1752 1792
                    size: mem::alignUp(offset, alignment),
1753 1793
                    alignment,
1754 1794
                },
1757 1797
            return Type::Nominal(nominal);
1758 1798
        }
1759 1799
        case Type::Fn(info) => {
1760 1800
            let arena = substitutionArena(context);
1761 1801
            let a = alloc::arenaAllocator(arena);
1762 -
            let mut params: *mut [*Type] = &mut [];
1763 -
            let mut throwTypes: *mut [*Type] = &mut [];
1802 +
            let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1803 +
            let mut throwTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
1764 1804
            for param in info.paramTypes {
1765 1805
                let concrete = try substituteTypeWithContext(context, *param, sub);
1766 -
                params.append(allocSubstitutedType(context, concrete), a);
1806 +
                vec::append⟨*Type⟩(&mut params, allocSubstitutedType(context, concrete), a);
1767 1807
            }
1768 1808
            for thrown in info.throwList {
1769 1809
                let concrete = try substituteTypeWithContext(context, *thrown, sub);
1770 -
                throwTypes.append(allocSubstitutedType(context, concrete), a);
1810 +
                vec::append⟨*Type⟩(&mut throwTypes, allocSubstitutedType(context, concrete), a);
1771 1811
            }
1772 1812
            let result = try substituteTypeWithContext(
1773 1813
                context, *info.returnType, sub
1774 1814
            );
1775 1815
            let fnType = try! alloc::alloc(
1776 1816
                arena, @sizeOf(FnType), @alignOf(FnType)
1777 1817
            ) as *mut FnType;
1778 1818
            set *fnType = FnType {
1779 -
                paramTypes: &params[..],
1819 +
                paramTypes: vec::view⟨*Type⟩(&params),
1780 1820
                returnType: allocSubstitutedType(context, result),
1781 -
                throwList: &throwTypes[..],
1821 +
                throwList: vec::view⟨*Type⟩(&throwTypes),
1782 1822
                isUnsafe: info.isUnsafe,
1783 1823
                localCount: info.localCount,
1784 1824
            };
1785 1825
            return Type::Fn(fnType);
1786 1826
        }
1861 1901
}
1862 1902
1863 1903
/// Returns an error, if any, associated with the given node.
1864 1904
fn errorForNode(self: *Resolver, node: *ast::Node) -> ?*Error {
1865 1905
    for i in 0..self.errors.len {
1866 -
        let err = &self.errors[i];
1906 +
        let err = &self.errors.data[i];
1867 1907
        if err.node == node {
1868 1908
            return err;
1869 1909
        }
1870 1910
    }
1871 1911
    return nil;
1941 1981
        unsafeDepth: 0,
1942 1982
        config,
1943 1983
        arena,
1944 1984
        nodeData: NodeDataTable { entries: storage.nodeData },
1945 1985
        types: nil,
1946 -
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1986 +
        errors: vec::Vec⟨Error⟩ { data: storage.errors, len: 0 },
1947 1987
        // TODO: Shouldn't be undefined.
1948 1988
        moduleGraph: undefined,
1949 1989
        moduleScopes,
1950 1990
        instances: undefined,
1951 1991
        instancesLen: 0,
1958 1998
        genericDataSpecializations: nil,
1959 1999
        genericRoots: 0,
1960 2000
        genericSpecializationCount: 0,
1961 2001
    };
1962 2002
}
2003 +
/// Return the diagnostics collected by the resolver.
2004 +
fn diagnostics(self: *mut Resolver) -> Diagnostics {
2005 +
    return Diagnostics { errors: vec::viewMut⟨Error⟩(&mut self.errors) };
2006 +
}
1963 2007
1964 2008
/// Return `true` if there are no errors in the diagnostics.
1965 2009
export fn success(diag: *Diagnostics) -> bool {
1966 2010
    return diag.errors.len == 0;
1967 2011
}
1975 2019
}
1976 2020
1977 2021
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1978 2022
fn emitError(self: *mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1979 2023
    // If our error list is full, just return an error without recording it.
1980 -
    if self.errors.len >= self.errors.cap {
2024 +
    if self.errors.len >= self.errors.data.len {
1981 2025
        return ResolveError::Failure;
1982 2026
    }
1983 2027
    // Don't record more than one error per node.
1984 2028
    if let n = node; errorForNode(self, n) <> nil {
1985 2029
        return ResolveError::Failure;
1986 2030
    }
1987 -
    let idx = self.errors.len;
1988 -
    set self.errors = @sliceOf(self.errors.ptr, idx + 1, self.errors.cap);
1989 -
    set self.errors[idx] = Error { kind, node, moduleId: self.currentMod };
2031 +
    assert vec::push⟨Error⟩(
2032 +
        &mut self.errors,
2033 +
        Error { kind, node, moduleId: self.currentMod },
2034 +
    ), "emitError: error storage unexpectedly full";
1990 2035
1991 2036
    return ResolveError::Failure;
1992 2037
}
1993 2038
1994 2039
/// Like [`emitError`], but for type mismatches specifically.
2144 2189
fn recordGenericTypeUse(self: *mut Resolver, site: *ast::Node, ty: Type) {
2145 2190
    let current = self.currentGenericTemplate else return;
2146 2191
    if not containsGenericParameter(ty) {
2147 2192
        return;
2148 2193
    }
2149 -
    current.typeUses.append(
2194 +
    vec::append⟨GenericTypeUse⟩(
2195 +
        &mut current.typeUses,
2150 2196
        GenericTypeUse { site, ty },
2151 2197
        alloc::arenaAllocator(&mut self.arena),
2152 2198
    );
2153 2199
}
2154 2200
2216 2262
}
2217 2263
2218 2264
/// Associate slice range metadata with a subscript expression.
2219 2265
fn setSliceRangeInfo(self: *mut Resolver, node: *ast::Node, info: SliceRangeInfo) {
2220 2266
    recordGenericTypeUse(self, node, *info.itemType);
2267 +
    if let length = info.capacity {
2268 +
        recordGenericTypeUse(self, node, *length);
2269 +
    }
2221 2270
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
2222 2271
}
2223 2272
2224 2273
/// Associate union variant metadata with a pattern or constructor node.
2225 2274
fn setVariantInfo(self: *mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
2249 2298
fn setForLoopInfo(self: *mut Resolver, node: *ast::Node, info: ForLoopInfo) {
2250 2299
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
2251 2300
    match info {
2252 2301
        case ForLoopInfo::Range { valType, .. } =>
2253 2302
            recordGenericTypeUse(self, node, *valType),
2254 -
        case ForLoopInfo::Collection { elemType, .. } =>
2255 -
            recordGenericTypeUse(self, node, *elemType),
2303 +
        case ForLoopInfo::Collection { elemType, length, .. } => {
2304 +
            recordGenericTypeUse(self, node, *elemType);
2305 +
            if let descriptor = length {
2306 +
                recordGenericTypeUse(self, node, *descriptor);
2307 +
            }
2308 +
        }
2256 2309
    }
2257 2310
}
2258 2311
2259 2312
/// Retrieve the constant value associated with a node, if any.
2260 2313
export fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue {
2464 2517
        }
2465 2518
    }
2466 2519
    return layout;
2467 2520
}
2468 2521
2469 -
/// Get the layout of an array type.
2522 +
/// Get the layout of a concrete array type.
2470 2523
export fn getArrayLayout(arr: ArrayType) -> Layout {
2524 +
    let length = concreteArrayLength(arr.length)
2525 +
        else panic "getArrayLayout: symbolic array length";
2471 2526
    let itemLayout = getTypeLayout(*arr.item);
2472 2527
    return Layout {
2473 -
        size: itemLayout.size * arr.length,
2528 +
        size: itemLayout.size * length,
2474 2529
        alignment: itemLayout.alignment,
2475 2530
    };
2476 2531
}
2477 2532
2478 2533
/// Get the layout of an optional type.
2844 2899
    match to {
2845 2900
        case Type::Array(lhs) => {
2846 2901
            let case Type::Array(rhs) = from
2847 2902
                else return nil;
2848 2903
2849 -
            if lhs.length <> rhs.length {
2904 +
            if not typesEqual(*lhs.length, *rhs.length) {
2850 2905
                return nil;
2851 2906
            }
2852 2907
            // For array literals, check each element individually for
2853 2908
            // assignability.
2854 2909
            match rval.value {
2855 2910
                case ast::NodeValue::ArrayLit(items) => {
2856 -
                    if rhs.length == 0 and lhs.length == 0 {
2911 +
                    if let length = concreteArrayLength(lhs.length); length == 0 {
2857 2912
                        return Coercion::Identity;
2858 2913
                    }
2859 2914
                    // TODO: This won't work, because we should be setting coercions
2860 2915
                    // for every list item, but we don't. It's best to not have an
2861 2916
                    // `isAssignable` function and just have one that records coercions.
2996 3051
            }
2997 3052
            return bEnd == nil;
2998 3053
        }
2999 3054
        case Type::Array(aa) => {
3000 3055
            let case Type::Array(ab) = b else return false;
3001 -
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
3056 +
            return typesEqual(*aa.length, *ab.length)
3057 +
                and typesEqual(*aa.item, *ab.item);
3002 3058
        }
3003 3059
        case Type::Optional(oa) => {
3004 3060
            let case Type::Optional(ob) = b else return false;
3005 3061
            return typesEqual(*oa, *ob);
3006 3062
        }
3144 3200
            case 1 => return RecordField {
3145 3201
                name: LEN_FIELD,
3146 3202
                fieldType: Type::U32,
3147 3203
                offset: PTR_SIZE as i32,
3148 3204
            },
3149 -
            case 2 => return RecordField {
3150 -
                name: CAP_FIELD,
3151 -
                fieldType: Type::U32,
3152 -
                offset: PTR_SIZE as i32 + 4,
3153 -
            },
3154 3205
            else => return nil,
3155 3206
        }
3156 3207
    }
3157 3208
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
3158 3209
        index < recInfo.fields.len
4023 4074
) -> RecordType throws (ResolveError) {
4024 4075
    if fields.len > parser::MAX_RECORD_FIELDS {
4025 4076
        throw emitError(self, node, ErrorKind::Internal);
4026 4077
    }
4027 4078
    let a = alloc::arenaAllocator(&mut self.arena);
4028 -
    let mut result: *mut [RecordField] = &mut [];
4079 +
    let mut result = vec::Vec⟨RecordField⟩ { data: &mut [], len: 0 };
4029 4080
    let mut offset: u32 = 0;
4030 4081
    let mut alignment: u32 = 1;
4031 4082
    for fieldNode, i in fields {
4032 4083
        let case ast::NodeValue::RecordField {
4033 4084
            field: nameNode,
4066 4117
        if labeled {
4067 4118
            let requiredName = nameNode
4068 4119
                else panic "buildRecordType: labeled record field missing name";
4069 4120
            set name = try nodeName(self, requiredName);
4070 4121
        }
4071 -
        result.append(RecordField {
4122 +
        vec::append⟨RecordField⟩(&mut result, RecordField {
4072 4123
            name,
4073 4124
            fieldType,
4074 4125
            offset: offset as i32,
4075 4126
        }, a);
4076 4127
        set offset += layout.size;
4077 4128
        set alignment = max(alignment, layout.alignment);
4078 4129
    }
4079 4130
    return RecordType {
4080 -
        fields: &result[..],
4131 +
        fields: vec::view⟨RecordField⟩(&result),
4081 4132
        labeled,
4082 4133
        layout: Layout {
4083 4134
            size: mem::alignUp(offset, alignment),
4084 4135
            alignment,
4085 4136
        },
4096 4147
    source: AggregateMemberSource,
4097 4148
) -> UnionType throws (ResolveError) {
4098 4149
    assert decl.variants.len <= MAX_UNION_VARIANTS,
4099 4150
        "buildUnionType: maximum union variants exceeded";
4100 4151
    let a = alloc::arenaAllocator(&mut self.arena);
4101 -
    let mut variants: *mut [UnionVariant] = &mut [];
4152 +
    let mut variants = vec::Vec⟨UnionVariant⟩ { data: &mut [], len: 0 };
4102 4153
    let mut iota: u32 = 0;
4103 4154
    let mut tagSubstitution: ?*Substitution = nil;
4104 4155
    if let case AggregateMemberSource::Generic {
4105 4156
        substitution, ..
4106 4157
    } = source {
4152 4203
            name,
4153 4204
            variantNode,
4154 4205
            0,
4155 4206
        );
4156 4207
        set symbol.moduleId = owner.moduleId;
4157 -
        variants.append(UnionVariant { name, valueType, symbol }, a);
4208 +
        vec::append⟨UnionVariant⟩(
4209 +
            &mut variants, UnionVariant { name, valueType, symbol }, a
4210 +
        );
4158 4211
    }
4159 -
    let info = computeUnionLayout(&variants[..]);
4212 +
    let variantView = vec::view⟨UnionVariant⟩(&variants);
4213 +
    let info = computeUnionLayout(variantView);
4160 4214
    return UnionType {
4161 -
        variants: &variants[..],
4215 +
        variants: variantView,
4162 4216
        layout: info.layout,
4163 4217
        valOffset: info.valOffset,
4164 4218
        isAllVoid: info.isAllVoid,
4165 4219
        declaredLinear,
4166 4220
    };
4285 4339
            expected: template.params.len,
4286 4340
            actual: app.args.len,
4287 4341
        }));
4288 4342
    }
4289 4343
    let a = alloc::arenaAllocator(&mut self.arena);
4290 -
    let mut args: *mut [*Type] = &mut [];
4344 +
    let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
4291 4345
    for argNode, i in app.args {
4292 4346
        let argType = try resolveGenericArgument(self, argNode, template.params[i]);
4293 4347
        if containsGenericParameter(argType) {
4294 4348
            throw emitError(self, argNode, ErrorKind::GenericConcreteArgumentsRequired);
4295 4349
        }
4296 -
        args.append(allocType(self, argType), a);
4350 +
        vec::append⟨*Type⟩(&mut args, allocType(self, argType), a);
4297 4351
    }
4298 4352
    let nominal = try specializeGenericData(
4299 -
        self, node, templateSym, &args[..], rooted
4353 +
        self, node, templateSym, vec::view⟨*Type⟩(&args), rooted
4300 4354
    );
4301 4355
4302 4356
    setNodeSymbol(self, node, templateSym);
4303 4357
    setNodeType(self, node, Type::Nominal(nominal));
4304 4358
    return nominal;
4420 4474
            expected: template.params.len,
4421 4475
            actual: app.args.len,
4422 4476
        }));
4423 4477
    }
4424 4478
    let a = alloc::arenaAllocator(&mut self.arena);
4425 -
    let mut args: *mut [*Type] = &mut [];
4479 +
    let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
4426 4480
    let caller = currentGenericTemplateSymbol(self);
4427 4481
    for argNode, i in app.args {
4428 4482
        let argType = try resolveGenericArgument(self, argNode, template.params[i]);
4429 4483
        let symbolic = containsGenericParameter(argType);
4430 4484
        if symbolic and caller == nil {
4439 4493
                        self, argNode, ErrorKind::GenericBoundUnsatisfied(bound.name)
4440 4494
                    );
4441 4495
                }
4442 4496
            }
4443 4497
        }
4444 -
        args.append(allocType(self, argType), a);
4498 +
        vec::append⟨*Type⟩(&mut args, allocType(self, argType), a);
4445 4499
    }
4500 +
    let argsView = vec::view⟨*Type⟩(&args);
4446 4501
    if rooted {
4447 4502
        if caller <> nil {
4448 4503
            throw emitError(self, node, ErrorKind::GenericConcreteArgumentsRequired);
4449 4504
        }
4450 4505
        let specialization = try internGenericFnSpecialization(
4451 -
            self, templateSym, &args[..], node, 0
4506 +
            self, templateSym, argsView, node, 0
4452 4507
        );
4453 4508
        setNodeSymbol(self, node, templateSym);
4454 4509
        setNodeType(self, node, Type::Fn(specialization.fnType));
4455 4510
        set self.nodeData.entries[node.id].extra =
4456 4511
            NodeExtra::GenericFnCall(specialization);
4457 4512
        return specialization.fnType;
4458 4513
    }
4459 4514
    if caller == nil {
4460 -
        if let existing = findGenericFnSpecialization(self, templateSym, &args[..]) {
4515 +
        if let existing = findGenericFnSpecialization(self, templateSym, argsView) {
4461 4516
            setNodeSymbol(self, node, templateSym);
4462 4517
            setNodeType(self, node, Type::Fn(existing.fnType));
4463 4518
            set self.nodeData.entries[node.id].extra =
4464 4519
                NodeExtra::GenericFnCall(existing);
4465 4520
            return existing.fnType;
4466 4521
        }
4467 4522
    }
4468 -
    let sub = Substitution { params: template.params, args: &args[..] };
4523 +
    let sub = Substitution { params: template.params, args: argsView };
4469 4524
    let applied = try substituteType(self, Type::Fn(signature), &sub, node);
4470 4525
    let case Type::Fn(appliedFn) = applied
4471 4526
        else throw emitError(self, node, ErrorKind::Internal);
4472 4527
    recordGenericFnDependency(
4473 -
        self, node, caller, templateSym, &args[..], appliedFn
4528 +
        self, node, caller, templateSym, argsView, appliedFn
4474 4529
    );
4475 4530
    return appliedFn;
4476 4531
}
4477 4532
4478 4533
/// Expand explicit roots through symbolic generic calls to a fixed point.
4497 4552
        };
4498 4553
        let mut edge = self.genericFnDependencies;
4499 4554
        while let dependency = edge {
4500 4555
            if dependency.caller == specialization.template {
4501 4556
                let a = alloc::arenaAllocator(&mut self.arena);
4502 -
                let mut concreteArgs: *mut [*Type] = &mut [];
4557 +
                let mut concreteArgs = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
4503 4558
                for arg in dependency.args {
4504 4559
                    let concrete = try substituteType(
4505 4560
                        self, *arg, &callerSub, dependency.site
4506 4561
                    );
4507 4562
                    if containsGenericParameter(concrete) {
4509 4564
                            self,
4510 4565
                            dependency.site,
4511 4566
                            ErrorKind::GenericConcreteArgumentsRequired,
4512 4567
                        );
4513 4568
                    }
4514 -
                    concreteArgs.append(allocType(self, concrete), a);
4569 +
                    vec::append⟨*Type⟩(
4570 +
                        &mut concreteArgs, allocType(self, concrete), a
4571 +
                    );
4515 4572
                }
4573 +
                let concreteArgsView = vec::view⟨*Type⟩(&concreteArgs);
4516 4574
                let calleeTemplate = genericTemplateFor(self, dependency.callee)
4517 4575
                    else throw emitError(
4518 4576
                        self, dependency.site, ErrorKind::Internal
4519 4577
                    );
4520 -
                for arg, i in concreteArgs {
4578 +
                for arg, i in concreteArgsView {
4521 4579
                    for bound in calleeTemplate.params[i].bounds {
4522 4580
                        if findInstance(self, bound, *arg) == nil {
4523 4581
                            throw emitError(
4524 4582
                                self,
4525 4583
                                dependency.site,
4529 4587
                    }
4530 4588
                }
4531 4589
                let concreteCallee = try internGenericFnSpecialization(
4532 4590
                    self,
4533 4591
                    dependency.callee,
4534 -
                    &concreteArgs[..],
4592 +
                    concreteArgsView,
4535 4593
                    dependency.site,
4536 4594
                    specialization.depth + 1,
4537 4595
                );
4538 4596
                let resolution = try! alloc::alloc(
4539 4597
                    &mut self.arena,
4594 4652
        );
4595 4653
        let sub = Substitution {
4596 4654
            params: template.params,
4597 4655
            args: specialization.args,
4598 4656
        };
4599 -
        for typeUse in template.typeUses {
4657 +
        for typeUse in vec::view⟨GenericTypeUse⟩(&template.typeUses) {
4600 4658
            let concrete = try substituteType(
4601 4659
                self, typeUse.ty, &sub, typeUse.site
4602 4660
            );
4603 4661
            if containsGenericParameter(concrete) {
4604 4662
                throw emitError(
5506 5564
) -> *[*GenericParamType] throws (ResolveError) {
5507 5565
    if nodes.len > MAX_GENERIC_PARAMS {
5508 5566
        throw emitError(self, owner, ErrorKind::GenericParameterLimit);
5509 5567
    }
5510 5568
    let a = alloc::arenaAllocator(&mut self.arena);
5511 -
    let mut result: *mut [*GenericParamType] = &mut [];
5569 +
    let mut result = vec::Vec⟨*GenericParamType⟩ { data: &mut [], len: 0 };
5512 5570
    for paramNode, index in nodes {
5513 5571
        let case ast::NodeValue::GenericParam(param) = paramNode.value
5514 5572
            else throw emitError(self, paramNode, ErrorKind::Internal);
5515 5573
        let mut paramName: *[u8] = undefined;
5516 5574
        let mut nameNode: *ast::Node = undefined;
5517 -
        let mut traitBounds: *mut [*TraitType] = &mut [];
5575 +
        let mut traitBounds = vec::Vec⟨*TraitType⟩ { data: &mut [], len: 0 };
5518 5576
        let mut constType: ?*Type = nil;
5519 5577
        match param {
5520 5578
            case ast::GenericParam::Const { name, type } => {
5521 5579
                set nameNode = name;
5522 5580
                set paramName = try nodeName(self, name);
5533 5591
                    let boundSym = try resolveNamePath(self, bound);
5534 5592
                    let case SymbolData::Trait(traitInfo) = boundSym.data else {
5535 5593
                        throw emitError(self, bound, ErrorKind::GenericBoundNotTrait);
5536 5594
                    };
5537 5595
                    setNodeSymbol(self, bound, boundSym);
5538 -
                    traitBounds.append(traitInfo, a);
5596 +
                    vec::append⟨*TraitType⟩(&mut traitBounds, traitInfo, a);
5539 5597
                }
5540 5598
            }
5541 5599
        }
5542 5600
        let used = try! alloc::alloc(
5543 5601
            &mut self.arena, @sizeOf(bool), @alignOf(bool)
5551 5609
        set *p = GenericParamType {
5552 5610
            owner,
5553 5611
            node: paramNode,
5554 5612
            name: paramName,
5555 5613
            index,
5556 -
            bounds: &traitBounds[..],
5614 +
            bounds: vec::view⟨*TraitType⟩(&traitBounds),
5557 5615
            used,
5558 5616
            constType,
5559 5617
        };
5560 5618
        let data = SymbolData::ConstParameter(p) if constType <> nil
5561 5619
            else SymbolData::TypeParameter(p);
5568 5626
            setNodeType(self, paramNode, *ty);
5569 5627
        } else {
5570 5628
            setNodeType(self, nameNode, Type::Parameter(p));
5571 5629
            setNodeType(self, paramNode, Type::Parameter(p));
5572 5630
        }
5573 -
        result.append(p, a);
5631 +
        vec::append⟨*GenericParamType⟩(&mut result, p, a);
5574 5632
    }
5575 -
    return &result[..];
5633 +
    return vec::view⟨*GenericParamType⟩(&result);
5576 5634
}
5577 5635
5578 5636
/// Retrieve sparse metadata for a generic declaration symbol.
5579 5637
export fn genericTemplateFor(self: *Resolver, symbol: *Symbol) -> ?*GenericTemplate {
5580 5638
    let mut cursor = self.genericTemplates;
5667 5725
        {
5668 5726
            throw emitError(self, node, ErrorKind::GenericFnAttribute);
5669 5727
        }
5670 5728
    }
5671 5729
    let a = alloc::arenaAllocator(&mut self.arena);
5672 -
    let mut paramTypes: *mut [*Type] = &mut [];
5673 -
    let mut throwList: *mut [*Type] = &mut [];
5730 +
    let mut paramTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
5731 +
    let mut throwList = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
5674 5732
    let mut fnType = FnType {
5675 5733
        paramTypes: &[],
5676 5734
        returnType: allocType(self, Type::Void),
5677 5735
        throwList: &[],
5678 5736
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
5711 5769
            self, paramNode, typeContext
5712 5770
        ) catch e {
5713 5771
            exitFn(self);
5714 5772
            throw e;
5715 5773
        };
5716 -
        paramTypes.append(allocType(self, paramTy), a);
5774 +
        vec::append⟨*Type⟩(&mut paramTypes, allocType(self, paramTy), a);
5717 5775
    }
5718 5776
5719 5777
    if decl.sig.throwList.len > MAX_FN_THROWS {
5720 5778
        exitFn(self);
5721 5779
        throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
5732 5790
        };
5733 5791
        try ensureStorableType(self, throwNode, throwTy) catch e {
5734 5792
            exitFn(self);
5735 5793
            throw e;
5736 5794
        };
5737 -
        throwList.append(allocType(self, throwTy), a);
5795 +
        vec::append⟨*Type⟩(&mut throwList, allocType(self, throwTy), a);
5738 5796
    }
5739 5797
    exitFn(self);
5740 -
    set fnType.paramTypes = &paramTypes[..];
5741 -
    set fnType.throwList = &throwList[..];
5798 +
    set fnType.paramTypes = vec::view⟨*Type⟩(&paramTypes);
5799 +
    set fnType.throwList = vec::view⟨*Type⟩(&throwList);
5742 5800
5743 5801
    let fnInfo = allocFnType(self, fnType);
5744 5802
    let ty = Type::Fn(fnInfo);
5745 5803
    let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask)
5746 5804
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
5751 5809
            params: genericParams,
5752 5810
            signature: fnInfo,
5753 5811
            members: &[],
5754 5812
            declaredLinear: false,
5755 5813
            bodyResolved: false,
5756 -
            typeUses: &mut [],
5814 +
            typeUses: vec::Vec⟨GenericTypeUse⟩ { data: &mut [], len: 0 },
5757 5815
            next: nil,
5758 5816
        });
5759 5817
    }
5760 5818
    return ty;
5761 5819
}
5928 5986
        params: genericParams,
5929 5987
        signature: nil,
5930 5988
        members: &[],
5931 5989
        declaredLinear,
5932 5990
        bodyResolved: true,
5933 -
        typeUses: &mut [],
5991 +
        typeUses: vec::Vec⟨GenericTypeUse⟩ { data: &mut [], len: 0 },
5934 5992
        next: nil,
5935 5993
    });
5936 5994
    let a = alloc::arenaAllocator(&mut self.arena);
5937 -
    let mut memberTypes: *mut [*Type] = &mut [];
5995 +
    let mut memberTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
5938 5996
    for member in members {
5939 5997
        let memberTy = try resolveGenericDataMember(self, member) catch e {
5940 5998
            exitScope(self);
5941 5999
            throw e;
5942 6000
        };
5943 -
        memberTypes.append(allocType(self, memberTy), a);
6001 +
        vec::append⟨*Type⟩(&mut memberTypes, allocType(self, memberTy), a);
5944 6002
    }
5945 6003
    exitScope(self);
5946 -
    set metadata.members = &memberTypes[..];
6004 +
    set metadata.members = vec::view⟨*Type⟩(&memberTypes);
5947 6005
}
5948 6006
5949 6007
/// Resolve record field types for a named record declaration.
5950 6008
fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
5951 6009
    throws (ResolveError)
6015 6073
    };
6016 6074
    set *entry = TraitType {
6017 6075
        name,
6018 6076
        moduleId: self.currentMod,
6019 6077
        nodeId: node.id,
6020 -
        methods: &mut [],
6021 -
        supertraits: &mut [],
6078 +
        methods: vec::Vec⟨TraitMethod⟩ { data: &mut [], len: 0 },
6079 +
        supertraits: vec::Vec⟨*TraitType⟩ { data: &mut [], len: 0 },
6022 6080
        selfType,
6023 6081
        state: TraitState::Queued,
6024 6082
        objectSafe: true,
6025 6083
    };
6026 6084
    return entry;
6048 6106
}
6049 6107
6050 6108
/// Find a trait method by name.
6051 6109
export fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod {
6052 6110
    for i in 0..traitType.methods.len {
6053 -
        if traitType.methods[i].name == name {
6054 -
            return &traitType.methods[i];
6111 +
        if traitType.methods.data[i].name == name {
6112 +
            return &traitType.methods.data[i];
6055 6113
        }
6056 6114
    }
6057 6115
    return nil;
6058 6116
}
6059 6117
6116 6174
                expected: ast::MAX_TRAIT_METHODS,
6117 6175
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
6118 6176
            }));
6119 6177
        }
6120 6178
        // Copy inherited methods into this trait's method table.
6121 -
        for inherited in superTrait.methods {
6179 +
        for inherited in vec::view⟨TraitMethod⟩(&superTrait.methods) {
6122 6180
            if let _ = findTraitMethod(traitType, inherited.name) {
6123 6181
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
6124 6182
            }
6125 -
            traitType.methods.append(TraitMethod {
6183 +
            vec::append⟨TraitMethod⟩(&mut traitType.methods, TraitMethod {
6126 6184
                name: inherited.name,
6127 6185
                fnType: inherited.fnType,
6128 6186
                mutable: inherited.mutable,
6129 6187
                receiverClass: inherited.receiverClass,
6130 6188
                owner: inherited.owner,
6131 -
                index: traitType.methods.len as u32,
6189 +
                index: traitType.methods.len,
6132 6190
            }, a);
6133 6191
        }
6134 -
        traitType.supertraits.append(superTrait, a);
6192 +
        vec::append⟨*TraitType⟩(&mut traitType.supertraits, superTrait, a);
6135 6193
        if not superTrait.objectSafe {
6136 6194
            set traitType.objectSafe = false;
6137 6195
        }
6138 6196
    }
6139 6197
6171 6229
        if receiverTargetName <> traitType.name {
6172 6230
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
6173 6231
        }
6174 6232
        // Resolve parameter types and return type.
6175 6233
        let a = alloc::arenaAllocator(&mut self.arena);
6176 -
        let mut paramTypes: *mut [*Type] = &mut [];
6177 -
        let mut throwList: *mut [*Type] = &mut [];
6234 +
        let mut paramTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
6235 +
        let mut throwList = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
6178 6236
        let mut retType = allocType(self, Type::Void);
6179 6237
6180 6238
        if sig.params.len > MAX_FN_PARAMS {
6181 6239
            throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch {
6182 6240
                expected: MAX_FN_PARAMS,
6185 6243
        }
6186 6244
        for paramNode in sig.params {
6187 6245
            let case ast::NodeValue::FnParam(param) = paramNode.value
6188 6246
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
6189 6247
            let paramTy = try resolveTraitSignatureType(self, traitType, param.type);
6190 -
            paramTypes.append(allocType(self, paramTy), a);
6248 +
            vec::append⟨*Type⟩(&mut paramTypes, allocType(self, paramTy), a);
6191 6249
        }
6192 6250
        if let ret = sig.returnType {
6193 6251
            set retType = allocType(
6194 6252
                self, try resolveTraitSignatureType(self, traitType, ret)
6195 6253
            );
6201 6259
                actual: sig.throwList.len,
6202 6260
            }));
6203 6261
        }
6204 6262
        for throwNode in sig.throwList {
6205 6263
            let throwTy = try resolveTraitSignatureType(self, traitType, throwNode);
6206 -
            throwList.append(allocType(self, throwTy), a);
6264 +
            vec::append⟨*Type⟩(&mut throwList, allocType(self, throwTy), a);
6207 6265
        }
6208 6266
        let fnType = FnType {
6209 -
            paramTypes: &paramTypes[..],
6267 +
            paramTypes: vec::view⟨*Type⟩(&paramTypes),
6210 6268
            returnType: retType,
6211 -
            throwList: &throwList[..],
6269 +
            throwList: vec::view⟨*Type⟩(&throwList),
6212 6270
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
6213 6271
            localCount: 0,
6214 6272
        };
6215 6273
        if containsGenericParameter(Type::Fn(&fnType)) {
6216 6274
            set traitType.objectSafe = false;
6217 6275
        }
6218 -
        traitType.methods.append(TraitMethod {
6276 +
        vec::append⟨TraitMethod⟩(&mut traitType.methods, TraitMethod {
6219 6277
            name: methodName,
6220 6278
            fnType: allocFnType(self, fnType),
6221 6279
            mutable,
6222 6280
            receiverClass,
6223 6281
            owner: traitType,
6224 -
            index: traitType.methods.len as u32,
6282 +
            index: traitType.methods.len,
6225 6283
        }, a);
6226 6284
6227 6285
        setNodeType(self, methodNode, Type::Void);
6228 6286
    }
6229 6287
    set traitType.state = TraitState::Complete;
6409 6467
            }
6410 6468
        }
6411 6469
6412 6470
        // Build final function type: receiver plus trait's canonical types.
6413 6471
        let a = alloc::arenaAllocator(&mut self.arena);
6414 -
        // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`?
6415 -
        let mut paramTypes: *mut [*Type] = &mut [];
6416 -
        paramTypes.append(allocType(self, receiverPtrType), a);
6472 +
        let mut paramTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
6473 +
        vec::append⟨*Type⟩(
6474 +
            &mut paramTypes, allocType(self, receiverPtrType), a
6475 +
        );
6417 6476
6418 6477
        for ty in expectedFn.paramTypes {
6419 -
            paramTypes.append(ty, a);
6478 +
            vec::append⟨*Type⟩(&mut paramTypes, ty, a);
6420 6479
        }
6421 6480
        let fnType = FnType {
6422 -
            paramTypes: &paramTypes[..],
6481 +
            paramTypes: vec::view⟨*Type⟩(&paramTypes),
6423 6482
            returnType: expectedFn.returnType,
6424 6483
            throwList: expectedFn.throwList,
6425 6484
            isUnsafe: expectedFn.isUnsafe,
6426 6485
            localCount: 0,
6427 6486
        };
6443 6502
        set entry.methods[tm.index] = sym;
6444 6503
        set covered[tm.index] = true;
6445 6504
    }
6446 6505
6447 6506
    // Fill inherited method slots from supertrait instances.
6448 -
    for superTrait in traitInfo.supertraits {
6507 +
    for superTrait in vec::view⟨*TraitType⟩(&traitInfo.supertraits) {
6449 6508
        let superInst = findInstance(self, superTrait, concreteType)
6450 6509
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
6451 -
        for superMethod, mi in superTrait.methods {
6510 +
        for superMethod, mi in vec::view⟨TraitMethod⟩(&superTrait.methods) {
6452 6511
            let merged = findTraitMethod(traitInfo, superMethod.name)
6453 6512
                else panic "resolveInstanceDecl: inherited method not found";
6454 6513
            if not covered[merged.index] {
6455 6514
                set entry.methods[merged.index] = superInst.methods[mi];
6456 6515
                set covered[merged.index] = true;
6457 6516
            }
6458 6517
        }
6459 6518
    }
6460 6519
6461 6520
    // Check that all trait methods are implemented.
6462 -
    for method, i in traitInfo.methods {
6521 +
    for method, i in vec::view⟨TraitMethod⟩(&traitInfo.methods) {
6463 6522
        if not covered[i] {
6464 6523
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
6465 6524
        }
6466 6525
    }
6467 6526
    set self.instances[self.instancesLen] = entry;
6573 6632
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
6574 6633
    }
6575 6634
6576 6635
    // Resolve parameter types.
6577 6636
    let a = alloc::arenaAllocator(&mut self.arena);
6578 -
    let mut paramTypes: *mut [*Type] = &mut [];
6637 +
    let mut paramTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
6579 6638
6580 6639
    // Receiver is the first parameter.
6581 6640
    let receiverPtrType = Type::Pointer(PointerType {
6582 6641
        class: receiver.class,
6583 6642
        target: allocType(self, concreteType),
6584 6643
        mutable: receiver.mutable,
6585 6644
    });
6586 -
    paramTypes.append(allocType(self, receiverPtrType), a);
6645 +
    vec::append⟨*Type⟩(
6646 +
        &mut paramTypes, allocType(self, receiverPtrType), a
6647 +
    );
6587 6648
6588 6649
    for paramNode in sig.params {
6589 6650
        let case ast::NodeValue::FnParam(param) = paramNode.value
6590 6651
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
6591 6652
        let paramTy = try resolveValueType(self, param.type);
6592 -
        paramTypes.append(allocType(self, paramTy), a);
6653 +
        vec::append⟨*Type⟩(&mut paramTypes, allocType(self, paramTy), a);
6593 6654
    }
6594 6655
6595 6656
    // Resolve return type.
6596 6657
    let mut returnType = Type::Void;
6597 6658
    if let retNode = sig.returnType {
6598 6659
        set returnType = try resolveValueType(self, retNode);
6599 6660
    }
6600 6661
6601 6662
    // Resolve throw list.
6602 -
    let mut throwTypes: *mut [*Type] = &mut [];
6663 +
    let mut throwTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
6603 6664
    for throwNode in sig.throwList {
6604 6665
        let throwTy = try resolveValueType(self, throwNode);
6605 -
        throwTypes.append(allocType(self, throwTy), a);
6666 +
        vec::append⟨*Type⟩(&mut throwTypes, allocType(self, throwTy), a);
6606 6667
    }
6607 6668
6608 6669
    let retTypePtr = allocType(self, returnType);
6609 -
    let throwList = &throwTypes[..];
6670 +
    let throwList = vec::view⟨*Type⟩(&throwTypes);
6671 +
    let paramTypesView = vec::view⟨*Type⟩(&paramTypes);
6610 6672
6611 6673
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
6612 6674
    // Full function type (receiver + params) for lowering.
6613 6675
    let fullFnType = FnType {
6614 -
        paramTypes: &paramTypes[..],
6676 +
        paramTypes: paramTypesView,
6615 6677
        returnType: retTypePtr,
6616 6678
        throwList,
6617 6679
        isUnsafe,
6618 6680
        localCount: 0,
6619 6681
    };
6620 6682
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
6621 6683
6622 6684
    // Function type excluding receiver, for call arg checking.
6623 6685
    let checkFnType = FnType {
6624 -
        paramTypes: &paramTypes[1..],
6686 +
        paramTypes: &paramTypesView[1..],
6625 6687
        returnType: retTypePtr,
6626 6688
        throwList,
6627 6689
        isUnsafe,
6628 6690
        localCount: 0,
6629 6691
    };
6921 6983
                } else => {}
6922 6984
            }
6923 6985
        }
6924 6986
        case Type::Array(arrayInfo) => {
6925 6987
            if let case ast::NodeValue::ArrayLit(items) = pattern.value {
6926 -
                if items.len as u32 <> arrayInfo.length {
6988 +
                let length = concreteArrayLength(arrayInfo.length)
6989 +
                    else throw emitError(self, pattern, ErrorKind::ConstExprRequired);
6990 +
                if items.len as u32 <> length {
6927 6991
                    throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(
6928 -
                        CountMismatch { expected: arrayInfo.length, actual: items.len as u32 }
6992 +
                        CountMismatch { expected: length, actual: items.len as u32 }
6929 6993
                    ));
6930 6994
                }
6931 6995
                let elemTy = *arrayInfo.item;
6932 6996
                for item in items {
6933 6997
                    try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy);
7780 7844
    self: *mut Resolver,
7781 7845
    node: *ast::Node,
7782 7846
    kind: ast::Builtin,
7783 7847
    args: *mut [*ast::Node]
7784 7848
) -> Type throws (ResolveError) {
7785 -
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
7849 +
    // Handle `@sliceOf(ptr, len)`.
7786 7850
    if kind == ast::Builtin::SliceOf {
7787 -
        if args.len <> 2 and args.len <> 3 {
7851 +
        if args.len <> 2 {
7788 7852
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
7789 7853
                expected: 2,
7790 7854
                actual: args.len as u32,
7791 7855
            }));
7792 7856
        }
7793 7857
        let ptrType = try visit(self, args[0], Type::Unknown);
7794 7858
        let case Type::Pointer(ptr) = ptrType else {
7795 7859
            throw emitError(self, node, ErrorKind::ExpectedPointer);
7796 7860
        };
7797 7861
        let _ = try checkAssignable(self, args[1], Type::U32);
7798 -
        if args.len == 3 {
7799 -
            let _ = try checkAssignable(self, args[2], Type::U32);
7800 -
        }
7801 7862
        return setNodeType(self, node, Type::Slice(SliceType {
7802 7863
            class: ptr.class,
7803 7864
            item: ptr.target,
7804 7865
            mutable: ptr.mutable,
7805 7866
        }));
7806 7867
    }
7868 +
    if kind == ast::Builtin::Relocate {
7869 +
        if args.len <> 2 {
7870 +
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
7871 +
                expected: 2,
7872 +
                actual: args.len as u32,
7873 +
            }));
7874 +
        }
7875 +
        let destinationType = try visit(self, args[0], Type::Unknown);
7876 +
        let case Type::Slice(destination) = destinationType else {
7877 +
            throw emitError(self, args[0], ErrorKind::ExpectedIndexable);
7878 +
        };
7879 +
        if not destination.mutable or not typesEqual(*destination.item, Type::U8) {
7880 +
            throw emitTypeMismatch(self, args[0], TypeMismatch {
7881 +
                expected: Type::Slice(SliceType {
7882 +
                    class: destination.class,
7883 +
                    item: allocType(self, Type::U8),
7884 +
                    mutable: true,
7885 +
                }),
7886 +
                actual: destinationType,
7887 +
            });
7888 +
        }
7889 +
        let sourceType = try visit(self, args[1], Type::Unknown);
7890 +
        let case Type::Slice(source) = sourceType else {
7891 +
            throw emitError(self, args[1], ErrorKind::ExpectedIndexable);
7892 +
        };
7893 +
        if not source.mutable or not typesEqual(*source.item, Type::U8) {
7894 +
            throw emitTypeMismatch(self, args[1], TypeMismatch {
7895 +
                expected: Type::Slice(SliceType {
7896 +
                    class: source.class,
7897 +
                    item: allocType(self, Type::U8),
7898 +
                    mutable: true,
7899 +
                }),
7900 +
                actual: sourceType,
7901 +
            });
7902 +
        }
7903 +
        return setNodeType(self, node, Type::Void);
7904 +
    }
7807 7905
    if args.len <> 1 {
7808 7906
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
7809 7907
            expected: 1,
7810 7908
            actual: args.len as u32,
7811 7909
        }));
7812 7910
    }
7813 7911
7814 7912
    let ty = try resolveValueType(self, args[0]);
7913 +
    recordGenericTypeUse(self, node, ty);
7914 +
    if containsGenericParameter(ty) {
7915 +
        return setNodeType(self, node, Type::U32);
7916 +
    }
7815 7917
    // Ensure the type body is resolved before computing layout.
7816 7918
    // TODO: Somehow, ensuring the type is resolved should just happen all
7817 7919
    // the time, lazily.
7818 7920
    try ensureTypeResolved(self, ty, args[0]);
7819 -
    if containsGenericParameter(ty) {
7820 -
        throw emitError(self, args[0], ErrorKind::GenericLayoutRequired);
7821 -
    }
7822 7921
    // TODO: This should be stored in `symbol` instead of having to recompute it.
7823 7922
    // That way there's a canonical place to look for code gen.
7824 7923
    let layout = getTypeLayout(ty);
7825 -
7826 7924
    // Evaluate the built-in.
7827 7925
    let mut value: u32 = undefined;
7828 7926
    match kind {
7829 7927
        case ast::Builtin::SizeOf => {
7830 7928
            set value = layout.size;
7833 7931
            set value = layout.alignment;
7834 7932
        },
7835 7933
        case ast::Builtin::SliceOf => {
7836 7934
            panic "unreachable: @sliceOf handled above";
7837 7935
        }
7936 +
        case ast::Builtin::Relocate => {
7937 +
            panic "resolveBuiltinCall: unreachable relocation builtin";
7938 +
        }
7838 7939
    }
7839 7940
    // Record as constant value for constant folding.
7840 7941
    setNodeConstValue(self, node, ConstValue::Int(ConstInt {
7841 7942
        magnitude: value as u64,
7842 7943
        bits: 32,
7891 7992
                return true;
7892 7993
            }
7893 7994
        }
7894 7995
        return typesEqual(pattern, evidence);
7895 7996
    }
7997 +
    if let case Type::ConstParameter(param) = pattern {
7998 +
        let case Type::ConstArgument { .. } = actual else return false;
7999 +
        for candidate, i in params {
8000 +
            if candidate == param {
8001 +
                if let prior = inferred[i] {
8002 +
                    return typesEqual(*prior, actual);
8003 +
                }
8004 +
                set inferred[i] = allocType(self, actual);
8005 +
                return true;
8006 +
            }
8007 +
        }
8008 +
        return typesEqual(pattern, actual);
8009 +
    }
7896 8010
    if typesEqual(pattern, actual) {
7897 8011
        return true;
7898 8012
    }
7899 8013
    if not containsGenericParameter(pattern) {
7900 8014
        return true;
7930 8044
                self, *inner, *actualInner, params, inferred
7931 8045
            );
7932 8046
        }
7933 8047
        case Type::Array(array) => {
7934 8048
            let case Type::Array(actualArray) = actual else return false;
7935 -
            return array.length == actualArray.length and inferGenericArgument(
8049 +
            return inferGenericArgument(
8050 +
                self, *array.length, *actualArray.length, params, inferred
8051 +
            ) and inferGenericArgument(
7936 8052
                self, *array.item, *actualArray.item, params, inferred
7937 8053
            );
7938 8054
        }
7939 8055
        case Type::GenericDataApply(application) => {
7940 8056
            let case Type::Nominal(nominal) = actual else return false;
7995 8111
        &mut inferred[..],
7996 8112
    ) {
7997 8113
        throw emitError(self, callee, ErrorKind::GenericInferenceConflict);
7998 8114
    }
7999 8115
    let a = alloc::arenaAllocator(&mut self.arena);
8000 -
    let mut args: *mut [*Type] = &mut [];
8116 +
    let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
8001 8117
    for _, i in template.params {
8002 8118
        let arg = inferred[i] else {
8003 8119
            throw emitError(
8004 8120
                self, callee, ErrorKind::GenericInferenceIncomplete
8005 8121
            );
8006 8122
        };
8007 -
        args.append(arg, a);
8123 +
        vec::append⟨*Type⟩(&mut args, arg, a);
8008 8124
    }
8009 -
    for arg, i in args {
8125 +
    let argsView = vec::view⟨*Type⟩(&args);
8126 +
    for arg, i in argsView {
8010 8127
        if not containsGenericParameter(*arg) {
8011 8128
            for bound in template.params[i].bounds {
8012 8129
                if findInstance(self, bound, *arg) == nil {
8013 8130
                    throw emitError(
8014 8131
                        self,
8017 8134
                    );
8018 8135
                }
8019 8136
            }
8020 8137
        }
8021 8138
    }
8022 -
    let sub = Substitution { params: template.params, args: &args[..] };
8139 +
    let sub = Substitution { params: template.params, args: argsView };
8023 8140
    let applied = try substituteType(self, Type::Fn(signature), &sub, callee);
8024 8141
    let case Type::Fn(appliedFn) = applied
8025 8142
        else throw emitError(self, callee, ErrorKind::Internal);
8026 8143
    let caller = currentGenericTemplateSymbol(self);
8027 8144
    if caller == nil {
8028 8145
        if let existing = findGenericFnSpecialization(
8029 -
            self, templateSym, &args[..]
8146 +
            self, templateSym, argsView
8030 8147
        ) {
8031 8148
            setNodeSymbol(self, callee, templateSym);
8032 8149
            setNodeType(self, callee, Type::Fn(existing.fnType));
8033 8150
            set self.nodeData.entries[callee.id].extra =
8034 8151
                NodeExtra::GenericFnCall(existing);
8035 8152
            return existing.fnType;
8036 8153
        }
8037 8154
    }
8038 8155
    recordGenericFnDependency(
8039 -
        self, callee, caller, templateSym, &args[..], appliedFn
8156 +
        self, callee, caller, templateSym, argsView, appliedFn
8040 8157
    );
8041 8158
    return appliedFn;
8042 8159
}
8043 8160
8044 8161
/// Resolve `Trait::method(receiver, ...)` for a rigid bounded parameter.
8089 8206
        self, Type::Fn(method.fnType), &sub, node
8090 8207
    );
8091 8208
    let case Type::Fn(methodFn) = substituted
8092 8209
        else throw emitError(self, node, ErrorKind::Internal);
8093 8210
    let a = alloc::arenaAllocator(&mut self.arena);
8094 -
    let mut params: *mut [*Type] = &mut [];
8095 -
    params.append(allocType(self, receiverTy), a);
8211 +
    let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
8212 +
    vec::append⟨*Type⟩(&mut params, allocType(self, receiverTy), a);
8096 8213
    for methodParam in methodFn.paramTypes {
8097 -
        params.append(methodParam, a);
8214 +
        vec::append⟨*Type⟩(&mut params, methodParam, a);
8098 8215
    }
8099 8216
    let fullFn = allocFnType(self, FnType {
8100 -
        paramTypes: &params[..],
8217 +
        paramTypes: vec::view⟨*Type⟩(&params),
8101 8218
        returnType: methodFn.returnType,
8102 8219
        throwList: methodFn.throwList,
8103 8220
        isUnsafe: methodFn.isUnsafe,
8104 8221
        localCount: 0,
8105 8222
    });
8120 8237
    call: ast::Call,
8121 8238
    ctx: CallCtx,
8122 8239
    expected: Type,
8123 8240
) -> Type throws (ResolveError)
8124 8241
{
8125 -
    // Intercept method calls on slices before inferring the callee.
8126 -
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
8127 -
        let parentTy = try infer(self, access.parent);
8128 -
        if isUnsafePointerType(parentTy) {
8129 -
            try requireUnsafe(self, access.parent);
8130 -
        }
8131 -
8132 -
        let subjectTy = autoDeref(parentTy);
8133 -
8134 -
        if let case Type::Slice(slice) = subjectTy {
8135 -
            let methodName = try nodeName(self, access.child);
8136 -
            if methodName == "append" {
8137 -
                return try resolveSliceAppend(
8138 -
                    self, node, access.parent, parentTy, call.args, slice.item, slice.mutable
8139 -
                );
8140 -
            }
8141 -
            if methodName == "delete" {
8142 -
                return try resolveSliceDelete(
8143 -
                    self, node, access.parent, call.args, slice.item, slice.mutable
8144 -
                );
8145 -
            }
8146 -
        }
8147 -
    }
8148 8242
    if let bounded = try resolveQualifiedGenericBoundCall(
8149 8243
        self, node, call, ctx
8150 8244
    ) {
8151 8245
        return bounded;
8152 8246
    }
8270 8364
8271 8365
    // Associate return type to call.
8272 8366
    return setNodeType(self, node, *info.returnType);
8273 8367
}
8274 8368
8275 -
/// Resolve `slice.append(val, allocator)`.
8276 -
fn resolveSliceAppend(
8277 -
    self: *mut Resolver,
8278 -
    node: *ast::Node,
8279 -
    parent: *ast::Node,
8280 -
    parentType: Type,
8281 -
    args: *mut [*ast::Node],
8282 -
    elemType: *Type,
8283 -
    mutable: bool
8284 -
) -> Type throws (ResolveError) {
8285 -
    if not mutable {
8286 -
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
8287 -
    }
8288 -
    if args.len <> 2 {
8289 -
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
8290 -
            expected: 2,
8291 -
            actual: args.len as u32,
8292 -
        }));
8293 -
    }
8294 -
    // First argument must be assignable to the element type.
8295 -
    try checkAssignable(self, args[0], *elemType);
8296 -
    // Second argument: the allocator. We accept any type -- the lowerer
8297 -
    // reads `.func` and `.ctx` at fixed offsets.
8298 -
    try visit(self, args[1], Type::Unknown);
8299 -
    recordGenericTypeUse(self, node, *elemType);
8300 -
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
8301 -
8302 -
    // Return the parent's type so the caller can rebind:
8303 -
    return setNodeType(self, node, parentType);
8304 -
}
8305 -
8306 -
/// Resolve `slice.delete(index)`.
8307 -
fn resolveSliceDelete(
8308 -
    self: *mut Resolver,
8309 -
    node: *ast::Node,
8310 -
    parent: *ast::Node,
8311 -
    args: *mut [*ast::Node],
8312 -
    elemType: *Type,
8313 -
    mutable: bool
8314 -
) -> Type throws (ResolveError) {
8315 -
    if not mutable {
8316 -
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
8317 -
    }
8318 -
    if args.len <> 1 {
8319 -
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
8320 -
            expected: 1,
8321 -
            actual: args.len as u32,
8322 -
        }));
8323 -
    }
8324 -
    try checkAssignable(self, args[0], Type::U32);
8325 -
    recordGenericTypeUse(self, node, *elemType);
8326 -
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType };
8327 -
8328 -
    return setNodeType(self, node, Type::Void);
8329 -
}
8330 -
8331 8369
/// Analyze an assignment expression.
8332 8370
fn resolveAssign(self: *mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
8333 8371
    throws (ResolveError)
8334 8372
{
8335 8373
    // Slice assignment: `slice[range] = value`.
8342 8380
            }
8343 8381
            let subjectTy = autoDeref(containerTy);
8344 8382
            try checkSliceRangeIndices(self, range);
8345 8383
8346 8384
            let mut item: *Type = undefined;
8347 -
            let mut capacity: ?u32 = nil;
8385 +
            let mut capacity: ?*Type = nil;
8348 8386
8349 8387
            if let case Type::Slice(slice) = subjectTy {
8350 8388
                if not slice.mutable {
8351 8389
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
8352 8390
                }
8353 8391
                set item = slice.item;
8354 8392
            } else {
8355 8393
                match subjectTy {
8356 8394
                    case Type::Array(a) => {
8357 -
                        try validateArraySliceBounds(self, range, a.length, node);
8395 +
                        if let length = concreteArrayLength(a.length) {
8396 +
                            try validateArraySliceBounds(self, range, length, node);
8397 +
                        }
8358 8398
                        set item = a.item;
8359 8399
                        set capacity = a.length;
8360 8400
                    }
8361 8401
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
8362 8402
                }
8476 8516
8477 8517
    match subjectTy {
8478 8518
        case Type::Array(arrayInfo) => {
8479 8519
            return setNodeType(self, node, *arrayInfo.item);
8480 8520
        }
8481 -
        case Type::GenericArray { item, .. } => {
8482 -
            return setNodeType(self, node, *item);
8483 -
        }
8484 8521
        else => {
8485 8522
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
8486 8523
        }
8487 8524
    }
8488 8525
}
8732 8769
        }
8733 8770
    }
8734 8771
    if expectedTy == Type::Unknown {
8735 8772
        throw emitError(self, node, ErrorKind::CannotInferType);
8736 8773
    };
8737 -
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
8774 +
    let arrayTy = Type::Array(ArrayType {
8775 +
        item: allocType(self, expectedTy),
8776 +
        length: allocConcreteArrayLength(self, length),
8777 +
    });
8738 8778
    return setNodeType(self, node, arrayTy);
8739 8779
}
8740 8780
8741 8781
/// Analyze an array repeat literal expression.
8742 8782
fn resolveArrayRepeat(self: *mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
8743 8783
    throws (ResolveError)
8744 8784
{
8745 8785
    let mut itemHint = hint;
8746 8786
    if let case Type::Array(ary) = hint {
8747 8787
        set itemHint = *ary.item;
8748 -
    } else if let case Type::GenericArray { item, .. } = hint {
8749 -
        set itemHint = *item;
8750 8788
    } else if let case Type::Optional(inner) = hint {
8751 8789
        if let case Type::Array(ary) = *inner {
8752 8790
            set itemHint = *ary.item;
8753 8791
        }
8754 8792
    }
8755 8793
    let valueTy = try visit(self, lit.item, itemHint);
8756 8794
    let _ = try checkNumeric(self, lit.count);
8757 -
    let mut arrayTy: Type = undefined;
8795 +
    let mut length: *Type = undefined;
8758 8796
    if let value = constValueEntry(self, lit.count) {
8759 8797
        if not validateConstIntRange(value, Type::U32) {
8760 8798
            throw emitError(self, lit.count, ErrorKind::NumericLiteralOverflow);
8761 8799
        }
8762 8800
        let case ConstValue::Int(int) = value
8763 8801
            else throw emitError(self, lit.count, ErrorKind::ConstExprRequired);
8764 -
        set arrayTy = Type::Array(ArrayType {
8765 -
            item: allocType(self, valueTy),
8766 -
            length: int.magnitude as u32,
8767 -
        });
8802 +
        set length = allocConcreteArrayLength(self, int.magnitude as u32);
8768 8803
    } else if isConstExpr(self, lit.count) and
8769 8804
              containsGenericConstExpr(self, lit.count)
8770 8805
    {
8771 -
        set arrayTy = Type::GenericArray {
8772 -
            item: allocType(self, valueTy),
8773 -
            length: lit.count,
8774 -
        };
8806 +
        set length = allocSymbolicArrayLength(self, lit.count);
8775 8807
    } else {
8776 8808
        throw emitError(self, lit.count, ErrorKind::ConstExprRequired);
8777 8809
    }
8778 -
    return setNodeType(self, node, arrayTy);
8810 +
    return setNodeType(self, node, Type::Array(ArrayType {
8811 +
        item: allocType(self, valueTy),
8812 +
        length,
8813 +
    }));
8779 8814
}
8780 8815
8781 8816
/// Resolve union variant access.
8782 8817
fn resolveUnionVariantAccess(
8783 8818
    self: *mut Resolver,
8920 8955
        }
8921 8956
        if mem::eq(fieldName, LEN_FIELD) {
8922 8957
            setRecordFieldIndex(self, fieldNode, 1);
8923 8958
            return setNodeType(self, node, Type::U32);
8924 8959
        }
8925 -
        if mem::eq(fieldName, CAP_FIELD) {
8926 -
            setRecordFieldIndex(self, fieldNode, 2);
8927 -
            return setNodeType(self, node, Type::U32);
8928 -
        }
8929 8960
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
8930 8961
    }
8931 8962
    if let case Type::TraitObject(traitObject) = subjectTy {
8932 8963
        let fieldName = try nodeName(self, access.child);
8933 8964
        let method = findTraitMethod(traitObject.traitInfo, fieldName)
8996 9027
        case Type::Array(arrayInfo) => {
8997 9028
            let fieldNode = access.child;
8998 9029
            let fieldName = try nodeName(self, fieldNode);
8999 9030
9000 9031
            if mem::eq(fieldName, LEN_FIELD) {
9001 -
                let lengthConst = constInt(arrayInfo.length as u64, 32, false, false);
9002 -
                setNodeConstValue(self, node, lengthConst);
9003 -
9004 -
                return setNodeType(self, node, Type::U32);
9005 -
            }
9006 -
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
9007 -
        }
9008 -
9009 -
        case Type::GenericArray { .. } => {
9010 -
            let fieldName = try nodeName(self, access.child);
9011 -
            if mem::eq(fieldName, LEN_FIELD) {
9032 +
                if let length = concreteArrayLength(arrayInfo.length) {
9033 +
                    setNodeConstValue(
9034 +
                        self, node, constInt(length as u64, 32, false, false)
9035 +
                    );
9036 +
                }
9012 9037
                return setNodeType(self, node, Type::U32);
9013 9038
            }
9014 -
9015 9039
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
9016 9040
        }
9017 9041
        else => {
9018 9042
            // Check for standalone methods on any nominal type (e.g. unions).
9019 9043
            if let case Type::Nominal(_) = subjectTy {
9139 9163
            let subjectTy = autoDeref(containerTy);
9140 9164
9141 9165
            try checkSliceRangeIndices(self, range);
9142 9166
9143 9167
            let mut item: *Type = undefined;
9144 -
            let mut capacity: ?u32 = nil;
9168 +
            let mut capacity: ?*Type = nil;
9145 9169
9146 9170
            if let case Type::Slice(slice) = subjectTy {
9147 9171
                if addr.mutable and not slice.mutable {
9148 9172
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
9149 9173
                }
9150 9174
                set item = slice.item;
9151 9175
            } else {
9152 9176
                match subjectTy {
9153 9177
                    case Type::Array(arrayInfo) => {
9154 -
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
9178 +
                        if let length = concreteArrayLength(arrayInfo.length) {
9179 +
                            try validateArraySliceBounds(self, range, length, node);
9180 +
                        }
9155 9181
                        set item = arrayInfo.item;
9156 9182
                        set capacity = arrayInfo.length;
9157 9183
                    }
9158 9184
                    else => {
9159 9185
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
9176 9202
        }
9177 9203
    }
9178 9204
    // Derive a hint for the target type from the slice hint.
9179 9205
    let mut targetHint: Type = Type::Unknown;
9180 9206
    if let case Type::Slice(slice) = hint {
9181 -
        set targetHint = Type::Array(ArrayType { item: slice.item, length: 0 });
9207 +
        set targetHint = Type::Array(ArrayType {
9208 +
            item: slice.item,
9209 +
            length: allocConcreteArrayLength(self, 0),
9210 +
        });
9182 9211
    }
9183 9212
    let targetTy = try visit(self, addr.target, targetHint);
9184 9213
9185 9214
    // Mark local variable symbols as address-taken so the lowerer
9186 9215
    // allocates a stack slot eagerly.
10007 10036
                else => panic "resolveTypeSyntax: invalid integer width",
10008 10037
            }
10009 10038
        }
10010 10039
        case ast::TypeSig::Array { itemType, length } => {
10011 10040
            let item = try resolveTypeSyntax(self, itemType, context);
10041 +
            let mut lengthType: *Type = undefined;
10012 10042
            match context {
10013 10043
                case TypeSyntaxContext::Concrete => {
10014 10044
                    let concreteLength = try checkSizeInt(self, length);
10015 -
                    set ty = Type::Array(ArrayType {
10016 -
                        item: allocType(self, item),
10017 -
                        length: concreteLength,
10018 -
                    });
10045 +
                    set lengthType = allocConcreteArrayLength(self, concreteLength);
10019 10046
                }
10020 10047
                case TypeSyntaxContext::Symbolic => {
10021 10048
                    let _ = try checkNumeric(self, length);
10022 10049
                    if let value = constValueEntry(self, length) {
10023 10050
                        if not validateConstIntRange(value, Type::U32) {
10028 10055
                        let case ConstValue::Int(int) = value else {
10029 10056
                            throw emitError(
10030 10057
                                self, length, ErrorKind::ConstExprRequired
10031 10058
                            );
10032 10059
                        };
10033 -
                        set ty = Type::Array(ArrayType {
10034 -
                            item: allocType(self, item),
10035 -
                            length: int.magnitude as u32,
10036 -
                        });
10060 +
                        set lengthType = allocConcreteArrayLength(
10061 +
                            self, int.magnitude as u32
10062 +
                        );
10037 10063
                    } else if isConstExpr(self, length) and
10038 10064
                              containsGenericConstExpr(self, length)
10039 10065
                    {
10040 -
                        set ty = Type::GenericArray {
10041 -
                            item: allocType(self, item),
10042 -
                            length,
10043 -
                        };
10066 +
                        set lengthType = allocSymbolicArrayLength(self, length);
10044 10067
                    } else {
10045 10068
                        throw emitError(
10046 10069
                            self, length, ErrorKind::ConstExprRequired
10047 10070
                        );
10048 10071
                    }
10049 10072
                }
10050 10073
            }
10074 +
            set ty = Type::Array(ArrayType {
10075 +
                item: allocType(self, item),
10076 +
                length: lengthType,
10077 +
            });
10051 10078
        }
10052 10079
        case ast::TypeSig::Slice { class, itemType, mutable } => {
10053 10080
            let item = try resolveTypeSyntax(self, itemType, context);
10054 10081
            set ty = Type::Slice(SliceType {
10055 10082
                class,
10085 10112
                                actual: app.args.len,
10086 10113
                            }),
10087 10114
                        );
10088 10115
                    }
10089 10116
                    let a = alloc::arenaAllocator(&mut self.arena);
10090 -
                    let mut args: *mut [*Type] = &mut [];
10117 +
                    let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
10091 10118
                    for argNode, i in app.args {
10092 10119
                        let arg = try resolveGenericArgument(
10093 10120
                            self, argNode, template.params[i]
10094 10121
                        );
10095 -
                        args.append(allocType(self, arg), a);
10122 +
                        vec::append⟨*Type⟩(&mut args, allocType(self, arg), a);
10096 10123
                    }
10097 10124
                    let symbolic = try! alloc::alloc(
10098 10125
                        &mut self.arena,
10099 10126
                        @sizeOf(GenericDataApplyType),
10100 10127
                        @alignOf(GenericDataApplyType),
10101 10128
                    ) as *mut GenericDataApplyType;
10102 10129
                    set *symbolic = GenericDataApplyType {
10103 10130
                        template: templateSym,
10104 -
                        args: &args[..],
10131 +
                        args: vec::view⟨*Type⟩(&args),
10105 10132
                        site: name,
10106 10133
                    };
10107 10134
                    return setNodeType(
10108 10135
                        self, node, Type::GenericDataApply(symbolic)
10109 10136
                    );
10164 10191
                        self, NominalType::Record(recordType)
10165 10192
                    ));
10166 10193
                }
10167 10194
                case TypeSyntaxContext::Symbolic => {
10168 10195
                    let a = alloc::arenaAllocator(&mut self.arena);
10169 -
                    let mut result: *mut [RecordField] = &mut [];
10196 +
                    let mut result = vec::Vec⟨RecordField⟩ {
10197 +
                        data: &mut [],
10198 +
                        len: 0,
10199 +
                    };
10170 10200
                    for field in fields {
10171 10201
                        let case ast::NodeValue::RecordField {
10172 10202
                            field: fieldNameNode,
10173 10203
                            type: typeNode,
10174 10204
                            value,
10186 10216
                        }
10187 10217
                        let mut fieldName: ?*[u8] = nil;
10188 10218
                        if let nameNode = fieldNameNode {
10189 10219
                            set fieldName = try nodeName(self, nameNode);
10190 10220
                        }
10191 -
                        result.append(RecordField {
10221 +
                        vec::append⟨RecordField⟩(&mut result, RecordField {
10192 10222
                            name: fieldName,
10193 10223
                            fieldType,
10194 10224
                            offset: -1,
10195 10225
                        }, a);
10196 10226
                    }
10198 10228
                        &mut self.arena,
10199 10229
                        @sizeOf(GenericRecordType),
10200 10230
                        @alignOf(GenericRecordType),
10201 10231
                    ) as *mut GenericRecordType;
10202 10232
                    set *genericRecord = GenericRecordType {
10203 -
                        fields: &result[..],
10233 +
                        fields: vec::view⟨RecordField⟩(&result),
10204 10234
                        labeled,
10205 10235
                    };
10206 10236
                    set ty = Type::GenericRecord(genericRecord);
10207 10237
                }
10208 10238
            }
10223 10253
                        actual: signature.throwList.len,
10224 10254
                    }
10225 10255
                ));
10226 10256
            }
10227 10257
            let a = alloc::arenaAllocator(&mut self.arena);
10228 -
            let mut params: *mut [*Type] = &mut [];
10229 -
            let mut throwTypes: *mut [*Type] = &mut [];
10258 +
            let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
10259 +
            let mut throwTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 };
10230 10260
            for param in signature.params {
10231 10261
                let paramType = try resolveContextualValueType(
10232 10262
                    self, param, context
10233 10263
                );
10234 -
                params.append(allocType(self, paramType), a);
10264 +
                vec::append⟨*Type⟩(&mut params, allocType(self, paramType), a);
10235 10265
            }
10236 10266
            for throwNode in signature.throwList {
10237 10267
                let throwType = try resolveContextualValueType(
10238 10268
                    self, throwNode, context
10239 10269
                );
10240 10270
                try ensureStorableType(self, throwNode, throwType);
10241 -
                throwTypes.append(allocType(self, throwType), a);
10271 +
                vec::append⟨*Type⟩(&mut throwTypes, allocType(self, throwType), a);
10242 10272
            }
10243 10273
            let mut returnType = allocType(self, Type::Void);
10244 10274
            if let returnNode = signature.returnType {
10245 10275
                let resolved = try resolveContextualValueType(
10246 10276
                    self, returnNode, context
10247 10277
                );
10248 10278
                try ensureStorableType(self, returnNode, resolved);
10249 10279
                set returnType = allocType(self, resolved);
10250 10280
            }
10251 10281
            set ty = Type::Fn(allocFnType(self, FnType {
10252 -
                paramTypes: &params[..],
10282 +
                paramTypes: vec::view⟨*Type⟩(&params),
10253 10283
                returnType,
10254 -
                throwList: &throwTypes[..],
10284 +
                throwList: vec::view⟨*Type⟩(&throwTypes),
10255 10285
                isUnsafe: false,
10256 10286
                localCount: 0,
10257 10287
            }));
10258 10288
        }
10259 10289
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
10299 10329
export fn resolveExpr(
10300 10330
    self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena
10301 10331
) -> Diagnostics throws (ResolveError) {
10302 10332
    let a = alloc::arenaAllocator(&mut arena.arena);
10303 10333
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
10304 -
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
10305 -
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
10334 +
    let mut bodyStmts = ast::nodeVec(arena, 1);
10335 +
    vec::append⟨*ast::Node⟩(&mut bodyStmts, exprStmt, a);
10336 +
    let module = ast::synthFnModule(
10337 +
        arena,
10338 +
        ANALYZE_EXPR_FN_NAME,
10339 +
        vec::viewMut⟨*ast::Node⟩(&mut bodyStmts),
10340 +
    );
10306 10341
10307 10342
    let case ast::NodeValue::Block(block) = module.modBody.value
10308 10343
        else panic "resolveExpr: expected block for module body";
10309 10344
    enterScope(self, module.modBody);
10310 10345
    try resolveModuleDecls(self, &block) catch {
10311 -
        return Diagnostics { errors: self.errors };
10346 +
        return diagnostics(self);
10312 10347
    };
10313 10348
    try resolveModuleDefs(self, &block) catch {
10314 -
        return Diagnostics { errors: self.errors };
10349 +
        return diagnostics(self);
10315 10350
    };
10316 10351
    exitScope(self);
10317 10352
10318 -
    return Diagnostics { errors: self.errors };
10353 +
    return diagnostics(self);
10319 10354
}
10320 10355
10321 10356
/// Analyze a parsed module root, ie. a block of top-level statements.
10322 10357
export fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
10323 10358
    let case ast::NodeValue::Block(block) = root.value
10324 10359
        else panic "resolveModuleRoot: expected block for module root";
10325 10360
10326 10361
    enterScope(self, root);
10327 10362
    try resolveModuleDecls(self, &block) catch {
10328 -
        return Diagnostics { errors: self.errors };
10363 +
        return diagnostics(self);
10329 10364
    };
10330 10365
    try resolveModuleDefs(self, &block) catch {
10331 -
        return Diagnostics { errors: self.errors };
10366 +
        return diagnostics(self);
10332 10367
    };
10333 10368
    exitScope(self);
10334 10369
    setNodeType(self, root, Type::Void);
10335 10370
10336 10371
    try closeGenericFnSpecializations(self) catch {
10337 -
        return Diagnostics { errors: self.errors };
10372 +
        return diagnostics(self);
10338 10373
    };
10339 10374
    try materializeGenericFnTypeUses(self) catch {
10340 -
        return Diagnostics { errors: self.errors };
10375 +
        return diagnostics(self);
10341 10376
    };
10342 10377
    try validateGenericDataRoots(self) catch {
10343 -
        return Diagnostics { errors: self.errors };
10378 +
        return diagnostics(self);
10344 10379
    };
10345 -
    return Diagnostics { errors: self.errors };
10380 +
    return diagnostics(self);
10346 10381
}
10347 10382
10348 10383
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
10349 10384
/// and scopes for them, and also binds type names in each module so that cross-module
10350 10385
/// type references work regardless of declaration order.
10855 10890
        let mut receiverClass = types::PointerClass::Unsafe;
10856 10891
        let mut receiverMutable = false;
10857 10892
        let mut haveReceiver = false;
10858 10893
        match checker.resolver.nodeData.entries[node.id].extra {
10859 10894
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
10860 -
                let method = &traitInfo.methods[methodIndex];
10895 +
                let method = &traitInfo.methods.data[methodIndex];
10861 10896
                set receiverClass = method.receiverClass;
10862 10897
                set receiverMutable = method.mutable;
10863 10898
                set haveReceiver = true;
10864 10899
            }
10865 10900
            case NodeExtra::GenericBoundMethodCall {
10866 10901
                traitInfo, methodIndex, explicitReceiver, ..
10867 10902
            } => {
10868 10903
                if not explicitReceiver {
10869 -
                    let method = &traitInfo.methods[methodIndex];
10904 +
                    let method = &traitInfo.methods.data[methodIndex];
10870 10905
                    set receiverClass = method.receiverClass;
10871 10906
                    set receiverMutable = method.mutable;
10872 10907
                    set haveReceiver = true;
10873 10908
                }
10874 10909
            }
11449 11484
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
11450 11485
        if not success(&diags) {
11451 11486
            return diags;
11452 11487
        }
11453 11488
        try closeGenericFnSpecializations(self) catch {
11454 -
            return Diagnostics { errors: self.errors };
11489 +
            return diagnostics(self);
11455 11490
        };
11456 11491
        try materializeGenericFnTypeUses(self) catch {
11457 -
            return Diagnostics { errors: self.errors };
11492 +
            return diagnostics(self);
11458 11493
        };
11459 11494
    }
11460 11495
    // Data roots are validated after every package and reachable generic body
11461 11496
    // has had a chance to root its concrete specialization dependencies.
11462 11497
    try validateGenericDataRoots(self) catch {
11463 -
        return Diagnostics { errors: self.errors };
11498 +
        return diagnostics(self);
11464 11499
    };
11465 -
    return Diagnostics { errors: self.errors };
11500 +
    return diagnostics(self);
11466 11501
}
11467 11502
11468 11503
/// Resolve a package.
11469 11504
fn resolvePackage(self: *mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
11470 11505
    let rootId = rootEntry.id;
11481 11516
        else panic "resolvePackage: expected block for module root";
11482 11517
11483 11518
    // Module graph analysis phase: bind all module name symbols and scopes.
11484 11519
    try resolveModuleGraph(self, &block) catch {
11485 11520
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11486 -
        return Diagnostics { errors: self.errors };
11521 +
        return diagnostics(self);
11487 11522
    };
11488 11523
11489 11524
    // Declaration phase: bind all names and analyze top-level declarations.
11490 11525
    try resolveModuleDecls(self, &block) catch {
11491 11526
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11492 11527
    };
11493 11528
    if self.errors.len > 0 {
11494 -
        return Diagnostics { errors: self.errors };
11529 +
        return diagnostics(self);
11495 11530
    }
11496 11531
11497 11532
    // Definition phase: analyze function bodies and sub-module definitions.
11498 11533
    try resolveModuleDefs(self, &block) catch {
11499 11534
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11500 11535
    };
11501 11536
    setNodeType(self, node, Type::Void);
11502 11537
11503 -
    return Diagnostics { errors: self.errors };
11538 +
    return diagnostics(self);
11504 11539
}
lib/std/lang/resolver/printer.rad +5 -6
116 116
        }
117 117
        case super::Type::Array(array) => {
118 118
            io::print("[");
119 119
            printTypeBody(*array.item, brief);
120 120
            io::print("; ");
121 -
            io::printU32(array.length);
121 +
            if let length = super::concreteArrayLength(array.length) {
122 +
                io::printU32(length);
123 +
            } else {
124 +
                printTypeBody(*array.length, brief);
125 +
            }
122 126
            io::print("]");
123 127
        }
124 -
        case super::Type::GenericArray { item, .. } => {
125 -
            io::print("[");
126 -
            printTypeBody(*item, brief);
127 -
            io::print("; <const>]");
128 -
        }
129 128
        case super::Type::Optional(inner) => {
130 129
            io::print("?");
131 130
            printTypeBody(*inner, brief);
132 131
        }
133 132
        case super::Type::Fn(fnType) => {
lib/std/lang/resolver/tests.rad +25 -152
7 7
use std::lang::types;
8 8
use std::lang::parser;
9 9
use std::lang::scanner;
10 10
use std::lang::module;
11 11
use std::lang::strings;
12 +
use std::lang::vec;
12 13
13 14
/// Synthetic file path used for resolver tests.
14 15
constant MODULE_PATH: *[u8] = "/dev/test.rad";
15 16
16 17
/// AST arena storage used by resolver tests.
88 89
fn resolveStatements(
89 90
    self: *mut super::Resolver, block: ast::Block, arena: *mut ast::NodeArena
90 91
) -> TestResult throws (super::ResolveError) {
91 92
    let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements);
92 93
    let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch {
93 -
        return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, root: module.modBody };
94 +
        return TestResult {
95 +
            diagnostics: super::Diagnostics {
96 +
                errors: vec::viewMut⟨super::Error⟩(&mut self.errors),
97 +
            },
98 +
            root: module.modBody,
99 +
        };
94 100
    };
95 101
    return TestResult { diagnostics, root: module.fnBody };
96 102
}
97 103
98 104
/// Parse and analyze an expression string for testing.
450 456
fn expectArrayType(ty: super::Type, length: u32) -> super::Type
451 457
    throws (testing::TestError)
452 458
{
453 459
    let case super::Type::Array(info) = ty
454 460
        else throw testing::TestError::Failed;
455 -
    try testing::expect(info.length == length);
461 +
    let actualLength = super::concreteArrayLength(info.length)
462 +
        else throw testing::TestError::Failed;
463 +
    try testing::expect(actualLength == length);
456 464
457 465
    return *info.item;
458 466
}
459 467
460 468
/// Require a slice type and return its element type.
3816 3824
    let arrStmt = try getBlockStmt(result.root, 1);
3817 3825
    let sym = super::symbolFor(&a, arrStmt)
3818 3826
        else throw testing::TestError::Failed;
3819 3827
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
3820 3828
        else throw testing::TestError::Failed;
3821 -
    try testing::expect(arrType.length == 3);
3829 +
    let actualLength = super::concreteArrayLength(arrType.length)
3830 +
        else throw testing::TestError::Failed;
3831 +
    try testing::expect(actualLength == 3);
3822 3832
}
3823 3833
3824 3834
/// Test that a record field can use a constant as its array length.
3825 3835
@test fn testRecordFieldWithConstArrayLength() throws (testing::TestError) {
3826 3836
    let mut a = testResolver();
4264 4274
        let case super::ErrorKind::TypeMismatch(_) = err.kind
4265 4275
            else throw testing::TestError::Failed;
4266 4276
    }
4267 4277
}
4268 4278
4269 -
/// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds.
4270 -
@test fn testResolveSliceOfWithCap() throws (testing::TestError) {
4271 -
    {
4272 -
        let mut a = testResolver();
4273 -
        let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4274 -
        let result = try resolveProgramStr(&mut a, program);
4275 -
        try expectNoErrors(&result);
4276 -
    }
4277 -
    // Mutable pointer produces mutable slice.
4278 -
    {
4279 -
        let mut a = testResolver();
4280 -
        let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }";
4281 -
        let result = try resolveProgramStr(&mut a, program);
4282 -
        try expectNoErrors(&result);
4283 -
    }
4284 -
}
4285 -
4286 -
/// Test @sliceOf with 3 arguments but wrong cap type.
4287 -
@test fn testResolveSliceOfCapWrongType() throws (testing::TestError) {
4288 -
    let mut a = testResolver();
4289 -
    let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4290 -
    let result = try resolveProgramStr(&mut a, program);
4291 -
    let err = try expectError(&result);
4292 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4293 -
        else throw testing::TestError::Failed;
4294 -
}
4295 -
4296 -
/// Test .cap field access on slices resolves to u32.
4297 -
@test fn testResolveSliceCapField() throws (testing::TestError) {
4298 -
    let mut a = testResolver();
4299 -
    let program = "fn f(s: *[u8]) -> u32 { return s.cap; }";
4300 -
    let result = try resolveProgramStr(&mut a, program);
4301 -
    try expectNoErrors(&result);
4302 -
}
4303 -
4304 -
/// Test `.append()` on immutable slice produces an error.
4305 -
@test fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4306 -
    let mut a = testResolver();
4307 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }";
4308 -
    let result = try resolveProgramStr(&mut a, program);
4309 -
    let err = try expectError(&result);
4310 -
    let case super::ErrorKind::ImmutableBinding = err.kind
4311 -
        else throw testing::TestError::Failed;
4312 -
}
4313 -
4314 -
/// Test `.append()` with wrong argument count produces an error.
4315 -
@test fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4316 -
    // Too few arguments.
4317 -
    {
4318 -
        let mut a = testResolver();
4319 -
        let program = "fn f(s: *mut [i32]) { s.append(1); }";
4320 -
        let result = try resolveProgramStr(&mut a, program);
4321 -
        let err = try expectError(&result);
4322 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4323 -
            else throw testing::TestError::Failed;
4324 -
        try testing::expect(m.expected == 2);
4325 -
        try testing::expect(m.actual == 1);
4326 -
    }
4327 -
    // Too many arguments.
4328 -
    {
4329 -
        let mut a = testResolver();
4330 -
        let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }";
4331 -
        let result = try resolveProgramStr(&mut a, program);
4332 -
        let err = try expectError(&result);
4333 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4334 -
            else throw testing::TestError::Failed;
4335 -
        try testing::expect(m.expected == 2);
4336 -
        try testing::expect(m.actual == 3);
4337 -
    }
4338 -
}
4339 -
4340 -
/// Test `.append()` with correct arguments succeeds.
4341 -
@test fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4342 -
    let mut a = testResolver();
4343 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }";
4344 -
    let result = try resolveProgramStr(&mut a, program);
4345 -
    try expectNoErrors(&result);
4346 -
}
4347 -
4348 -
/// Test `.append()` with wrong element type produces an error.
4349 -
@test fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4350 -
    let mut a = testResolver();
4351 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }";
4352 -
    let result = try resolveProgramStr(&mut a, program);
4353 -
    let err = try expectError(&result);
4354 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4355 -
        else throw testing::TestError::Failed;
4356 -
}
4357 -
4358 -
/// Test `.delete()` on immutable slice produces an error.
4359 -
@test fn testResolveSliceDeleteImmutable() throws (testing::TestError) {
4360 -
    let mut a = testResolver();
4361 -
    let program = "fn f(s: *[i32]) { s.delete(0); }";
4362 -
    let result = try resolveProgramStr(&mut a, program);
4363 -
    let err = try expectError(&result);
4364 -
    let case super::ErrorKind::ImmutableBinding = err.kind
4365 -
        else throw testing::TestError::Failed;
4366 -
}
4367 -
4368 -
/// Test `.delete()` with wrong argument count produces an error.
4369 -
@test fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) {
4370 -
    // No arguments.
4371 -
    {
4372 -
        let mut a = testResolver();
4373 -
        let program = "fn f(s: *mut [i32]) { s.delete(); }";
4374 -
        let result = try resolveProgramStr(&mut a, program);
4375 -
        let err = try expectError(&result);
4376 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4377 -
            else throw testing::TestError::Failed;
4378 -
        try testing::expect(m.expected == 1);
4379 -
        try testing::expect(m.actual == 0);
4380 -
    }
4381 -
    // Too many arguments.
4382 -
    {
4383 -
        let mut a = testResolver();
4384 -
        let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }";
4385 -
        let result = try resolveProgramStr(&mut a, program);
4386 -
        let err = try expectError(&result);
4387 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4388 -
            else throw testing::TestError::Failed;
4389 -
        try testing::expect(m.expected == 1);
4390 -
        try testing::expect(m.actual == 2);
4391 -
    }
4392 -
}
4393 -
4394 -
/// Test `.delete()` with correct arguments succeeds.
4395 -
@test fn testResolveSliceDeleteCorrect() throws (testing::TestError) {
4396 -
    let mut a = testResolver();
4397 -
    let program = "fn f(s: *mut [i32]) { s.delete(0); }";
4398 -
    let result = try resolveProgramStr(&mut a, program);
4399 -
    try expectNoErrors(&result);
4400 -
}
4401 -
4402 -
/// Test `.delete()` with wrong argument type produces an error.
4403 -
@test fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) {
4404 -
    let mut a = testResolver();
4405 -
    let program = "fn f(s: *mut [i32]) { s.delete(true); }";
4406 -
    let result = try resolveProgramStr(&mut a, program);
4407 -
    let err = try expectError(&result);
4408 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4409 -
        else throw testing::TestError::Failed;
4410 -
}
4411 -
4412 4279
/// Test `match &opt` produces immutable pointer bindings.
4413 4280
@test fn testResolveMatchRefUnionBinding() throws (testing::TestError) {
4414 4281
    let mut a = testResolver();
4415 4282
    let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }";
4416 4283
    let result = try resolveProgramStr(&mut a, program);
5116 4983
    let arrStmt = try getBlockStmt(result.root, 3);
5117 4984
    let sym = super::symbolFor(&a, arrStmt)
5118 4985
        else throw testing::TestError::Failed;
5119 4986
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5120 4987
        else throw testing::TestError::Failed;
5121 -
    try testing::expect(arrType.length == 5);
4988 +
    let actualLength = super::concreteArrayLength(arrType.length)
4989 +
        else throw testing::TestError::Failed;
4990 +
    try testing::expect(actualLength == 5);
5122 4991
}
5123 4992
5124 4993
/// Test cross-module constant expression: a constant in one module references
5125 4994
/// a constant from another module via scope access.
5126 4995
@test fn testCrossModuleConstExpr() throws (testing::TestError) {
5199 5068
    let arrStmt = try getBlockStmt(result.root, 2);
5200 5069
    let sym = super::symbolFor(&a, arrStmt)
5201 5070
        else throw testing::TestError::Failed;
5202 5071
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5203 5072
        else throw testing::TestError::Failed;
5204 -
    try testing::expect(arrType.length == 4);
5073 +
    let actualLength = super::concreteArrayLength(arrType.length)
5074 +
        else throw testing::TestError::Failed;
5075 +
    try testing::expect(actualLength == 4);
5205 5076
}
5206 5077
5207 5078
/// Test unsuffixed integer literals in constant expressions.
5208 5079
@test fn testConstExprUnsuffixedLiterals() throws (testing::TestError) {
5209 5080
    try expectConstFold("constant A: u32 = 4 * 4;", 0, 16);
5688 5559
        else throw testing::TestError::Failed;
5689 5560
    let case super::NominalType::Record(recordType) = *nominal
5690 5561
        else throw testing::TestError::Failed;
5691 5562
    let case super::Type::Array(arrayType) = recordType.fields[0].fieldType
5692 5563
        else throw testing::TestError::Failed;
5693 -
    assert arrayType.length == 4;
5564 +
    let actualLength = super::concreteArrayLength(arrayType.length)
5565 +
        else throw testing::TestError::Failed;
5566 +
    assert actualLength == 4;
5694 5567
    assert recordType.layout.size == 4;
5695 5568
}
5696 5569
5697 5570
/// Equivalent integer expressions share a canonical specialization.
5698 5571
@test fn testGenericConstParameterCanonical() throws (testing::TestError) {
5790 5663
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
5791 5664
        else throw testing::TestError::Failed;
5792 5665
    assert mem::eq(name, "T");
5793 5666
}
5794 5667
5795 -
/// Layout-dependent builtins reject symbolic generic types.
5796 -
@test fn testGenericParameterLayoutRejected() throws (testing::TestError) {
5668 +
/// Layout-dependent builtins accept symbolic generic types.
5669 +
@test fn testGenericParameterLayoutAccepted() throws (testing::TestError) {
5797 5670
    let mut a = testResolver();
5798 5671
    let result = try resolveProgramStr(
5799 5672
        &mut a,
5800 5673
        "record Sized⟨T⟩ { bytes: u32 = @sizeOf(T) }",
5801 5674
    );
5802 -
    try expectErrorKind(&result, super::ErrorKind::GenericLayoutRequired);
5675 +
    try expectNoErrors(&result);
5803 5676
}
5804 5677
5805 5678
/// Generic data declarations cannot be used without specialization arguments.
5806 5679
@test fn testGenericArgumentsRequired() throws (testing::TestError) {
5807 5680
    let mut a = testResolver();
lib/std/lang/scanner.rad +1 -3
228 228
export fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *mut strings::Pool) -> Scanner {
229 229
    // Intern built-in functions and attributes.
230 230
    strings::intern(pool, "@sizeOf");
231 231
    strings::intern(pool, "@alignOf");
232 232
    strings::intern(pool, "@sliceOf");
233 +
    strings::intern(pool, "@relocate");
233 234
    strings::intern(pool, "@default");
234 235
    strings::intern(pool, "@intrinsic");
235 236
    strings::intern(pool, "@test");
236 -
    // Intern built-in slice methods.
237 -
    strings::intern(pool, "append");
238 -
    strings::intern(pool, "delete");
239 237
240 238
    return Scanner { sourceLoc, source, token: 0, cursor: 0, pool };
241 239
}
242 240
243 241
/// Check if we've reached the end of input.
lib/std/lang/slice.rad added +11 -0
1 +
//! Typed slice construction from raw pointers.
2 +
3 +
/// Construct an immutable slice from a pointer and element count.
4 +
export fn fromRawParts⟨T⟩(ptr: *T, len: u32) -> *[T] {
5 +
    return @sliceOf(ptr, len);
6 +
}
7 +
8 +
/// Construct a mutable slice from a pointer and element count.
9 +
export fn fromRawPartsMut⟨T⟩(ptr: *mut T, len: u32) -> *mut [T] {
10 +
    return @sliceOf(ptr, len);
11 +
}
lib/std/lang/vec.rad added +151 -0
1 +
//! Typed vectors over caller-provided or allocator-grown storage.
2 +
3 +
use std::lang::alloc;
4 +
use std::lang::slice;
5 +
6 +
/// Typed vector metadata with initialized length and storage capacity.
7 +
export record Vec⟨T⟩ {
8 +
    /// Typed slice containing the vector's storage.
9 +
    data: *mut [T],
10 +
    /// Number of initialized elements.
11 +
    len: u32,
12 +
}
13 +
14 +
/// Initialize a vector over caller-owned storage.
15 +
export fn init⟨T⟩(vec: *mut Vec⟨T⟩, arena: *mut [T]) {
16 +
    set vec.data = arena;
17 +
    set vec.len = 0;
18 +
}
19 +
20 +
/// Return the number of initialized elements.
21 +
export fn len⟨T⟩(vec: *Vec⟨T⟩) -> u32 {
22 +
    return vec.len;
23 +
}
24 +
25 +
/// Return the storage capacity in elements.
26 +
export fn capacity⟨T⟩(vec: *Vec⟨T⟩) -> u32 {
27 +
    return vec.data.len;
28 +
}
29 +
30 +
/// Return a view containing the initialized elements.
31 +
export fn view⟨T⟩(vec: *Vec⟨T⟩) -> *[T] {
32 +
    return slice::fromRawParts⟨T⟩(vec.data.ptr, vec.len);
33 +
}
34 +
35 +
/// Return a mutable view containing the initialized elements.
36 +
export fn viewMut⟨T⟩(vec: *mut Vec⟨T⟩) -> *mut [T] {
37 +
    return slice::fromRawPartsMut⟨T⟩(vec.data.ptr, vec.len);
38 +
}
39 +
40 +
/// Reset a vector without clearing its storage.
41 +
export fn reset⟨T⟩(vec: *mut Vec⟨T⟩) {
42 +
    set vec.len = 0;
43 +
}
44 +
45 +
/// Return a typed pointer to an element, or `nil` when out of bounds.
46 +
export fn get⟨T⟩(vec: *Vec⟨T⟩, index: u32) -> ?*T {
47 +
    if index >= vec.len {
48 +
        return nil;
49 +
    }
50 +
51 +
    return &vec.data[index];
52 +
}
53 +
54 +
/// Append an element, returning `false` when the vector is full.
55 +
export fn push⟨T⟩(vec: *mut Vec⟨T⟩, value: T) -> bool {
56 +
    if vec.len >= vec.data.len {
57 +
        return false;
58 +
    }
59 +
60 +
    set vec.data[vec.len] = value;
61 +
    set vec.len += 1;
62 +
63 +
    return true;
64 +
}
65 +
66 +
/// Move bytes between equally bounded, potentially overlapping storage regions.
67 +
fn relocateBytes(destination: *mut [u8], source: *mut [u8]) {
68 +
    @relocate(destination, source);
69 +
}
70 +
71 +
/// Allocate grown storage and move the initialized bytes into it.
72 +
fn growStorage(
73 +
    data: *mut u8,
74 +
    initializedBytes: u32,
75 +
    capacityBytes: u32,
76 +
    alignment: u32,
77 +
    allocator: alloc::Allocator,
78 +
) -> *mut u8 {
79 +
    if capacityBytes == 0 {
80 +
        return data;
81 +
    }
82 +
    let grown = allocator.func(allocator.ctx, capacityBytes, alignment) as *mut u8;
83 +
    if initializedBytes > 0 {
84 +
        relocateBytes(
85 +
            slice::fromRawPartsMut⟨u8(grown, initializedBytes),
86 +
            slice::fromRawPartsMut⟨u8(data, initializedBytes),
87 +
        );
88 +
    }
89 +
    return grown;
90 +
}
91 +
92 +
/// Append an element, growing the vector when it is full.
93 +
export fn append⟨T⟩(vec: *mut Vec⟨T⟩, value: T, allocator: alloc::Allocator) {
94 +
    if vec.len == vec.data.len {
95 +
        let capacity = (vec.data.len << 1) | 1;
96 +
        let itemSize = @sizeOf(T);
97 +
        let data = growStorage(
98 +
            vec.data.ptr as *mut opaque as *mut u8,
99 +
            vec.len * itemSize,
100 +
            capacity * itemSize,
101 +
            @alignOf(T),
102 +
            allocator,
103 +
        ) as *mut opaque as *mut T;
104 +
        set vec.data = slice::fromRawPartsMut⟨T⟩(data, capacity);
105 +
    }
106 +
107 +
    set vec.data[vec.len] = value;
108 +
    set vec.len += 1;
109 +
}
110 +
111 +
/// Replace an initialized element, returning `false` when out of bounds.
112 +
export fn put⟨T⟩(vec: *mut Vec⟨T⟩, index: u32, value: T) -> bool {
113 +
    if index >= vec.len {
114 +
        return false;
115 +
    }
116 +
117 +
    set vec.data[index] = value;
118 +
    return true;
119 +
}
120 +
121 +
/// Remove and return an initialized element while preserving later element order.
122 +
export fn remove⟨T⟩(vec: *mut Vec⟨T⟩, index: u32) -> T {
123 +
    assert index < vec.len;
124 +
125 +
    let removed = vec.data[index];
126 +
    let tailBytes = (vec.len - index - 1) * @sizeOf(T);
127 +
    if tailBytes > 0 {
128 +
        relocateBytes(
129 +
            slice::fromRawPartsMut⟨u8(
130 +
                &mut vec.data[index] as *mut opaque as *mut u8,
131 +
                tailBytes,
132 +
            ),
133 +
            slice::fromRawPartsMut⟨u8(
134 +
                &mut vec.data[index + 1] as *mut opaque as *mut u8,
135 +
                tailBytes,
136 +
            ),
137 +
        );
138 +
    }
139 +
140 +
    set vec.len -= 1;
141 +
    return removed;
142 +
}
143 +
144 +
/// Remove and return the last element, or `nil` when empty.
145 +
export fn pop⟨T⟩(vec: *mut Vec⟨T⟩) -> ?T {
146 +
    if vec.len == 0 {
147 +
        return nil;
148 +
    }
149 +
    set vec.len -= 1;
150 +
    return vec.data[vec.len];
151 +
}
lib/std/slice.rad added +5 -0
1 +
//! Public typed slice API.
2 +
//!
3 +
//! The implementation lives in `std::lang::slice` so language modules can
4 +
//! resolve it before this stable top-level facade.
5 +
export use std::lang::slice::*;
lib/std/tests.rad +146 -5
1 1
//! Tests for formatting functions.
2 2
3 +
use std::lang::alloc;
3 4
use std::fmt;
4 5
use std::mem;
5 6
use std::vec;
7 +
use std::slice;
6 8
use std::testing;
7 9
use std::sys::unix;
8 10
9 11
/// Mixed-width value used by vector tests.
10 12
record Obj {
12 14
    wide: u64,
13 15
    /// Narrow field.
14 16
    narrow: u8,
15 17
}
16 18
19 +
/// Zero-sized value used by vector growth and removal tests.
20 +
record ZeroSized {}
21 +
17 22
// fmt /////////////////////////////////////////////////////////////////////////
18 23
19 24
@test fn testFormatU32Zero() throws (testing::TestError) {
20 25
    let mut buffer: [u8; 11] = [0; 11];
21 26
    let result: *[u8] = fmt::formatU32(0, &mut buffer[..]);
339 344
    try testing::expect(mem::cmp("abcd", "abc") > 0);
340 345
}
341 346
342 347
// vec /////////////////////////////////////////////////////////////////////////
343 348
344 -
instantiate vec::Vec⟨i32⟩,
345 -
            vec::init⟨i32⟩,
349 +
instantiate vec::init⟨i32⟩,
346 350
            vec::len⟨i32⟩,
347 351
            vec::capacity⟨i32⟩,
348 352
            vec::reset⟨i32⟩,
349 353
            vec::get⟨i32⟩,
354 +
            vec::viewMut⟨i32⟩,
350 355
            vec::push⟨i32⟩,
351 356
            vec::put⟨i32⟩,
352 -
            vec::pop⟨i32⟩;
357 +
            vec::pop⟨i32⟩,
358 +
            vec::append⟨i32⟩,
359 +
            vec::remove⟨i32⟩,
360 +
            vec::view⟨i32⟩;
361 +
362 +
instantiate vec::init⟨u8⟩,
363 +
            vec::append⟨u8⟩,
364 +
            vec::view⟨u8⟩;
353 365
354 -
instantiate vec::Vec⟨Obj⟩,
355 -
            vec::init⟨Obj⟩,
366 +
instantiate vec::init⟨Obj⟩,
356 367
            vec::get⟨Obj⟩,
357 368
            vec::push⟨Obj⟩,
358 369
            vec::put⟨Obj⟩;
359 370
371 +
instantiate vec::init⟨ZeroSized⟩,
372 +
            vec::append⟨ZeroSized⟩,
373 +
            vec::remove⟨ZeroSized⟩,
374 +
            vec::view⟨ZeroSized⟩;
375 +
360 376
@test fn testVecInitialState() throws (testing::TestError) {
361 377
    let mut arena: [i32; 4] = undefined;
362 378
    let mut v: vec::Vec⟨i32= undefined;
363 379
    vec::init⟨i32(&mut v, &mut arena[..]);
364 380
386 402
        else throw testing::TestError::Failed;
387 403
    assert *replaced == 999;
388 404
    assert not vec::put⟨i32(&mut v, 3, 1);
389 405
}
390 406
407 +
@test fn testVecMutableViewAliasesStorage() throws (testing::TestError) {
408 +
    let mut arena: [i32; 2] = undefined;
409 +
    let mut v: vec::Vec⟨i32= undefined;
410 +
    vec::init⟨i32(&mut v, &mut arena[..]);
411 +
412 +
    assert vec::push⟨i32(&mut v, 10);
413 +
    assert vec::push⟨i32(&mut v, 20);
414 +
415 +
    let mut view = vec::viewMut⟨i32(&mut v);
416 +
    assert view.len == 2;
417 +
    set view[1] = 999;
418 +
419 +
    let updated = vec::get⟨i32(&v, 1)
420 +
        else throw testing::TestError::Failed;
421 +
    assert *updated == 999;
422 +
}
423 +
391 424
@test fn testVecPopReset() throws (testing::TestError) {
392 425
    let mut arena: [i32; 2] = undefined;
393 426
    let mut v: vec::Vec⟨i32= undefined;
394 427
    vec::init⟨i32(&mut v, &mut arena[..]);
395 428
414 447
    let pair = vec::get⟨Obj⟩(&v, 0)
415 448
        else throw testing::TestError::Failed;
416 449
    assert pair.wide == 12;
417 450
    assert pair.narrow == 2;
418 451
}
452 +
453 +
/// Storage for allocations made while testing vector growth.
454 +
static VEC_GROWTH_STORAGE: [u8; 256] = undefined;
455 +
456 +
@test fn testVecAppendAndRemove() throws (testing::TestError) {
457 +
    let mut arena = alloc::new(&mut VEC_GROWTH_STORAGE[..]);
458 +
    let allocator = alloc::arenaAllocator(&mut arena);
459 +
    let mut storage: [i32; 2] = undefined;
460 +
    let mut values: vec::Vec⟨i32= undefined;
461 +
    vec::init⟨i32(&mut values, &mut storage[..]);
462 +
463 +
    vec::append⟨i32(&mut values, 10, allocator);
464 +
    vec::append⟨i32(&mut values, 20, allocator);
465 +
    vec::append⟨i32(&mut values, 30, allocator);
466 +
    assert vec::capacity⟨i32(&values) > 2;
467 +
    let grown = vec::view⟨i32(&values);
468 +
    assert grown.len == 3;
469 +
    assert grown[0] == 10;
470 +
    assert grown[1] == 20;
471 +
    assert grown[2] == 30;
472 +
473 +
    let capacity = vec::capacity⟨i32(&values);
474 +
    let first = vec::remove⟨i32(&mut values, 0);
475 +
    assert first == 10;
476 +
    vec::append⟨i32(&mut values, 40, allocator);
477 +
    let third = vec::remove⟨i32(&mut values, 1);
478 +
    assert third == 30;
479 +
    let middle = vec::view⟨i32(&values);
480 +
    assert middle.len == 2;
481 +
    assert middle[0] == 20;
482 +
    assert middle[1] == 40;
483 +
    assert vec::capacity⟨i32(&values) == capacity;
484 +
485 +
    let final = vec::remove⟨i32(&mut values, 1);
486 +
    assert final == 40;
487 +
    let last = vec::view⟨i32(&values);
488 +
    assert last.len == 1;
489 +
    assert last[0] == 20;
490 +
}
491 +
492 +
@test fn testVecZeroSizedGrowthAndRemove() throws (testing::TestError) {
493 +
    let mut arena = alloc::new(&mut VEC_GROWTH_STORAGE[..]);
494 +
    let allocator = alloc::arenaAllocator(&mut arena);
495 +
    let mut values: vec::Vec⟨ZeroSized⟩ = undefined;
496 +
    vec::init⟨ZeroSized⟩(&mut values, &mut []);
497 +
498 +
    vec::append⟨ZeroSized⟩(&mut values, ZeroSized {}, allocator);
499 +
    vec::append⟨ZeroSized⟩(&mut values, ZeroSized {}, allocator);
500 +
    vec::append⟨ZeroSized⟩(&mut values, ZeroSized {}, allocator);
501 +
    vec::append⟨ZeroSized⟩(&mut values, ZeroSized {}, allocator);
502 +
    let initialized = vec::view⟨ZeroSized⟩(&values);
503 +
    assert initialized.len == 4;
504 +
505 +
    let removed: ZeroSized = vec::remove⟨ZeroSized⟩(&mut values, 1);
506 +
    assert removed == ZeroSized {};
507 +
    let remaining = vec::view⟨ZeroSized⟩(&values);
508 +
    assert remaining.len == 3;
509 +
}
510 +
511 +
@test fn testVecAppendFromEmpty() throws (testing::TestError) {
512 +
    let mut arena = alloc::new(&mut VEC_GROWTH_STORAGE[..]);
513 +
    let allocator = alloc::arenaAllocator(&mut arena);
514 +
    let mut values: vec::Vec⟨i32= undefined;
515 +
    vec::init⟨i32(&mut values, &mut []);
516 +
517 +
    vec::append⟨i32(&mut values, 99, allocator);
518 +
    assert vec::capacity⟨i32(&values) > 0;
519 +
    let view = vec::view⟨i32(&values);
520 +
    assert view.len == 1;
521 +
    assert view[0] == 99;
522 +
}
523 +
524 +
@test fn testVecAppendU8() throws (testing::TestError) {
525 +
    let mut arena = alloc::new(&mut VEC_GROWTH_STORAGE[..]);
526 +
    let allocator = alloc::arenaAllocator(&mut arena);
527 +
    let mut storage: [u8; 1] = undefined;
528 +
    let mut values: vec::Vec⟨u8= undefined;
529 +
    vec::init⟨u8(&mut values, &mut storage[..]);
530 +
531 +
    vec::append⟨u8(&mut values, 0xAB, allocator);
532 +
    vec::append⟨u8(&mut values, 0xCD, allocator);
533 +
    let view = vec::view⟨u8(&values);
534 +
    assert view.len == 2;
535 +
    assert view[0] == 0xAB;
536 +
    assert view[1] == 0xCD;
537 +
}
538 +
539 +
// slice ///////////////////////////////////////////////////////////////////////
540 +
541 +
instantiate slice::fromRawParts⟨i32⟩,
542 +
            slice::fromRawPartsMut⟨i32⟩;
543 +
544 +
@test fn testSliceFromRawParts() throws (testing::TestError) {
545 +
    let mut values: [i32; 4] = [10, 20, 30, 40];
546 +
    let view = slice::fromRawParts⟨i32(&values[1], 2);
547 +
    assert view.len == 2;
548 +
    assert view[0] == 20;
549 +
    assert view[1] == 30;
550 +
551 +
    let mutable = slice::fromRawPartsMut⟨i32(&mut values[0], 3);
552 +
    assert mutable.len == 3;
553 +
    set mutable[1] = 99;
554 +
    assert values[1] == 99;
555 +
    assert view[0] == 99;
556 +
557 +
    let empty = slice::fromRawParts⟨i32(&values[0], 0);
558 +
    assert empty.len == 0;
559 +
}
lib/std/vec.rad +5 -70
1 -
//! Typed vector backed by caller-owned static storage.
2 -
3 -
/// Typed vector metadata backed by caller-owned storage.
4 -
export record Vec⟨T⟩ {
5 -
    /// Typed slice containing the vector's storage.
6 -
    data: *mut [T],
7 -
    /// Number of initialized elements.
8 -
    len: u32,
9 -
}
10 -
11 -
/// Initialize a vector over caller-owned storage.
12 -
export fn init⟨T⟩(vec: *mut Vec⟨T⟩, arena: *mut [T]) {
13 -
    set vec.data = arena;
14 -
    set vec.len = 0;
15 -
}
16 -
17 -
/// Return the number of initialized elements.
18 -
export fn len⟨T⟩(vec: *Vec⟨T⟩) -> u32 {
19 -
    return vec.len;
20 -
}
21 -
22 -
/// Return the storage capacity in elements.
23 -
export fn capacity⟨T⟩(vec: *Vec⟨T⟩) -> u32 {
24 -
    return vec.data.len;
25 -
}
26 -
27 -
/// Reset a vector without clearing its storage.
28 -
export fn reset⟨T⟩(vec: *mut Vec⟨T⟩) {
29 -
    set vec.len = 0;
30 -
}
31 -
32 -
/// Return a typed pointer to an element, or `nil` when out of bounds.
33 -
export fn get⟨T⟩(vec: *Vec⟨T⟩, index: u32) -> ?*T {
34 -
    if index >= vec.len {
35 -
        return nil;
36 -
    }
37 -
38 -
    return &vec.data[index];
39 -
}
40 -
41 -
/// Append an element, returning `false` when the vector is full.
42 -
export fn push⟨T⟩(vec: *mut Vec⟨T⟩, value: T) -> bool {
43 -
    if vec.len >= vec.data.len {
44 -
        return false;
45 -
    }
46 -
47 -
    set vec.data[vec.len] = value;
48 -
    set vec.len += 1;
49 -
50 -
    return true;
51 -
}
52 -
53 -
/// Replace an initialized element, returning `false` when out of bounds.
54 -
export fn put⟨T⟩(vec: *mut Vec⟨T⟩, index: u32, value: T) -> bool {
55 -
    if index >= vec.len {
56 -
        return false;
57 -
    }
58 -
59 -
    set vec.data[index] = value;
60 -
    return true;
61 -
}
62 -
63 -
/// Remove and return the last element, or `nil` when empty.
64 -
export fn pop⟨T⟩(vec: *mut Vec⟨T⟩) -> ?T {
65 -
    if vec.len == 0 {
66 -
        return nil;
67 -
    }
68 -
    set vec.len -= 1;
69 -
    return vec.data[vec.len];
70 -
}
1 +
//! Public typed vector API.
2 +
//!
3 +
//! The implementation lives in `std::lang::vec` so language modules can
4 +
//! resolve it before this stable top-level facade.
5 +
export use std::lang::vec::*;
seed/radiance.rv64 +0 -0

Binary file changed.

seed/radiance.rv64.git +1 -1
1 -
f102340ee2c496ef3ae997c4a449df1e805d63e30eef07fe7aa562bf2884eab3
1 +
3225bfc0f4e8414a142c1c31c309d6172bf96408739626c07578605ac8253188
std.lib +3 -0
1 1
lib/std.rad
2 2
lib/std/char.rad
3 3
lib/std/fmt.rad
4 4
lib/std/mem.rad
5 +
lib/std/slice.rad
5 6
lib/std/vec.rad
6 7
lib/std/io.rad
7 8
lib/std/intrinsics.rad
8 9
lib/std/sys.rad
9 10
lib/std/collections.rad
20 21
lib/std/arch/rv64/asm/scanner.rad
21 22
lib/std/arch/rv64/asm/parser.rad
22 23
lib/std/arch/rv64/asm/emit.rad
23 24
lib/std/lang.rad
24 25
lib/std/lang/alloc.rad
26 +
lib/std/lang/slice.rad
27 +
lib/std/lang/vec.rad
25 28
lib/std/lang/types.rad
26 29
lib/std/lang/strings.rad
27 30
lib/std/lang/sexpr.rad
28 31
lib/std/lang/ast.rad
29 32
lib/std/lang/ast/printer.rad
test/tests/array.slice.full.ril +0 -1
1 1
fn w64 $sliceFull(w64 %0, w64 %1) {
2 2
  @entry0
3 3
    reserve %2 16 8;
4 4
    store w64 %1 %2 0;
5 5
    store w32 4 %2 8;
6 -
    store w32 4 %2 12;
7 6
    blit %0 %2 16;
8 7
    ret %0;
9 8
}
test/tests/array.slice.openend.ril +0 -1
9 9
    add w64 %4 %1 %3;
10 10
    sub w32 %5 4 %2;
11 11
    reserve %6 16 8;
12 12
    store w64 %4 %6 0;
13 13
    store w32 %5 %6 8;
14 -
    store w32 %5 %6 12;
15 14
    blit %0 %6 16;
16 15
    ret %0;
17 16
}
test/tests/array.slice.openstart.ril +0 -1
6 6
    unreachable;
7 7
  @guard#pass2
8 8
    reserve %3 16 8;
9 9
    store w64 %1 %3 0;
10 10
    store w32 %2 %3 8;
11 -
    store w32 %2 %3 12;
12 11
    blit %0 %3 16;
13 12
    ret %0;
14 13
}
test/tests/builtin.sliceof.invalid.cap.rad deleted +0 -10
1 -
//! returns: 133
2 -
//! Test @sliceOf runtime validation for len > cap.
3 -
4 -
@default fn main() -> i32 {
5 -
    let mut arr: [i32; 4] = [10, 20, 30, 40];
6 -
    let ptr: *i32 = &arr[0];
7 -
    let slice: *[i32] = @sliceOf(ptr, 4, 3);
8 -
9 -
    return slice.len as i32;
10 -
}
test/tests/const.array.repeat.string.slice.ril +2 -2
3 3
}
4 4
5 5
data $WORDS align 8 {
6 6
    sym $WORDS$literal$0;
7 7
    w32 3;
8 -
    w32 3;
8 +
    undef * 4;
9 9
    sym $WORDS$literal$0;
10 10
    w32 3;
11 -
    w32 3;
11 +
    undef * 4;
12 12
}
13 13
14 14
fn w32 $main() {
15 15
  @entry0
16 16
    copy %0 $WORDS;
test/tests/const.array.strings.slice.ril +3 -3
7 7
}
8 8
9 9
data $WORDS align 8 {
10 10
    sym $WORDS$literal$0;
11 11
    w32 5;
12 -
    w32 5;
12 +
    undef * 4;
13 13
    sym $WORDS$literal$1;
14 14
    w32 6;
15 -
    w32 6;
15 +
    undef * 4;
16 16
    sym $WORDS$literal$0;
17 17
    w32 5;
18 -
    w32 5;
18 +
    undef * 4;
19 19
}
20 20
21 21
fn w32 $totalLen() {
22 22
  @entry0
23 23
    copy %0 $WORDS;
test/tests/const.record.union.ril +3 -3
11 11
}
12 12
13 13
data $KEYWORDS align 8 {
14 14
    sym $KEYWORDS$literal$0;
15 15
    w32 2;
16 -
    w32 2;
16 +
    undef * 4;
17 17
    w8 0;
18 18
    undef * 7;
19 19
    sym $KEYWORDS$literal$1;
20 20
    w32 3;
21 -
    w32 3;
21 +
    undef * 4;
22 22
    w8 1;
23 23
    undef * 7;
24 24
    sym $KEYWORDS$literal$2;
25 25
    w32 2;
26 -
    w32 2;
26 +
    undef * 4;
27 27
    w8 2;
28 28
    undef * 7;
29 29
}
30 30
31 31
fn w32 $getFirstTag() {
test/tests/const.slice.of.slices.ril +5 -5
7 7
}
8 8
9 9
data $GROUPS$literal$2 align 8 {
10 10
    sym $GROUPS$literal$0;
11 11
    w32 2;
12 -
    w32 2;
12 +
    undef * 4;
13 13
    sym $GROUPS$literal$1;
14 14
    w32 2;
15 -
    w32 2;
15 +
    undef * 4;
16 16
}
17 17
18 18
data $GROUPS$literal$3 align 1 {
19 19
    str "efg";
20 20
}
21 21
22 22
data $GROUPS$literal$4 align 8 {
23 23
    sym $GROUPS$literal$3;
24 24
    w32 3;
25 -
    w32 3;
25 +
    undef * 4;
26 26
}
27 27
28 28
data $GROUPS align 8 {
29 29
    sym $GROUPS$literal$2;
30 30
    w32 2;
31 -
    w32 2;
31 +
    undef * 4;
32 32
    sym $GROUPS$literal$4;
33 33
    w32 1;
34 -
    w32 1;
34 +
    undef * 4;
35 35
}
36 36
37 37
fn w32 $totalLen() {
38 38
  @entry0
39 39
    copy %0 $GROUPS;
test/tests/const.string.ril +1 -2
3 3
}
4 4
5 5
data $HELLO align 8 {
6 6
    sym $HELLO$literal$0;
7 7
    w32 5;
8 -
    w32 5;
8 +
    undef * 4;
9 9
}
10 10
11 11
fn w32 $getLen() {
12 12
  @entry0
13 13
    copy %0 $HELLO$literal$0;
14 14
    reserve %1 16 8;
15 15
    store w64 %0 %1 0;
16 16
    store w32 5 %1 8;
17 -
    store w32 5 %1 12;
18 17
    load w32 %2 %1 8;
19 18
    ret %2;
20 19
}
test/tests/const.string.scoped.names.ril +4 -6
3 3
}
4 4
5 5
data $HELLO1 align 8 {
6 6
    sym $HELLO1$literal$0;
7 7
    w32 5;
8 -
    w32 5;
8 +
    undef * 4;
9 9
}
10 10
11 11
data $HELLO2 align 8 {
12 12
    sym $HELLO1$literal$0;
13 13
    w32 5;
14 -
    w32 5;
14 +
    undef * 4;
15 15
}
16 16
17 17
data $MSGS$literal$3 align 1 {
18 18
    str "world";
19 19
}
20 20
21 21
data $MSGS align 8 {
22 22
    sym $HELLO1$literal$0;
23 23
    w32 5;
24 -
    w32 5;
24 +
    undef * 4;
25 25
    sym $MSGS$literal$3;
26 26
    w32 5;
27 -
    w32 5;
27 +
    undef * 4;
28 28
}
29 29
30 30
fn w32 $totalLen() {
31 31
  @entry0
32 32
    copy %0 $HELLO1$literal$0;
33 33
    reserve %1 16 8;
34 34
    store w64 %0 %1 0;
35 35
    store w32 5 %1 8;
36 -
    store w32 5 %1 12;
37 36
    load w32 %2 %1 8;
38 37
    copy %3 $HELLO1$literal$0;
39 38
    reserve %4 16 8;
40 39
    store w64 %3 %4 0;
41 40
    store w32 5 %4 8;
42 -
    store w32 5 %4 12;
43 41
    load w32 %5 %4 8;
44 42
    add w32 %6 %2 %5;
45 43
    copy %7 $MSGS;
46 44
    load w32 %8 %7 8;
47 45
    add w32 %9 %6 %8;
test/tests/field.aggregate.ril +0 -1
33 33
    store w32 3 %0 8;
34 34
    reserve %1 24 8;
35 35
    reserve %2 16 8;
36 36
    store w64 %0 %2 0;
37 37
    store w32 3 %2 8;
38 -
    store w32 3 %2 12;
39 38
    blit %1 %2 16;
40 39
    store w32 77 %1 16;
41 40
    load w32 %3 %1 8;
42 41
    ret %3;
43 42
}
test/tests/generic.array.iteration.rad added +42 -0
1 +
//! returns: 0
2 +
//! Generic array iteration specializes symbolic element types and lengths.
3 +
4 +
/// Array storage whose length is derived from a constant argument.
5 +
record Extended⟨T, constant N: u32{
6 +
    /// Elements stored in the derived array type.
7 +
    items: [T; N + 1],
8 +
}
9 +
10 +
/// Visit every element and return the last one by index.
11 +
fn lastAfterVisit⟨T, constant N: u32(items: [T; N]) -> T {
12 +
    let mut visited: u32 = 0;
13 +
    for _ in items {
14 +
        set visited += 1;
15 +
    }
16 +
    assert visited == items.len;
17 +
    return items[items.len - 1];
18 +
}
19 +
20 +
fn lastByte⟨constant N: u64(items: [u8; N]) -> u8 {
21 +
    assert items.len == 3;
22 +
    return items[items.len - 1];
23 +
}
24 +
25 +
instantiate Extended⟨i32, 2⟩;
26 +
instantiate Extended⟨u8, 3⟩;
27 +
instantiate lastAfterVisit⟨i32, 3⟩;
28 +
instantiate lastAfterVisit⟨u8, 4⟩;
29 +
instantiate lastByte⟨3⟩;
30 +
31 +
/// Exercise generic iteration for distinct element types and derived lengths.
32 +
@default fn main() -> i32 {
33 +
    let integers = Extended⟨i32, 2{ items: [3, 5, 8] };
34 +
    let bytes = Extended⟨u8, 3{ items: [1, 2, 3, 13] };
35 +
    let integer = lastAfterVisit⟨i32, 3(integers.items);
36 +
    let byte = lastAfterVisit⟨u8, 4(bytes.items);
37 +
    let bytes64: [u8; 3] = [1, 2, 3];
38 +
    assert bytes64.len == 3;
39 +
    let last = lastByte⟨3(bytes64);
40 +
    assert last == bytes64[bytes64.len - 1];
41 +
    return integer + byte as i32 + last as i32 - 24;
42 +
}
test/tests/generic.function.ril +0 -2
50 50
    load w64 %2 %1 0;
51 51
    load w32 %3 %1 8;
52 52
    reserve %4 16 8;
53 53
    store w64 %2 %4 0;
54 54
    store w32 %3 %4 8;
55 -
    store w32 %3 %4 12;
56 55
    blit %0 %4 16;
57 56
    ret %0;
58 57
}
59 58
60 59
fn w64 $"test::reslice⟨i32⟩"(w64 %0, w64 %1) {
62 61
    load w64 %2 %1 0;
63 62
    load w32 %3 %1 8;
64 63
    reserve %4 16 8;
65 64
    store w64 %2 %4 0;
66 65
    store w32 %3 %4 8;
67 -
    store w32 %3 %4 12;
68 66
    blit %0 %4 16;
69 67
    ret %0;
70 68
}
71 69
72 70
fn w64 $"test::some⟨test::Pair⟩"(w64 %0, w64 %1) {
test/tests/literal.slice.bytes.ril +0 -1
8 8
  @entry0
9 9
    copy %0 $byteSlice$literal$0;
10 10
    reserve %1 16 8;
11 11
    store w64 %0 %1 0;
12 12
    store w32 3 %1 8;
13 -
    store w32 3 %1 12;
14 13
    load w32 %2 %1 8;
15 14
    ret %2;
16 15
}
test/tests/literal.slice.dedup.ril +0 -2
8 8
  @entry0
9 9
    copy %0 $dedupSlice$literal$0;
10 10
    reserve %1 16 8;
11 11
    store w64 %0 %1 0;
12 12
    store w32 3 %1 8;
13 -
    store w32 3 %1 12;
14 13
    copy %2 $dedupSlice$literal$0;
15 14
    reserve %3 16 8;
16 15
    store w64 %2 %3 0;
17 16
    store w32 3 %3 8;
18 -
    store w32 3 %3 12;
19 17
    load w32 %4 %1 8;
20 18
    load w32 %5 %3 8;
21 19
    add w32 %6 %4 %5;
22 20
    ret %6;
23 21
}
test/tests/literal.slice.empty.ril +0 -1
1 1
fn w32 $emptySlice() {
2 2
  @entry0
3 3
    reserve %0 16 8;
4 4
    store w64 0 %0 0;
5 5
    store w32 0 %0 8;
6 -
    store w32 0 %0 12;
7 6
    load w32 %1 %0 8;
8 7
    ret %1;
9 8
}
test/tests/literal.slice.multi.ril +0 -2
13 13
  @entry0
14 14
    copy %0 $multiSlice$literal$0;
15 15
    reserve %1 16 8;
16 16
    store w64 %0 %1 0;
17 17
    store w32 2 %1 8;
18 -
    store w32 2 %1 12;
19 18
    copy %2 $multiSlice$literal$1;
20 19
    reserve %3 16 8;
21 20
    store w64 %2 %3 0;
22 21
    store w32 3 %3 8;
23 -
    store w32 3 %3 12;
24 22
    load w32 %4 %1 8;
25 23
    load w32 %5 %3 8;
26 24
    add w32 %6 %4 %5;
27 25
    ret %6;
28 26
}
test/tests/literal.slice.record.ril +0 -1
9 9
  @entry0
10 10
    copy %0 $localSliceOfRecords$literal$0;
11 11
    reserve %1 16 8;
12 12
    store w64 %0 %1 0;
13 13
    store w32 2 %1 8;
14 -
    store w32 2 %1 12;
15 14
    load w32 %2 %1 8;
16 15
    br.ult w32 0 %2 @guard#pass1 @guard#trap2;
17 16
  @guard#pass1
18 17
    load w64 %3 %1 0;
19 18
    sload w32 %4 %3 0;
test/tests/literal.slice.ril +0 -1
8 8
  @entry0
9 9
    copy %0 $sliceLiteral$literal$0;
10 10
    reserve %1 16 8;
11 11
    store w64 %0 %1 0;
12 12
    store w32 3 %1 8;
13 -
    store w32 3 %1 12;
14 13
    load w32 %2 %1 8;
15 14
    ret %2;
16 15
}
test/tests/literal.string.dedup.ril +0 -2
6 6
  @entry0
7 7
    copy %0 $dedupString$literal$0;
8 8
    reserve %1 16 8;
9 9
    store w64 %0 %1 0;
10 10
    store w32 5 %1 8;
11 -
    store w32 5 %1 12;
12 11
    copy %2 $dedupString$literal$0;
13 12
    reserve %3 16 8;
14 13
    store w64 %2 %3 0;
15 14
    store w32 5 %3 8;
16 -
    store w32 5 %3 12;
17 15
    load w32 %4 %1 8;
18 16
    load w32 %5 %3 8;
19 17
    add w32 %6 %4 %5;
20 18
    ret %6;
21 19
}
test/tests/literal.string.empty.ril +0 -1
6 6
  @entry0
7 7
    copy %0 $emptyString$literal$0;
8 8
    reserve %1 16 8;
9 9
    store w64 %0 %1 0;
10 10
    store w32 0 %1 8;
11 -
    store w32 0 %1 12;
12 11
    load w32 %2 %1 8;
13 12
    ret %2;
14 13
}
test/tests/literal.string.fns.ril +0 -2
10 10
  @entry0
11 11
    copy %0 $first$literal$0;
12 12
    reserve %1 16 8;
13 13
    store w64 %0 %1 0;
14 14
    store w32 3 %1 8;
15 -
    store w32 3 %1 12;
16 15
    load w32 %2 %1 8;
17 16
    ret %2;
18 17
}
19 18
20 19
fn w32 $second() {
21 20
  @entry0
22 21
    copy %0 $second$literal$0;
23 22
    reserve %1 16 8;
24 23
    store w64 %0 %1 0;
25 24
    store w32 3 %1 8;
26 -
    store w32 3 %1 12;
27 25
    load w32 %2 %1 8;
28 26
    ret %2;
29 27
}
test/tests/literal.string.multi.ril +0 -2
10 10
  @entry0
11 11
    copy %0 $multiString$literal$0;
12 12
    reserve %1 16 8;
13 13
    store w64 %0 %1 0;
14 14
    store w32 5 %1 8;
15 -
    store w32 5 %1 12;
16 15
    copy %2 $multiString$literal$1;
17 16
    reserve %3 16 8;
18 17
    store w64 %2 %3 0;
19 18
    store w32 5 %3 8;
20 -
    store w32 5 %3 12;
21 19
    load w32 %4 %1 8;
22 20
    load w32 %5 %3 8;
23 21
    add w32 %6 %4 %5;
24 22
    ret %6;
25 23
}
test/tests/literal.string.ril +0 -1
6 6
  @entry0
7 7
    copy %0 $stringLen$literal$0;
8 8
    reserve %1 16 8;
9 9
    store w64 %0 %1 0;
10 10
    store w32 5 %1 8;
11 -
    store w32 5 %1 12;
12 11
    load w32 %2 %1 8;
13 12
    ret %2;
14 13
}
test/tests/lower.const.record.ident.ril +2 -2
15 15
}
16 16
17 17
data $ENTRIES align 8 {
18 18
    sym $ENTRIES$literal$2;
19 19
    w32 2;
20 -
    w32 2;
20 +
    undef * 4;
21 21
    w8 10;
22 22
    undef * 7;
23 23
    sym $ENTRIES$literal$3;
24 24
    w32 2;
25 -
    w32 2;
25 +
    undef * 4;
26 26
    w8 11;
27 27
    undef * 7;
28 28
}
29 29
30 30
fn w32 $main() {
test/tests/lower.private.union.const.ril +2 -2
7 7
}
8 8
9 9
data $ENTRIES align 8 {
10 10
    sym $ENTRIES$literal$0;
11 11
    w32 1;
12 -
    w32 1;
12 +
    undef * 4;
13 13
    w8 0;
14 14
    undef * 7;
15 15
    sym $ENTRIES$literal$1;
16 16
    w32 1;
17 -
    w32 1;
17 +
    undef * 4;
18 18
    w8 1;
19 19
    undef * 7;
20 20
}
21 21
22 22
fn w32 $main() {
test/tests/lower.record.scalar.record.const.ril +2 -2
7 7
}
8 8
9 9
data $ENTRIES align 8 {
10 10
    sym $ENTRIES$literal$0;
11 11
    w32 2;
12 -
    w32 2;
12 +
    undef * 4;
13 13
    w8 10;
14 14
    undef * 7;
15 15
    sym $ENTRIES$literal$1;
16 16
    w32 2;
17 -
    w32 2;
17 +
    undef * 4;
18 18
    w8 11;
19 19
    undef * 7;
20 20
}
21 21
22 22
fn w32 $main() {
test/tests/opt.slice.npo.ril +0 -12
22 22
fn w8 $checkNil() {
23 23
  @entry0
24 24
    reserve %0 16 8;
25 25
    store w64 0 %0 0;
26 26
    store w32 0 %0 8;
27 -
    store w32 0 %0 12;
28 27
    load w64 %1 %0 0;
29 28
    eq w64 %2 %1 0;
30 29
    br.ne w32 %2 0 @assert.ok2 @assert.fail1;
31 30
  @assert.fail1
32 31
    unreachable;
41 40
    store w8 2 %0 1;
42 41
    store w8 3 %0 2;
43 42
    reserve %1 16 8;
44 43
    store w64 %0 %1 0;
45 44
    store w32 3 %1 8;
46 -
    store w32 3 %1 12;
47 45
    reserve %2 16 8;
48 46
    blit %2 %1 16;
49 47
    load w64 %3 %2 0;
50 48
    ne w64 %4 %3 0;
51 49
    br.ne w32 %4 0 @assert.ok2 @assert.fail1;
62 60
    store w8 20 %0 1;
63 61
    store w8 30 %0 2;
64 62
    reserve %1 16 8;
65 63
    store w64 %0 %1 0;
66 64
    store w32 3 %1 8;
67 -
    store w32 3 %1 12;
68 65
    reserve %2 16 8;
69 66
    blit %2 %1 16;
70 67
    reserve %3 16 8;
71 68
    blit %3 %2 16;
72 69
    load w64 %4 %3 0;
107 104
    unreachable;
108 105
  @merge13
109 106
    reserve %13 16 8;
110 107
    store w64 0 %13 0;
111 108
    store w32 0 %13 8;
112 -
    store w32 0 %13 12;
113 109
    reserve %14 16 8;
114 110
    blit %14 %13 16;
115 111
    load w64 %15 %14 0;
116 112
    br.ne w32 %15 0 @then14 @else15;
117 113
  @then14
129 125
    store w8 20 %0 1;
130 126
    store w8 30 %0 2;
131 127
    reserve %1 16 8;
132 128
    store w64 %0 %1 0;
133 129
    store w32 3 %1 8;
134 -
    store w32 3 %1 12;
135 130
    reserve %2 16 8;
136 131
    blit %2 %1 16;
137 132
    reserve %3 16 8;
138 133
    blit %3 %2 16;
139 134
    load w64 %4 %3 0;
154 149
fn w64 $returnNil(w64 %0) {
155 150
  @entry0
156 151
    reserve %1 16 8;
157 152
    store w64 0 %1 0;
158 153
    store w32 0 %1 8;
159 -
    store w32 0 %1 12;
160 154
    blit %0 %1 16;
161 155
    ret %0;
162 156
}
163 157
164 158
fn w64 $returnSome(w64 %0) {
167 161
    store w8 42 %1 0;
168 162
    store w8 99 %1 1;
169 163
    reserve %2 16 8;
170 164
    store w64 %1 %2 0;
171 165
    store w32 2 %2 8;
172 -
    store w32 2 %2 12;
173 166
    blit %0 %2 16;
174 167
    ret %0;
175 168
}
176 169
177 170
fn w8 $checkReturn() {
222 215
    store w8 5 %0 0;
223 216
    store w8 6 %0 1;
224 217
    reserve %1 16 8;
225 218
    store w64 %0 %1 0;
226 219
    store w32 2 %1 8;
227 -
    store w32 2 %1 12;
228 220
    reserve %2 16 8;
229 221
    blit %2 %1 16;
230 222
    reserve %3 16 8;
231 223
    blit %3 %2 16;
232 224
    jmp @arm1;
241 233
    jmp @merge6;
242 234
  @merge6
243 235
    reserve %5 16 8;
244 236
    store w64 0 %5 0;
245 237
    store w32 0 %5 8;
246 -
    store w32 0 %5 12;
247 238
    reserve %6 16 8;
248 239
    blit %6 %5 16;
249 240
    jmp @arm7;
250 241
  @arm7
251 242
    load w64 %7 %6 0;
263 254
fn w8 $checkEq() {
264 255
  @entry0
265 256
    reserve %0 16 8;
266 257
    store w64 0 %0 0;
267 258
    store w32 0 %0 8;
268 -
    store w32 0 %0 12;
269 259
    reserve %1 16 8;
270 260
    store w64 0 %1 0;
271 261
    store w32 0 %1 8;
272 -
    store w32 0 %1 12;
273 262
    load w64 %2 %0 0;
274 263
    load w64 %3 %1 0;
275 264
    eq w64 %4 %2 %3;
276 265
    load w32 %5 %0 8;
277 266
    load w32 %6 %1 8;
285 274
    store w8 1 %9 0;
286 275
    store w8 2 %9 1;
287 276
    reserve %10 16 8;
288 277
    store w64 %9 %10 0;
289 278
    store w32 2 %10 8;
290 -
    store w32 2 %10 12;
291 279
    reserve %11 16 8;
292 280
    blit %11 %10 16;
293 281
    load w64 %12 %11 0;
294 282
    load w64 %13 %0 0;
295 283
    eq w64 %14 %12 %13;
test/tests/ptr.deref.ril +0 -2
52 52
    call w32 %4 $derefArrayIndex(%3);
53 53
    copy %5 $main$literal$0;
54 54
    reserve %6 16 8;
55 55
    store w64 %5 %6 0;
56 56
    store w32 3 %6 8;
57 -
    store w32 3 %6 12;
58 57
    call w32 %7 $derefSliceIndex(%6);
59 58
    add w32 %8 %1 %2;
60 59
    add w32 %9 %8 %4;
61 60
    add w32 %10 %9 %7;
62 61
    ret %10;
63 62
}
64 -
test/tests/reference.array.repeat.ril +0 -1
6 6
  @entry0
7 7
    copy %1 $repeatedSlice$literal$0;
8 8
    reserve %2 16 8;
9 9
    store w64 %1 %2 0;
10 10
    store w32 3 %2 8;
11 -
    store w32 3 %2 12;
12 11
    blit %0 %2 16;
13 12
    ret %0;
14 13
}
test/tests/relocate.bounds.rad added +10 -0
1 +
//! returns: 133
2 +
//! Test that relocation traps when the destination is shorter than the source.
3 +
4 +
@default fn main() -> i32 {
5 +
    let mut bytes: [u8; 4] = [1, 2, 3, 4];
6 +
    let destination: *mut [u8] = &mut bytes[0..1];
7 +
    let source: *mut [u8] = &mut bytes[1..3];
8 +
    @relocate(destination, source);
9 +
    return 0;
10 +
}
test/tests/relocate.rad added +37 -0
1 +
//! returns: 0
2 +
//! Test overlap-safe relocation between mutable byte slices.
3 +
4 +
@default fn main() -> i32 {
5 +
    let mut bytes: [u8; 6] = [1, 2, 3, 4, 5, 6];
6 +
7 +
    let rightDestination: *mut [u8] = &mut bytes[1..5];
8 +
    let rightSource: *mut [u8] = &mut bytes[0..4];
9 +
    @relocate(rightDestination, rightSource);
10 +
11 +
    assert bytes[0] == 1;
12 +
    assert bytes[1] == 1;
13 +
    assert bytes[2] == 2;
14 +
    assert bytes[3] == 3;
15 +
    assert bytes[4] == 4;
16 +
    assert bytes[5] == 6;
17 +
18 +
    let leftDestination: *mut [u8] = &mut bytes[0..4];
19 +
    let leftSource: *mut [u8] = &mut bytes[1..5];
20 +
    @relocate(leftDestination, leftSource);
21 +
22 +
    assert bytes[0] == 1;
23 +
    assert bytes[1] == 2;
24 +
    assert bytes[2] == 3;
25 +
    assert bytes[3] == 4;
26 +
    assert bytes[4] == 4;
27 +
    assert bytes[5] == 6;
28 +
29 +
    let emptyDestination: *mut [u8] = &mut bytes[0..0];
30 +
    let emptySource: *mut [u8] = &mut bytes[6..6];
31 +
    @relocate(emptyDestination, emptySource);
32 +
33 +
    assert bytes[0] == 1;
34 +
    assert bytes[5] == 6;
35 +
36 +
    return 0;
37 +
}
test/tests/slice.append.rad deleted +0 -191
1 -
//! returns: 0
2 -
//! Test slice .append() method with inline allocator.
3 -
4 -
/// Simple bump allocator for testing.
5 -
record Arena {
6 -
    data: *mut [u8],
7 -
    offset: u32,
8 -
}
9 -
10 -
fn newArena(data: *mut [u8]) -> Arena {
11 -
    return Arena { data, offset: 0 };
12 -
}
13 -
14 -
fn arenaAlloc(arena: *mut Arena, size: u32, al: u32) -> *mut opaque {
15 -
    let aligned = (arena.offset + al - 1) / al * al;
16 -
    let newOffset = aligned + size;
17 -
18 -
    assert newOffset <= arena.data.len as u32;
19 -
20 -
    let base: *mut u8 = &mut arena.data[aligned];
21 -
    set arena.offset = newOffset;
22 -
23 -
    return base as *mut opaque;
24 -
}
25 -
26 -
/// Allocator record matching the compiler's expected layout.
27 -
record Allocator {
28 -
    func: fn(*mut opaque, u32, u32) -> *mut opaque,
29 -
    ctx: *mut opaque,
30 -
}
31 -
32 -
fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque {
33 -
    let arena = ctx as *mut Arena;
34 -
    return arenaAlloc(arena, size, al);
35 -
}
36 -
37 -
fn arenaAllocator(arena: *mut Arena) -> Allocator {
38 -
    return Allocator {
39 -
        func: arenaAllocFn,
40 -
        ctx: arena as *mut opaque,
41 -
    };
42 -
}
43 -
44 -
static BUF: [u8; 4096] = undefined;
45 -
46 -
@default fn main() -> i32 {
47 -
    let mut arena = newArena(&mut BUF[..]);
48 -
    let a = arenaAllocator(&mut arena);
49 -
50 -
    // Allocate initial capacity of 4.
51 -
    let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32));
52 -
    let mut nums = @sliceOf(ptr as *mut i32, 0, 4);
53 -
54 -
    // Append within capacity.
55 -
    nums.append(10, a);
56 -
    nums.append(20, a);
57 -
    nums.append(30, a);
58 -
59 -
    if nums.len <> 3 {
60 -
        return 1;
61 -
    }
62 -
    if nums.cap <> 4 {
63 -
        return 2;
64 -
    }
65 -
    if nums[0] <> 10 {
66 -
        return 3;
67 -
    }
68 -
    if nums[1] <> 20 {
69 -
        return 4;
70 -
    }
71 -
    if nums[2] <> 30 {
72 -
        return 5;
73 -
    }
74 -
75 -
    // Append to fill capacity.
76 -
    nums.append(40, a);
77 -
78 -
    if nums.len <> 4 {
79 -
        return 6;
80 -
    }
81 -
    if nums.cap <> 4 {
82 -
        return 7;
83 -
    }
84 -
85 -
    // Append past capacity -- triggers growth.
86 -
    nums.append(50, a);
87 -
88 -
    if nums.len <> 5 {
89 -
        return 8;
90 -
    }
91 -
    // Cap should have grown (cap * 2 | 1 = 4 * 2 | 1 = 9).
92 -
    if nums.cap <> 9 {
93 -
        return 9;
94 -
    }
95 -
    if nums[4] <> 50 {
96 -
        return 10;
97 -
    }
98 -
99 -
    // Verify old elements survived the copy.
100 -
    if nums[0] <> 10 {
101 -
        return 11;
102 -
    }
103 -
    if nums[3] <> 40 {
104 -
        return 12;
105 -
    }
106 -
107 -
    // Append from empty (cap == 0 triggers growth with cap = 1).
108 -
    let mut empty = @sliceOf(ptr as *mut i32, 0, 0);
109 -
    empty.append(99, a);
110 -
111 -
    if empty.len <> 1 {
112 -
        return 13;
113 -
    }
114 -
    if empty.cap <> 1 {
115 -
        return 14;
116 -
    }
117 -
    if empty[0] <> 99 {
118 -
        return 15;
119 -
    }
120 -
121 -
    // Sum all elements in nums.
122 -
    let mut sum: i32 = 0;
123 -
    for n in nums {
124 -
        set sum += n;
125 -
    }
126 -
    if sum <> 150 {
127 -
        return 16;
128 -
    }
129 -
130 -
    // Test append with u8 elements (stride = 1).
131 -
    let bytePtr = arenaAlloc(&mut arena, 2, 1);
132 -
    let mut bytes = @sliceOf(bytePtr as *mut u8, 0, 2);
133 -
    bytes.append(0xAB, a);
134 -
    bytes.append(0xCD, a);
135 -
136 -
    if bytes.len <> 2 {
137 -
        return 17;
138 -
    }
139 -
    if bytes[0] <> 0xAB {
140 -
        return 18;
141 -
    }
142 -
    if bytes[1] <> 0xCD {
143 -
        return 19;
144 -
    }
145 -
146 -
    // Trigger growth on u8 slice.
147 -
    bytes.append(0xEF, a);
148 -
149 -
    if bytes.len <> 3 {
150 -
        return 20;
151 -
    }
152 -
    if bytes.cap <> 5 {
153 -
        return 21;
154 -
    }
155 -
    if bytes[2] <> 0xEF {
156 -
        return 22;
157 -
    }
158 -
    // Verify old u8 elements survived.
159 -
    if bytes[0] <> 0xAB {
160 -
        return 23;
161 -
    }
162 -
163 -
    // Test that .append() returns the slice, allowing rebinding.
164 -
    let ptr2 = arenaAlloc(&mut arena, @sizeOf(i32) * 2, @alignOf(i32));
165 -
    let mut vals = @sliceOf(ptr2 as *mut i32, 0, 2);
166 -
167 -
    // Append within capacity, rebind via return value.
168 -
    set vals = vals.append(42, a);
169 -
    assert vals.len == 1;
170 -
    assert vals[0] == 42;
171 -
172 -
    set vals = vals.append(43, a);
173 -
    assert vals.len == 2;
174 -
175 -
    // Append past capacity -- triggers growth and returns updated slice.
176 -
    let oldPtr = &vals[0] as *opaque;
177 -
    set vals = vals.append(44, a);
178 -
    assert vals.len == 3;
179 -
    assert vals.cap == 5;
180 -
181 -
    // After growth, the data pointer should have changed.
182 -
    let newPtr = &vals[0] as *opaque;
183 -
    assert oldPtr <> newPtr;
184 -
185 -
    // Verify all elements survived.
186 -
    assert vals[0] == 42;
187 -
    assert vals[1] == 43;
188 -
    assert vals[2] == 44;
189 -
190 -
    return 0;
191 -
}
test/tests/slice.append.ril deleted +0 -789
1 -
data mut $BUF align 1 {
2 -
    undef * 4096;
3 -
}
4 -
5 -
fn w64 $newArena(w64 %0, w64 %1) {
6 -
  @entry0
7 -
    reserve %2 24 8;
8 -
    blit %2 %1 16;
9 -
    store w32 0 %2 16;
10 -
    blit %0 %2 24;
11 -
    ret %0;
12 -
}
13 -
14 -
fn w64 $arenaAlloc(w64 %0, w32 %1, w32 %2) {
15 -
  @entry0
16 -
    load w32 %3 %0 16;
17 -
    add w32 %4 %3 %2;
18 -
    sub w32 %5 %4 1;
19 -
    udiv w32 %6 %5 %2;
20 -
    mul w32 %7 %6 %2;
21 -
    add w32 %8 %7 %1;
22 -
    load w32 %9 %0 8;
23 -
    br.ult w32 %9 %8 @assert.fail1 @assert.ok2;
24 -
  @assert.fail1
25 -
    unreachable;
26 -
  @assert.ok2
27 -
    load w32 %10 %0 8;
28 -
    br.ult w32 %7 %10 @guard#pass3 @guard#trap4;
29 -
  @guard#pass3
30 -
    load w64 %11 %0 0;
31 -
    add w64 %12 %11 %7;
32 -
    store w32 %8 %0 16;
33 -
    ret %12;
34 -
  @guard#trap4
35 -
    ebreak;
36 -
    unreachable;
37 -
}
38 -
39 -
fn w64 $arenaAllocFn(w64 %0, w32 %1, w32 %2) {
40 -
  @entry0
41 -
    call w64 %3 $arenaAlloc(%0, %1, %2);
42 -
    ret %3;
43 -
}
44 -
45 -
fn w64 $arenaAllocator(w64 %0, w64 %1) {
46 -
  @entry0
47 -
    reserve %2 16 8;
48 -
    copy %3 $arenaAllocFn;
49 -
    store w64 %3 %2 0;
50 -
    store w64 %1 %2 8;
51 -
    blit %0 %2 16;
52 -
    ret %0;
53 -
}
54 -
55 -
fn w32 $main() {
56 -
  @entry0
57 -
    copy %0 $BUF;
58 -
    reserve %1 16 8;
59 -
    store w64 %0 %1 0;
60 -
    store w32 4096 %1 8;
61 -
    store w32 4096 %1 12;
62 -
    reserve %2 24 8;
63 -
    call w64 %3 $newArena(%2, %1);
64 -
    reserve %4 16 8;
65 -
    call w64 %5 $arenaAllocator(%4, %3);
66 -
    mul w32 %6 4 4;
67 -
    call w64 %7 $arenaAlloc(%3, %6, 4);
68 -
    reserve %8 16 8;
69 -
    store w64 %7 %8 0;
70 -
    store w32 0 %8 8;
71 -
    store w32 4 %8 12;
72 -
    load w32 %9 %8 8;
73 -
    load w32 %10 %8 12;
74 -
    br.ult w32 %9 %10 @append.store1 @append.grow2;
75 -
  @append.store1
76 -
    load w64 %24 %8 0;
77 -
    mul w64 %25 %9 4;
78 -
    add w64 %26 %24 %25;
79 -
    store w32 10 %26 0;
80 -
    add w32 %27 %9 1;
81 -
    store w32 %27 %8 8;
82 -
    load w32 %32 %8 8;
83 -
    load w32 %33 %8 12;
84 -
    br.ult w32 %32 %33 @append.store6 @append.grow7;
85 -
  @append.grow2
86 -
    shl w32 %11 %10 1;
87 -
    or w32 %12 %11 1;
88 -
    load w64 %13 %5 0;
89 -
    load w64 %14 %5 8;
90 -
    mul w32 %15 %12 4;
91 -
    call w64 %16 %13(%14, %15, 4);
92 -
    load w64 %17 %8 0;
93 -
    mul w32 %18 %9 4;
94 -
    jmp @append3(0);
95 -
  @append3(w32 %19)
96 -
    br.ult w32 %19 %18 @append4 @append5;
97 -
  @append4
98 -
    add w64 %20 %17 %19;
99 -
    load w8 %21 %20 0;
100 -
    add w64 %22 %16 %19;
101 -
    store w8 %21 %22 0;
102 -
    add w32 %23 %19 1;
103 -
    jmp @append3(%23);
104 -
  @append5
105 -
    store w64 %16 %8 0;
106 -
    store w32 %12 %8 12;
107 -
    jmp @append.store1;
108 -
  @append.store6
109 -
    load w64 %47 %8 0;
110 -
    mul w64 %48 %32 4;
111 -
    add w64 %49 %47 %48;
112 -
    store w32 20 %49 0;
113 -
    add w32 %50 %32 1;
114 -
    store w32 %50 %8 8;
115 -
    load w32 %55 %8 8;
116 -
    load w32 %56 %8 12;
117 -
    br.ult w32 %55 %56 @append.store11 @append.grow12;
118 -
  @append.grow7
119 -
    shl w32 %34 %33 1;
120 -
    or w32 %35 %34 1;
121 -
    load w64 %36 %5 0;
122 -
    load w64 %37 %5 8;
123 -
    mul w32 %38 %35 4;
124 -
    call w64 %39 %36(%37, %38, 4);
125 -
    load w64 %40 %8 0;
126 -
    mul w32 %41 %32 4;
127 -
    jmp @append8(0);
128 -
  @append8(w32 %42)
129 -
    br.ult w32 %42 %41 @append9 @append10;
130 -
  @append9
131 -
    add w64 %43 %40 %42;
132 -
    load w8 %44 %43 0;
133 -
    add w64 %45 %39 %42;
134 -
    store w8 %44 %45 0;
135 -
    add w32 %46 %42 1;
136 -
    jmp @append8(%46);
137 -
  @append10
138 -
    store w64 %39 %8 0;
139 -
    store w32 %35 %8 12;
140 -
    jmp @append.store6;
141 -
  @append.store11
142 -
    load w64 %70 %8 0;
143 -
    mul w64 %71 %55 4;
144 -
    add w64 %72 %70 %71;
145 -
    store w32 30 %72 0;
146 -
    add w32 %73 %55 1;
147 -
    store w32 %73 %8 8;
148 -
    load w32 %76 %8 8;
149 -
    br.ne w32 %76 3 @then16 @merge17;
150 -
  @append.grow12
151 -
    shl w32 %57 %56 1;
152 -
    or w32 %58 %57 1;
153 -
    load w64 %59 %5 0;
154 -
    load w64 %60 %5 8;
155 -
    mul w32 %61 %58 4;
156 -
    call w64 %62 %59(%60, %61, 4);
157 -
    load w64 %63 %8 0;
158 -
    mul w32 %64 %55 4;
159 -
    jmp @append13(0);
160 -
  @append13(w32 %65)
161 -
    br.ult w32 %65 %64 @append14 @append15;
162 -
  @append14
163 -
    add w64 %66 %63 %65;
164 -
    load w8 %67 %66 0;
165 -
    add w64 %68 %62 %65;
166 -
    store w8 %67 %68 0;
167 -
    add w32 %69 %65 1;
168 -
    jmp @append13(%69);
169 -
  @append15
170 -
    store w64 %62 %8 0;
171 -
    store w32 %58 %8 12;
172 -
    jmp @append.store11;
173 -
  @then16
174 -
    ret 1;
175 -
  @merge17
176 -
    load w32 %77 %8 12;
177 -
    br.ne w32 %77 4 @then18 @merge19;
178 -
  @then18
179 -
    ret 2;
180 -
  @merge19
181 -
    load w32 %78 %8 8;
182 -
    br.ult w32 0 %78 @guard#pass22 @guard#trap23;
183 -
  @then20
184 -
    ret 3;
185 -
  @merge21
186 -
    load w32 %81 %8 8;
187 -
    br.ult w32 1 %81 @guard#pass26 @guard#trap27;
188 -
  @guard#pass22
189 -
    load w64 %79 %8 0;
190 -
    sload w32 %80 %79 0;
191 -
    br.ne w32 %80 10 @then20 @merge21;
192 -
  @guard#trap23
193 -
    ebreak;
194 -
    unreachable;
195 -
  @then24
196 -
    ret 4;
197 -
  @merge25
198 -
    load w32 %86 %8 8;
199 -
    br.ult w32 2 %86 @guard#pass30 @guard#trap31;
200 -
  @guard#pass26
201 -
    load w64 %82 %8 0;
202 -
    mul w64 %83 1 4;
203 -
    add w64 %84 %82 %83;
204 -
    sload w32 %85 %84 0;
205 -
    br.ne w32 %85 20 @then24 @merge25;
206 -
  @guard#trap27
207 -
    ebreak;
208 -
    unreachable;
209 -
  @then28
210 -
    ret 5;
211 -
  @merge29
212 -
    load w32 %93 %8 8;
213 -
    load w32 %94 %8 12;
214 -
    br.ult w32 %93 %94 @append.store32 @append.grow33;
215 -
  @guard#pass30
216 -
    load w64 %87 %8 0;
217 -
    mul w64 %88 2 4;
218 -
    add w64 %89 %87 %88;
219 -
    sload w32 %90 %89 0;
220 -
    br.ne w32 %90 30 @then28 @merge29;
221 -
  @guard#trap31
222 -
    ebreak;
223 -
    unreachable;
224 -
  @append.store32
225 -
    load w64 %108 %8 0;
226 -
    mul w64 %109 %93 4;
227 -
    add w64 %110 %108 %109;
228 -
    store w32 40 %110 0;
229 -
    add w32 %111 %93 1;
230 -
    store w32 %111 %8 8;
231 -
    load w32 %114 %8 8;
232 -
    br.ne w32 %114 4 @then37 @merge38;
233 -
  @append.grow33
234 -
    shl w32 %95 %94 1;
235 -
    or w32 %96 %95 1;
236 -
    load w64 %97 %5 0;
237 -
    load w64 %98 %5 8;
238 -
    mul w32 %99 %96 4;
239 -
    call w64 %100 %97(%98, %99, 4);
240 -
    load w64 %101 %8 0;
241 -
    mul w32 %102 %93 4;
242 -
    jmp @append34(0);
243 -
  @append34(w32 %103)
244 -
    br.ult w32 %103 %102 @append35 @append36;
245 -
  @append35
246 -
    add w64 %104 %101 %103;
247 -
    load w8 %105 %104 0;
248 -
    add w64 %106 %100 %103;
249 -
    store w8 %105 %106 0;
250 -
    add w32 %107 %103 1;
251 -
    jmp @append34(%107);
252 -
  @append36
253 -
    store w64 %100 %8 0;
254 -
    store w32 %96 %8 12;
255 -
    jmp @append.store32;
256 -
  @then37
257 -
    ret 6;
258 -
  @merge38
259 -
    load w32 %115 %8 12;
260 -
    br.ne w32 %115 4 @then39 @merge40;
261 -
  @then39
262 -
    ret 7;
263 -
  @merge40
264 -
    load w32 %118 %8 8;
265 -
    load w32 %119 %8 12;
266 -
    br.ult w32 %118 %119 @append.store41 @append.grow42;
267 -
  @append.store41
268 -
    load w64 %133 %8 0;
269 -
    mul w64 %134 %118 4;
270 -
    add w64 %135 %133 %134;
271 -
    store w32 50 %135 0;
272 -
    add w32 %136 %118 1;
273 -
    store w32 %136 %8 8;
274 -
    load w32 %139 %8 8;
275 -
    br.ne w32 %139 5 @then46 @merge47;
276 -
  @append.grow42
277 -
    shl w32 %120 %119 1;
278 -
    or w32 %121 %120 1;
279 -
    load w64 %122 %5 0;
280 -
    load w64 %123 %5 8;
281 -
    mul w32 %124 %121 4;
282 -
    call w64 %125 %122(%123, %124, 4);
283 -
    load w64 %126 %8 0;
284 -
    mul w32 %127 %118 4;
285 -
    jmp @append43(0);
286 -
  @append43(w32 %128)
287 -
    br.ult w32 %128 %127 @append44 @append45;
288 -
  @append44
289 -
    add w64 %129 %126 %128;
290 -
    load w8 %130 %129 0;
291 -
    add w64 %131 %125 %128;
292 -
    store w8 %130 %131 0;
293 -
    add w32 %132 %128 1;
294 -
    jmp @append43(%132);
295 -
  @append45
296 -
    store w64 %125 %8 0;
297 -
    store w32 %121 %8 12;
298 -
    jmp @append.store41;
299 -
  @then46
300 -
    ret 8;
301 -
  @merge47
302 -
    load w32 %140 %8 12;
303 -
    br.ne w32 %140 9 @then48 @merge49;
304 -
  @then48
305 -
    ret 9;
306 -
  @merge49
307 -
    load w32 %141 %8 8;
308 -
    br.ult w32 4 %141 @guard#pass52 @guard#trap53;
309 -
  @then50
310 -
    ret 10;
311 -
  @merge51
312 -
    load w32 %146 %8 8;
313 -
    br.ult w32 0 %146 @guard#pass56 @guard#trap57;
314 -
  @guard#pass52
315 -
    load w64 %142 %8 0;
316 -
    mul w64 %143 4 4;
317 -
    add w64 %144 %142 %143;
318 -
    sload w32 %145 %144 0;
319 -
    br.ne w32 %145 50 @then50 @merge51;
320 -
  @guard#trap53
321 -
    ebreak;
322 -
    unreachable;
323 -
  @then54
324 -
    ret 11;
325 -
  @merge55
326 -
    load w32 %149 %8 8;
327 -
    br.ult w32 3 %149 @guard#pass60 @guard#trap61;
328 -
  @guard#pass56
329 -
    load w64 %147 %8 0;
330 -
    sload w32 %148 %147 0;
331 -
    br.ne w32 %148 10 @then54 @merge55;
332 -
  @guard#trap57
333 -
    ebreak;
334 -
    unreachable;
335 -
  @then58
336 -
    ret 12;
337 -
  @merge59
338 -
    reserve %164 16 8;
339 -
    store w64 %7 %164 0;
340 -
    store w32 0 %164 8;
341 -
    store w32 0 %164 12;
342 -
    load w32 %167 %164 8;
343 -
    load w32 %168 %164 12;
344 -
    br.ult w32 %167 %168 @append.store62 @append.grow63;
345 -
  @guard#pass60
346 -
    load w64 %150 %8 0;
347 -
    mul w64 %151 3 4;
348 -
    add w64 %152 %150 %151;
349 -
    sload w32 %153 %152 0;
350 -
    br.ne w32 %153 40 @then58 @merge59;
351 -
  @guard#trap61
352 -
    ebreak;
353 -
    unreachable;
354 -
  @append.store62
355 -
    load w64 %182 %164 0;
356 -
    mul w64 %183 %167 4;
357 -
    add w64 %184 %182 %183;
358 -
    store w32 99 %184 0;
359 -
    add w32 %185 %167 1;
360 -
    store w32 %185 %164 8;
361 -
    load w32 %188 %164 8;
362 -
    br.ne w32 %188 1 @then67 @merge68;
363 -
  @append.grow63
364 -
    shl w32 %169 %168 1;
365 -
    or w32 %170 %169 1;
366 -
    load w64 %171 %5 0;
367 -
    load w64 %172 %5 8;
368 -
    mul w32 %173 %170 4;
369 -
    call w64 %174 %171(%172, %173, 4);
370 -
    load w64 %175 %164 0;
371 -
    mul w32 %176 %167 4;
372 -
    jmp @append64(0);
373 -
  @append64(w32 %177)
374 -
    br.ult w32 %177 %176 @append65 @append66;
375 -
  @append65
376 -
    add w64 %178 %175 %177;
377 -
    load w8 %179 %178 0;
378 -
    add w64 %180 %174 %177;
379 -
    store w8 %179 %180 0;
380 -
    add w32 %181 %177 1;
381 -
    jmp @append64(%181);
382 -
  @append66
383 -
    store w64 %174 %164 0;
384 -
    store w32 %170 %164 12;
385 -
    jmp @append.store62;
386 -
  @then67
387 -
    ret 13;
388 -
  @merge68
389 -
    load w32 %189 %164 12;
390 -
    br.ne w32 %189 1 @then69 @merge70;
391 -
  @then69
392 -
    ret 14;
393 -
  @merge70
394 -
    load w32 %190 %164 8;
395 -
    br.ult w32 0 %190 @guard#pass73 @guard#trap74;
396 -
  @then71
397 -
    ret 15;
398 -
  @merge72
399 -
    load w32 %195 %8 8;
400 -
    load w64 %196 %8 0;
401 -
    jmp @loop75(0, 0);
402 -
  @guard#pass73
403 -
    load w64 %191 %164 0;
404 -
    sload w32 %192 %191 0;
405 -
    br.ne w32 %192 99 @then71 @merge72;
406 -
  @guard#trap74
407 -
    ebreak;
408 -
    unreachable;
409 -
  @loop75(w32 %197, w32 %201)
410 -
    br.slt w32 %197 %195 @body76 @merge77;
411 -
  @body76
412 -
    mul w64 %198 %197 4;
413 -
    add w64 %199 %196 %198;
414 -
    sload w32 %200 %199 0;
415 -
    add w32 %202 %201 %200;
416 -
    add w32 %203 %197 1;
417 -
    jmp @loop75(%203, %202);
418 -
  @merge77
419 -
    br.ne w32 %201 150 @then78 @merge79;
420 -
  @then78
421 -
    ret 16;
422 -
  @merge79
423 -
    call w64 %217 $arenaAlloc(%3, 2, 1);
424 -
    reserve %218 16 8;
425 -
    store w64 %217 %218 0;
426 -
    store w32 0 %218 8;
427 -
    store w32 2 %218 12;
428 -
    load w32 %222 %218 8;
429 -
    load w32 %223 %218 12;
430 -
    br.ult w32 %222 %223 @append.store80 @append.grow81;
431 -
  @append.store80
432 -
    load w64 %237 %218 0;
433 -
    add w64 %238 %237 %222;
434 -
    store w8 171 %238 0;
435 -
    add w32 %239 %222 1;
436 -
    store w32 %239 %218 8;
437 -
    load w32 %244 %218 8;
438 -
    load w32 %245 %218 12;
439 -
    br.ult w32 %244 %245 @append.store85 @append.grow86;
440 -
  @append.grow81
441 -
    shl w32 %224 %223 1;
442 -
    or w32 %225 %224 1;
443 -
    load w64 %226 %5 0;
444 -
    load w64 %227 %5 8;
445 -
    mul w32 %228 %225 1;
446 -
    call w64 %229 %226(%227, %228, 1);
447 -
    load w64 %230 %218 0;
448 -
    mul w32 %231 %222 1;
449 -
    jmp @append82(0);
450 -
  @append82(w32 %232)
451 -
    br.ult w32 %232 %231 @append83 @append84;
452 -
  @append83
453 -
    add w64 %233 %230 %232;
454 -
    load w8 %234 %233 0;
455 -
    add w64 %235 %229 %232;
456 -
    store w8 %234 %235 0;
457 -
    add w32 %236 %232 1;
458 -
    jmp @append82(%236);
459 -
  @append84
460 -
    store w64 %229 %218 0;
461 -
    store w32 %225 %218 12;
462 -
    jmp @append.store80;
463 -
  @append.store85
464 -
    load w64 %259 %218 0;
465 -
    add w64 %260 %259 %244;
466 -
    store w8 205 %260 0;
467 -
    add w32 %261 %244 1;
468 -
    store w32 %261 %218 8;
469 -
    load w32 %264 %218 8;
470 -
    br.ne w32 %264 2 @then90 @merge91;
471 -
  @append.grow86
472 -
    shl w32 %246 %245 1;
473 -
    or w32 %247 %246 1;
474 -
    load w64 %248 %5 0;
475 -
    load w64 %249 %5 8;
476 -
    mul w32 %250 %247 1;
477 -
    call w64 %251 %248(%249, %250, 1);
478 -
    load w64 %252 %218 0;
479 -
    mul w32 %253 %244 1;
480 -
    jmp @append87(0);
481 -
  @append87(w32 %254)
482 -
    br.ult w32 %254 %253 @append88 @append89;
483 -
  @append88
484 -
    add w64 %255 %252 %254;
485 -
    load w8 %256 %255 0;
486 -
    add w64 %257 %251 %254;
487 -
    store w8 %256 %257 0;
488 -
    add w32 %258 %254 1;
489 -
    jmp @append87(%258);
490 -
  @append89
491 -
    store w64 %251 %218 0;
492 -
    store w32 %247 %218 12;
493 -
    jmp @append.store85;
494 -
  @then90
495 -
    ret 17;
496 -
  @merge91
497 -
    load w32 %265 %218 8;
498 -
    br.ult w32 0 %265 @guard#pass94 @guard#trap95;
499 -
  @then92
500 -
    ret 18;
501 -
  @merge93
502 -
    load w32 %268 %218 8;
503 -
    br.ult w32 1 %268 @guard#pass98 @guard#trap99;
504 -
  @guard#pass94
505 -
    load w64 %266 %218 0;
506 -
    load w8 %267 %266 0;
507 -
    br.ne w8 %267 171 @then92 @merge93;
508 -
  @guard#trap95
509 -
    ebreak;
510 -
    unreachable;
511 -
  @then96
512 -
    ret 19;
513 -
  @merge97
514 -
    load w32 %274 %218 8;
515 -
    load w32 %275 %218 12;
516 -
    br.ult w32 %274 %275 @append.store100 @append.grow101;
517 -
  @guard#pass98
518 -
    load w64 %269 %218 0;
519 -
    add w64 %270 %269 1;
520 -
    load w8 %271 %270 0;
521 -
    br.ne w8 %271 205 @then96 @merge97;
522 -
  @guard#trap99
523 -
    ebreak;
524 -
    unreachable;
525 -
  @append.store100
526 -
    load w64 %289 %218 0;
527 -
    add w64 %290 %289 %274;
528 -
    store w8 239 %290 0;
529 -
    add w32 %291 %274 1;
530 -
    store w32 %291 %218 8;
531 -
    load w32 %294 %218 8;
532 -
    br.ne w32 %294 3 @then105 @merge106;
533 -
  @append.grow101
534 -
    shl w32 %276 %275 1;
535 -
    or w32 %277 %276 1;
536 -
    load w64 %278 %5 0;
537 -
    load w64 %279 %5 8;
538 -
    mul w32 %280 %277 1;
539 -
    call w64 %281 %278(%279, %280, 1);
540 -
    load w64 %282 %218 0;
541 -
    mul w32 %283 %274 1;
542 -
    jmp @append102(0);
543 -
  @append102(w32 %284)
544 -
    br.ult w32 %284 %283 @append103 @append104;
545 -
  @append103
546 -
    add w64 %285 %282 %284;
547 -
    load w8 %286 %285 0;
548 -
    add w64 %287 %281 %284;
549 -
    store w8 %286 %287 0;
550 -
    add w32 %288 %284 1;
551 -
    jmp @append102(%288);
552 -
  @append104
553 -
    store w64 %281 %218 0;
554 -
    store w32 %277 %218 12;
555 -
    jmp @append.store100;
556 -
  @then105
557 -
    ret 20;
558 -
  @merge106
559 -
    load w32 %295 %218 12;
560 -
    br.ne w32 %295 5 @then107 @merge108;
561 -
  @then107
562 -
    ret 21;
563 -
  @merge108
564 -
    load w32 %296 %218 8;
565 -
    br.ult w32 2 %296 @guard#pass111 @guard#trap112;
566 -
  @then109
567 -
    ret 22;
568 -
  @merge110
569 -
    load w32 %300 %218 8;
570 -
    br.ult w32 0 %300 @guard#pass115 @guard#trap116;
571 -
  @guard#pass111
572 -
    load w64 %297 %218 0;
573 -
    add w64 %298 %297 2;
574 -
    load w8 %299 %298 0;
575 -
    br.ne w8 %299 239 @then109 @merge110;
576 -
  @guard#trap112
577 -
    ebreak;
578 -
    unreachable;
579 -
  @then113
580 -
    ret 23;
581 -
  @merge114
582 -
    mul w32 %309 4 2;
583 -
    call w64 %310 $arenaAlloc(%3, %309, 4);
584 -
    reserve %311 16 8;
585 -
    store w64 %310 %311 0;
586 -
    store w32 0 %311 8;
587 -
    store w32 2 %311 12;
588 -
    load w32 %314 %311 8;
589 -
    load w32 %315 %311 12;
590 -
    br.ult w32 %314 %315 @append.store117 @append.grow118;
591 -
  @guard#pass115
592 -
    load w64 %301 %218 0;
593 -
    load w8 %302 %301 0;
594 -
    br.ne w8 %302 171 @then113 @merge114;
595 -
  @guard#trap116
596 -
    ebreak;
597 -
    unreachable;
598 -
  @append.store117
599 -
    load w64 %329 %311 0;
600 -
    mul w64 %330 %314 4;
601 -
    add w64 %331 %329 %330;
602 -
    store w32 42 %331 0;
603 -
    add w32 %332 %314 1;
604 -
    store w32 %332 %311 8;
605 -
    blit %311 %311 16;
606 -
    load w32 %335 %311 8;
607 -
    br.eq w32 %335 1 @assert.ok123 @assert.fail122;
608 -
  @append.grow118
609 -
    shl w32 %316 %315 1;
610 -
    or w32 %317 %316 1;
611 -
    load w64 %318 %5 0;
612 -
    load w64 %319 %5 8;
613 -
    mul w32 %320 %317 4;
614 -
    call w64 %321 %318(%319, %320, 4);
615 -
    load w64 %322 %311 0;
616 -
    mul w32 %323 %314 4;
617 -
    jmp @append119(0);
618 -
  @append119(w32 %324)
619 -
    br.ult w32 %324 %323 @append120 @append121;
620 -
  @append120
621 -
    add w64 %325 %322 %324;
622 -
    load w8 %326 %325 0;
623 -
    add w64 %327 %321 %324;
624 -
    store w8 %326 %327 0;
625 -
    add w32 %328 %324 1;
626 -
    jmp @append119(%328);
627 -
  @append121
628 -
    store w64 %321 %311 0;
629 -
    store w32 %317 %311 12;
630 -
    jmp @append.store117;
631 -
  @assert.fail122
632 -
    unreachable;
633 -
  @assert.ok123
634 -
    load w32 %336 %311 8;
635 -
    br.ult w32 0 %336 @guard#pass126 @guard#trap127;
636 -
  @assert.fail124
637 -
    unreachable;
638 -
  @assert.ok125
639 -
    load w32 %341 %311 8;
640 -
    load w32 %342 %311 12;
641 -
    br.ult w32 %341 %342 @append.store128 @append.grow129;
642 -
  @guard#pass126
643 -
    load w64 %337 %311 0;
644 -
    sload w32 %338 %337 0;
645 -
    br.eq w32 %338 42 @assert.ok125 @assert.fail124;
646 -
  @guard#trap127
647 -
    ebreak;
648 -
    unreachable;
649 -
  @append.store128
650 -
    load w64 %356 %311 0;
651 -
    mul w64 %357 %341 4;
652 -
    add w64 %358 %356 %357;
653 -
    store w32 43 %358 0;
654 -
    add w32 %359 %341 1;
655 -
    store w32 %359 %311 8;
656 -
    blit %311 %311 16;
657 -
    load w32 %362 %311 8;
658 -
    br.eq w32 %362 2 @assert.ok134 @assert.fail133;
659 -
  @append.grow129
660 -
    shl w32 %343 %342 1;
661 -
    or w32 %344 %343 1;
662 -
    load w64 %345 %5 0;
663 -
    load w64 %346 %5 8;
664 -
    mul w32 %347 %344 4;
665 -
    call w64 %348 %345(%346, %347, 4);
666 -
    load w64 %349 %311 0;
667 -
    mul w32 %350 %341 4;
668 -
    jmp @append130(0);
669 -
  @append130(w32 %351)
670 -
    br.ult w32 %351 %350 @append131 @append132;
671 -
  @append131
672 -
    add w64 %352 %349 %351;
673 -
    load w8 %353 %352 0;
674 -
    add w64 %354 %348 %351;
675 -
    store w8 %353 %354 0;
676 -
    add w32 %355 %351 1;
677 -
    jmp @append130(%355);
678 -
  @append132
679 -
    store w64 %348 %311 0;
680 -
    store w32 %344 %311 12;
681 -
    jmp @append.store128;
682 -
  @assert.fail133
683 -
    unreachable;
684 -
  @assert.ok134
685 -
    load w32 %363 %311 8;
686 -
    br.ult w32 0 %363 @guard#pass135 @guard#trap136;
687 -
  @guard#pass135
688 -
    load w64 %364 %311 0;
689 -
    load w32 %367 %311 8;
690 -
    load w32 %368 %311 12;
691 -
    br.ult w32 %367 %368 @append.store137 @append.grow138;
692 -
  @guard#trap136
693 -
    ebreak;
694 -
    unreachable;
695 -
  @append.store137
696 -
    load w64 %382 %311 0;
697 -
    mul w64 %383 %367 4;
698 -
    add w64 %384 %382 %383;
699 -
    store w32 44 %384 0;
700 -
    add w32 %385 %367 1;
701 -
    store w32 %385 %311 8;
702 -
    blit %311 %311 16;
703 -
    load w32 %388 %311 8;
704 -
    br.eq w32 %388 3 @assert.ok143 @assert.fail142;
705 -
  @append.grow138
706 -
    shl w32 %369 %368 1;
707 -
    or w32 %370 %369 1;
708 -
    load w64 %371 %5 0;
709 -
    load w64 %372 %5 8;
710 -
    mul w32 %373 %370 4;
711 -
    call w64 %374 %371(%372, %373, 4);
712 -
    load w64 %375 %311 0;
713 -
    mul w32 %376 %367 4;
714 -
    jmp @append139(0);
715 -
  @append139(w32 %377)
716 -
    br.ult w32 %377 %376 @append140 @append141;
717 -
  @append140
718 -
    add w64 %378 %375 %377;
719 -
    load w8 %379 %378 0;
720 -
    add w64 %380 %374 %377;
721 -
    store w8 %379 %380 0;
722 -
    add w32 %381 %377 1;
723 -
    jmp @append139(%381);
724 -
  @append141
725 -
    store w64 %374 %311 0;
726 -
    store w32 %370 %311 12;
727 -
    jmp @append.store137;
728 -
  @assert.fail142
729 -
    unreachable;
730 -
  @assert.ok143
731 -
    load w32 %389 %311 12;
732 -
    br.eq w32 %389 5 @assert.ok145 @assert.fail144;
733 -
  @assert.fail144
734 -
    unreachable;
735 -
  @assert.ok145
736 -
    load w32 %390 %311 8;
737 -
    br.ult w32 0 %390 @guard#pass146 @guard#trap147;
738 -
  @guard#pass146
739 -
    load w64 %391 %311 0;
740 -
    br.ne w64 %364 %391 @assert.ok149 @assert.fail148;
741 -
  @guard#trap147
742 -
    ebreak;
743 -
    unreachable;
744 -
  @assert.fail148
745 -
    unreachable;
746 -
  @assert.ok149
747 -
    load w32 %394 %311 8;
748 -
    br.ult w32 0 %394 @guard#pass152 @guard#trap153;
749 -
  @assert.fail150
750 -
    unreachable;
751 -
  @assert.ok151
752 -
    load w32 %397 %311 8;
753 -
    br.ult w32 1 %397 @guard#pass156 @guard#trap157;
754 -
  @guard#pass152
755 -
    load w64 %395 %311 0;
756 -
    sload w32 %396 %395 0;
757 -
    br.eq w32 %396 42 @assert.ok151 @assert.fail150;
758 -
  @guard#trap153
759 -
    ebreak;
760 -
    unreachable;
761 -
  @assert.fail154
762 -
    unreachable;
763 -
  @assert.ok155
764 -
    load w32 %402 %311 8;
765 -
    br.ult w32 2 %402 @guard#pass160 @guard#trap161;
766 -
  @guard#pass156
767 -
    load w64 %398 %311 0;
768 -
    mul w64 %399 1 4;
769 -
    add w64 %400 %398 %399;
770 -
    sload w32 %401 %400 0;
771 -
    br.eq w32 %401 43 @assert.ok155 @assert.fail154;
772 -
  @guard#trap157
773 -
    ebreak;
774 -
    unreachable;
775 -
  @assert.fail158
776 -
    unreachable;
777 -
  @assert.ok159
778 -
    ret 0;
779 -
  @guard#pass160
780 -
    load w64 %403 %311 0;
781 -
    mul w64 %404 2 4;
782 -
    add w64 %405 %403 %404;
783 -
    sload w32 %406 %405 0;
784 -
    br.eq w32 %406 44 @assert.ok159 @assert.fail158;
785 -
  @guard#trap161
786 -
    ebreak;
787 -
    unreachable;
788 -
}
789 -
test/tests/slice.basic.rad +0 -9
1 1
/// Returns the length field from a slice header.
2 2
fn sliceLen(s: *[i32]) -> u32 {
3 3
    return s.len;
4 4
}
5 5
6 -
/// Returns the capacity field from a slice header.
7 -
fn sliceCap(s: *[i32]) -> u32 {
8 -
    return s.cap;
9 -
}
10 -
11 6
/// Returns the pointer field from a slice header.
12 7
fn slicePtr(s: *[i32]) -> *i32 {
13 8
    return s.ptr;
14 9
}
15 10
22 17
/// Builds a slice header from a pointer and length.
23 18
fn sliceOf(ptr: *i32, len: u32) -> *[i32] {
24 19
    return @sliceOf(ptr, len);
25 20
}
26 21
27 -
/// Builds a slice header from pointer, length, and capacity.
28 -
fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] {
29 -
    return @sliceOf(ptr, len, cap);
30 -
}
test/tests/slice.basic.ril +0 -21
2 2
  @entry0
3 3
    load w32 %1 %0 8;
4 4
    ret %1;
5 5
}
6 6
7 -
fn w32 $sliceCap(w64 %0) {
8 -
  @entry0
9 -
    load w32 %1 %0 12;
10 -
    ret %1;
11 -
}
12 -
13 7
fn w64 $slicePtr(w64 %0) {
14 8
  @entry0
15 9
    load w64 %1 %0 0;
16 10
    ret %1;
17 11
}
27 21
fn w64 $sliceOf(w64 %0, w64 %1, w32 %2) {
28 22
  @entry0
29 23
    reserve %3 16 8;
30 24
    store w64 %1 %3 0;
31 25
    store w32 %2 %3 8;
32 -
    store w32 %2 %3 12;
33 26
    blit %0 %3 16;
34 27
    ret %0;
35 28
}
36 29
37 -
fn w64 $sliceOfWithCap(w64 %0, w64 %1, w32 %2, w32 %3) {
38 -
  @entry0
39 -
    br.ult w32 %3 %2 @guard#trap1 @guard#pass2;
40 -
  @guard#trap1
41 -
    ebreak;
42 -
    unreachable;
43 -
  @guard#pass2
44 -
    reserve %4 16 8;
45 -
    store w64 %1 %4 0;
46 -
    store w32 %2 %4 8;
47 -
    store w32 %3 %4 12;
48 -
    blit %0 %4 16;
49 -
    ret %0;
50 -
}
test/tests/slice.cap.rad deleted +0 -53
1 -
//! returns: 0
2 -
//! Test slice capacity field and @sliceOf builtin.
3 -
4 -
@default fn main() -> i32 {
5 -
    // Test that regular slices have cap == len.
6 -
    let mut arr: [i32; 4] = [10, 20, 30, 40];
7 -
    let s = &mut arr[..];
8 -
9 -
    if s.cap <> 4 {
10 -
        return 1;
11 -
    }
12 -
    if s.len <> s.cap {
13 -
        return 2;
14 -
    }
15 -
16 -
    // Test @sliceOf with explicit capacity.
17 -
    let ptr = &mut arr[0];
18 -
    let s2 = @sliceOf(ptr, 2, 4);
19 -
20 -
    if s2.len <> 2 {
21 -
        return 3;
22 -
    }
23 -
    if s2.cap <> 4 {
24 -
        return 4;
25 -
    }
26 -
    if s2[0] <> 10 {
27 -
        return 5;
28 -
    }
29 -
    if s2[1] <> 20 {
30 -
        return 6;
31 -
    }
32 -
33 -
    // Test @sliceOf with zero length.
34 -
    let s3 = @sliceOf(ptr, 0, 4);
35 -
36 -
    if s3.len <> 0 {
37 -
        return 7;
38 -
    }
39 -
    if s3.cap <> 4 {
40 -
        return 8;
41 -
    }
42 -
43 -
    // Test @sliceOf still works and has cap == len.
44 -
    let s4 = @sliceOf(ptr, 3);
45 -
46 -
    if s4.len <> 3 {
47 -
        return 9;
48 -
    }
49 -
    if s4.cap <> 3 {
50 -
        return 10;
51 -
    }
52 -
    return 0;
53 -
}
test/tests/slice.delete.rad deleted +0 -61
1 -
//! returns: 0
2 -
//! Test slice .delete() method.
3 -
4 -
@default fn main() -> i32 {
5 -
    let mut arr: [i32; 5] = [10, 20, 30, 40, 50];
6 -
    let mut s = &mut arr[..];
7 -
8 -
    // Delete middle element (index 2: value 30).
9 -
    s.delete(2);
10 -
11 -
    if s.len <> 4 {
12 -
        return 1;
13 -
    }
14 -
    if s[0] <> 10 {
15 -
        return 2;
16 -
    }
17 -
    if s[1] <> 20 {
18 -
        return 3;
19 -
    }
20 -
    if s[2] <> 40 {
21 -
        return 4;
22 -
    }
23 -
    if s[3] <> 50 {
24 -
        return 5;
25 -
    }
26 -
27 -
    // Delete first element (index 0: value 10).
28 -
    s.delete(0);
29 -
30 -
    if s.len <> 3 {
31 -
        return 6;
32 -
    }
33 -
    if s[0] <> 20 {
34 -
        return 7;
35 -
    }
36 -
    if s[1] <> 40 {
37 -
        return 8;
38 -
    }
39 -
    if s[2] <> 50 {
40 -
        return 9;
41 -
    }
42 -
43 -
    // Delete last element (index 2: value 50).
44 -
    s.delete(2);
45 -
46 -
    if s.len <> 2 {
47 -
        return 10;
48 -
    }
49 -
    if s[0] <> 20 {
50 -
        return 11;
51 -
    }
52 -
    if s[1] <> 40 {
53 -
        return 12;
54 -
    }
55 -
56 -
    // Cap should be unchanged.
57 -
    if s.cap <> 5 {
58 -
        return 13;
59 -
    }
60 -
    return 0;
61 -
}
test/tests/slice.delete.ril deleted +0 -231
1 -
fn w32 $main() {
2 -
  @entry0
3 -
    reserve %0 20 4;
4 -
    store w32 10 %0 0;
5 -
    store w32 20 %0 4;
6 -
    store w32 30 %0 8;
7 -
    store w32 40 %0 12;
8 -
    store w32 50 %0 16;
9 -
    reserve %1 16 8;
10 -
    store w64 %0 %1 0;
11 -
    store w32 5 %1 8;
12 -
    store w32 5 %1 12;
13 -
    load w32 %2 %1 8;
14 -
    br.ult w32 2 %2 @guard#pass1 @guard#trap2;
15 -
  @guard#pass1
16 -
    load w64 %3 %1 0;
17 -
    mul w64 %4 2 4;
18 -
    add w64 %5 %3 %4;
19 -
    add w64 %6 %5 4;
20 -
    sub w32 %7 %2 2;
21 -
    sub w32 %8 %7 1;
22 -
    mul w32 %9 %8 4;
23 -
    jmp @delete3(0);
24 -
  @guard#trap2
25 -
    ebreak;
26 -
    unreachable;
27 -
  @delete3(w32 %10)
28 -
    br.ult w32 %10 %9 @delete4 @delete5;
29 -
  @delete4
30 -
    add w64 %11 %6 %10;
31 -
    load w8 %12 %11 0;
32 -
    add w64 %13 %5 %10;
33 -
    store w8 %12 %13 0;
34 -
    add w32 %14 %10 1;
35 -
    jmp @delete3(%14);
36 -
  @delete5
37 -
    sub w32 %15 %2 1;
38 -
    store w32 %15 %1 8;
39 -
    load w32 %17 %1 8;
40 -
    br.ne w32 %17 4 @then6 @merge7;
41 -
  @then6
42 -
    ret 1;
43 -
  @merge7
44 -
    load w32 %18 %1 8;
45 -
    br.ult w32 0 %18 @guard#pass10 @guard#trap11;
46 -
  @then8
47 -
    ret 2;
48 -
  @merge9
49 -
    load w32 %21 %1 8;
50 -
    br.ult w32 1 %21 @guard#pass14 @guard#trap15;
51 -
  @guard#pass10
52 -
    load w64 %19 %1 0;
53 -
    sload w32 %20 %19 0;
54 -
    br.ne w32 %20 10 @then8 @merge9;
55 -
  @guard#trap11
56 -
    ebreak;
57 -
    unreachable;
58 -
  @then12
59 -
    ret 3;
60 -
  @merge13
61 -
    load w32 %26 %1 8;
62 -
    br.ult w32 2 %26 @guard#pass18 @guard#trap19;
63 -
  @guard#pass14
64 -
    load w64 %22 %1 0;
65 -
    mul w64 %23 1 4;
66 -
    add w64 %24 %22 %23;
67 -
    sload w32 %25 %24 0;
68 -
    br.ne w32 %25 20 @then12 @merge13;
69 -
  @guard#trap15
70 -
    ebreak;
71 -
    unreachable;
72 -
  @then16
73 -
    ret 4;
74 -
  @merge17
75 -
    load w32 %31 %1 8;
76 -
    br.ult w32 3 %31 @guard#pass22 @guard#trap23;
77 -
  @guard#pass18
78 -
    load w64 %27 %1 0;
79 -
    mul w64 %28 2 4;
80 -
    add w64 %29 %27 %28;
81 -
    sload w32 %30 %29 0;
82 -
    br.ne w32 %30 40 @then16 @merge17;
83 -
  @guard#trap19
84 -
    ebreak;
85 -
    unreachable;
86 -
  @then20
87 -
    ret 5;
88 -
  @merge21
89 -
    load w32 %36 %1 8;
90 -
    br.ult w32 0 %36 @guard#pass24 @guard#trap25;
91 -
  @guard#pass22
92 -
    load w64 %32 %1 0;
93 -
    mul w64 %33 3 4;
94 -
    add w64 %34 %32 %33;
95 -
    sload w32 %35 %34 0;
96 -
    br.ne w32 %35 50 @then20 @merge21;
97 -
  @guard#trap23
98 -
    ebreak;
99 -
    unreachable;
100 -
  @guard#pass24
101 -
    load w64 %37 %1 0;
102 -
    add w64 %38 %37 4;
103 -
    sub w32 %39 %36 0;
104 -
    sub w32 %40 %39 1;
105 -
    mul w32 %41 %40 4;
106 -
    jmp @delete26(0);
107 -
  @guard#trap25
108 -
    ebreak;
109 -
    unreachable;
110 -
  @delete26(w32 %42)
111 -
    br.ult w32 %42 %41 @delete27 @delete28;
112 -
  @delete27
113 -
    add w64 %43 %38 %42;
114 -
    load w8 %44 %43 0;
115 -
    add w64 %45 %37 %42;
116 -
    store w8 %44 %45 0;
117 -
    add w32 %46 %42 1;
118 -
    jmp @delete26(%46);
119 -
  @delete28
120 -
    sub w32 %47 %36 1;
121 -
    store w32 %47 %1 8;
122 -
    load w32 %49 %1 8;
123 -
    br.ne w32 %49 3 @then29 @merge30;
124 -
  @then29
125 -
    ret 6;
126 -
  @merge30
127 -
    load w32 %50 %1 8;
128 -
    br.ult w32 0 %50 @guard#pass33 @guard#trap34;
129 -
  @then31
130 -
    ret 7;
131 -
  @merge32
132 -
    load w32 %53 %1 8;
133 -
    br.ult w32 1 %53 @guard#pass37 @guard#trap38;
134 -
  @guard#pass33
135 -
    load w64 %51 %1 0;
136 -
    sload w32 %52 %51 0;
137 -
    br.ne w32 %52 20 @then31 @merge32;
138 -
  @guard#trap34
139 -
    ebreak;
140 -
    unreachable;
141 -
  @then35
142 -
    ret 8;
143 -
  @merge36
144 -
    load w32 %58 %1 8;
145 -
    br.ult w32 2 %58 @guard#pass41 @guard#trap42;
146 -
  @guard#pass37
147 -
    load w64 %54 %1 0;
148 -
    mul w64 %55 1 4;
149 -
    add w64 %56 %54 %55;
150 -
    sload w32 %57 %56 0;
151 -
    br.ne w32 %57 40 @then35 @merge36;
152 -
  @guard#trap38
153 -
    ebreak;
154 -
    unreachable;
155 -
  @then39
156 -
    ret 9;
157 -
  @merge40
158 -
    load w32 %63 %1 8;
159 -
    br.ult w32 2 %63 @guard#pass43 @guard#trap44;
160 -
  @guard#pass41
161 -
    load w64 %59 %1 0;
162 -
    mul w64 %60 2 4;
163 -
    add w64 %61 %59 %60;
164 -
    sload w32 %62 %61 0;
165 -
    br.ne w32 %62 50 @then39 @merge40;
166 -
  @guard#trap42
167 -
    ebreak;
168 -
    unreachable;
169 -
  @guard#pass43
170 -
    load w64 %64 %1 0;
171 -
    mul w64 %65 2 4;
172 -
    add w64 %66 %64 %65;
173 -
    add w64 %67 %66 4;
174 -
    sub w32 %68 %63 2;
175 -
    sub w32 %69 %68 1;
176 -
    mul w32 %70 %69 4;
177 -
    jmp @delete45(0);
178 -
  @guard#trap44
179 -
    ebreak;
180 -
    unreachable;
181 -
  @delete45(w32 %71)
182 -
    br.ult w32 %71 %70 @delete46 @delete47;
183 -
  @delete46
184 -
    add w64 %72 %67 %71;
185 -
    load w8 %73 %72 0;
186 -
    add w64 %74 %66 %71;
187 -
    store w8 %73 %74 0;
188 -
    add w32 %75 %71 1;
189 -
    jmp @delete45(%75);
190 -
  @delete47
191 -
    sub w32 %76 %63 1;
192 -
    store w32 %76 %1 8;
193 -
    load w32 %78 %1 8;
194 -
    br.ne w32 %78 2 @then48 @merge49;
195 -
  @then48
196 -
    ret 10;
197 -
  @merge49
198 -
    load w32 %79 %1 8;
199 -
    br.ult w32 0 %79 @guard#pass52 @guard#trap53;
200 -
  @then50
201 -
    ret 11;
202 -
  @merge51
203 -
    load w32 %82 %1 8;
204 -
    br.ult w32 1 %82 @guard#pass56 @guard#trap57;
205 -
  @guard#pass52
206 -
    load w64 %80 %1 0;
207 -
    sload w32 %81 %80 0;
208 -
    br.ne w32 %81 20 @then50 @merge51;
209 -
  @guard#trap53
210 -
    ebreak;
211 -
    unreachable;
212 -
  @then54
213 -
    ret 12;
214 -
  @merge55
215 -
    load w32 %87 %1 12;
216 -
    br.ne w32 %87 5 @then58 @merge59;
217 -
  @guard#pass56
218 -
    load w64 %83 %1 0;
219 -
    mul w64 %84 1 4;
220 -
    add w64 %85 %83 %84;
221 -
    sload w32 %86 %85 0;
222 -
    br.ne w32 %86 40 @then54 @merge55;
223 -
  @guard#trap57
224 -
    ebreak;
225 -
    unreachable;
226 -
  @then58
227 -
    ret 13;
228 -
  @merge59
229 -
    ret 0;
230 -
}
231 -
test/tests/slice.range.ril +0 -5
21 21
    add w64 %7 %4 %6;
22 22
    sub w32 %8 %3 %2;
23 23
    reserve %9 16 8;
24 24
    store w64 %7 %9 0;
25 25
    store w32 %8 %9 8;
26 -
    store w32 %8 %9 12;
27 26
    blit %0 %9 16;
28 27
    ret %0;
29 28
}
30 29
31 30
fn w64 $sliceRangeOpenEnd(w64 %0, w64 %1, w32 %2) {
41 40
    add w64 %6 %3 %5;
42 41
    sub w32 %7 %4 %2;
43 42
    reserve %8 16 8;
44 43
    store w64 %6 %8 0;
45 44
    store w32 %7 %8 8;
46 -
    store w32 %7 %8 12;
47 45
    blit %0 %8 16;
48 46
    ret %0;
49 47
}
50 48
51 49
fn w64 $sliceRangeOpenStart(w64 %0, w64 %1, w32 %2) {
58 56
    unreachable;
59 57
  @guard#pass2
60 58
    reserve %5 16 8;
61 59
    store w64 %3 %5 0;
62 60
    store w32 %2 %5 8;
63 -
    store w32 %2 %5 12;
64 61
    blit %0 %5 16;
65 62
    ret %0;
66 63
}
67 64
68 65
fn w64 $sliceRangeFull(w64 %0, w64 %1) {
70 67
    load w64 %2 %1 0;
71 68
    load w32 %3 %1 8;
72 69
    reserve %4 16 8;
73 70
    store w64 %2 %4 0;
74 71
    store w32 %3 %4 8;
75 -
    store w32 %3 %4 12;
76 72
    blit %0 %4 16;
77 73
    ret %0;
78 74
}
79 75
80 76
fn w64 $sliceArray(w64 %0, w64 %1) {
83 79
    add w64 %3 %1 %2;
84 80
    sub w32 %4 3 1;
85 81
    reserve %5 16 8;
86 82
    store w64 %3 %5 0;
87 83
    store w32 %4 %5 8;
88 -
    store w32 %4 %5 12;
89 84
    blit %0 %5 16;
90 85
    ret %0;
91 86
}
test/tests/slice.runtime.i32.ril +0 -3
3 3
    reserve %2 4 4;
4 4
    store w32 %1 %2 0;
5 5
    reserve %3 16 8;
6 6
    store w64 %2 %3 0;
7 7
    store w32 1 %3 8;
8 -
    store w32 1 %3 12;
9 8
    blit %0 %3 16;
10 9
    ret %0;
11 10
}
12 11
13 12
fn w64 $sliceWithVars(w64 %0, w32 %1, w32 %2) {
16 15
    store w32 %1 %3 0;
17 16
    store w32 %2 %3 4;
18 17
    reserve %4 16 8;
19 18
    store w64 %3 %4 0;
20 19
    store w32 2 %4 8;
21 -
    store w32 2 %4 12;
22 20
    blit %0 %4 16;
23 21
    ret %0;
24 22
}
25 23
26 24
fn w64 $sliceWithMixed(w64 %0, w32 %1) {
30 28
    store w32 42 %2 4;
31 29
    store w32 %1 %2 8;
32 30
    reserve %3 16 8;
33 31
    store w64 %2 %3 0;
34 32
    store w32 3 %3 8;
35 -
    store w32 3 %3 12;
36 33
    blit %0 %3 16;
37 34
    ret %0;
38 35
}
test/tests/slice.runtime.literal.ril +0 -2
3 3
    reserve %2 1 1;
4 4
    store w8 %1 %2 0;
5 5
    reserve %3 16 8;
6 6
    store w64 %2 %3 0;
7 7
    store w32 1 %3 8;
8 -
    store w32 1 %3 12;
9 8
    blit %0 %3 16;
10 9
    ret %0;
11 10
}
12 11
13 12
fn w64 $sliceWithVars(w64 %0, w8 %1, w8 %2) {
16 15
    store w8 %1 %3 0;
17 16
    store w8 %2 %3 1;
18 17
    reserve %4 16 8;
19 18
    store w64 %3 %4 0;
20 19
    store w32 2 %4 8;
21 -
    store w32 2 %4 12;
22 20
    blit %0 %4 16;
23 21
    ret %0;
24 22
}
test/tests/trait.supertrait.ril +0 -3
129 129
    load w64 %8 %7 8;
130 130
    copy %9 $main$literal$0;
131 131
    reserve %10 16 8;
132 132
    store w64 %9 %10 0;
133 133
    store w32 3 %10 8;
134 -
    store w32 3 %10 12;
135 134
    call w32 %11 %8(%6, %10);
136 135
    br.eq w32 %11 3 @assert.ok2 @assert.fail1;
137 136
  @assert.fail1
138 137
    unreachable;
139 138
  @assert.ok2
147 146
    load w64 %15 %5 8;
148 147
    load w64 %16 %15 0;
149 148
    reserve %17 16 8;
150 149
    store w64 %13 %17 0;
151 150
    store w32 8 %17 8;
152 -
    store w32 8 %17 12;
153 151
    call w32 %18 %16(%14, %17);
154 152
    br.eq w32 %18 5 @assert.ok6 @assert.fail5;
155 153
  @assert.fail5
156 154
    unreachable;
157 155
  @assert.ok6
179 177
  @assert.fail13
180 178
    unreachable;
181 179
  @assert.ok14
182 180
    ret 0;
183 181
}
184 -
test/tests/vec.generic.rad added +43 -0
1 +
//! returns: 0
2 +
3 +
use std::lang::alloc;
4 +
use std::vec;
5 +
6 +
/// Fixed storage for vector growth allocations.
7 +
static VEC_ARENA_STORAGE: [u8; 64] = undefined;
8 +
9 +
/// Initial vector storage whose capacity is deliberately exceeded.
10 +
static VEC_INITIAL_STORAGE: [i32; 2] = undefined;
11 +
12 +
instantiate vec::init⟨i32⟩,
13 +
            vec::append⟨i32⟩,
14 +
            vec::view⟨i32⟩,
15 +
            vec::remove⟨i32⟩;
16 +
17 +
/// Exercise public vector specializations from outside the standard package.
18 +
@default fn main() -> i32 {
19 +
    let mut arena = alloc::new(&mut VEC_ARENA_STORAGE[..]);
20 +
    let allocator = alloc::arenaAllocator(&mut arena);
21 +
    let mut values: vec::Vec⟨i32= undefined;
22 +
    vec::init⟨i32(&mut values, &mut VEC_INITIAL_STORAGE[..]);
23 +
24 +
    vec::append⟨i32(&mut values, 10, allocator);
25 +
    vec::append⟨i32(&mut values, 20, allocator);
26 +
    vec::append⟨i32(&mut values, 30, allocator);
27 +
28 +
    let initialized = vec::view⟨i32(&values);
29 +
    assert initialized.len == 3;
30 +
    assert initialized[0] == 10;
31 +
    assert initialized[1] == 20;
32 +
    assert initialized[2] == 30;
33 +
34 +
    let removed = vec::remove⟨i32(&mut values, 1);
35 +
    assert removed == 20;
36 +
37 +
    let remaining = vec::view⟨i32(&values);
38 +
    assert remaining.len == 2;
39 +
    assert remaining[0] == 10;
40 +
    assert remaining[1] == 30;
41 +
42 +
    return 0;
43 +
}