compiler: Add lexical regions

9a8d09d29db1f7eebae771702e2d703635f8dadb93e45700b3b5969e17abeb6a
Alexis Sellier committed ago 1 parent 0d7c8248
compiler/radiance.rad +246 -111
14 14
use std::lang::package;
15 15
use std::lang::il;
16 16
use std::lang::lower;
17 17
use std::arch::rv64;
18 18
use std::arch::rv64::asm;
19 +
use std::arch::rv64::emit;
19 20
use std::arch::rv64::printer;
20 21
use std::lang::sexpr;
21 22
use std::lang::gen::data;
22 23
use std::lang::il::binary;
23 24
use std::lang::il::binary::collect;
24 25
use std::lang::il::binary::program;
26 +
use std::lang::gen::labels;
25 27
use std::lang::gen::types;
26 28
use std::sys;
27 29
use std::sys::unix;
28 30
use std::collections::dict;
29 31
30 32
/// Maximum number of modules we can load per package.
31 -
constant MAX_LOADED_MODULES: u32 = module::MAX_MODULES;
33 +
constant MAX_LOADED_MODULES: u32 = 128;
32 34
/// Maximum number of packages we can compile.
33 35
constant MAX_PACKAGES: u32 = 4;
34 -
/// Total module entries across all packages.
35 -
constant MAX_TOTAL_MODULES: u32 = 192;
36 -
/// Source code buffer arena (2 MB).
37 -
constant MAX_SOURCES_SIZE: u32 = 2097152;
36 +
/// Source code buffer arena (4 MB).
37 +
constant MAX_SOURCES_SIZE: u32 = 4194304;
38 38
/// Maximum number of test functions we can discover.
39 39
constant MAX_TESTS: u32 = 1024;
40 40
/// Maximum number of assembly source paths we can load per package.
41 41
constant MAX_ASM_MODULES: u32 = 64;
42 42
43 43
/// AST arena size (64 MB) - retains parsed nodes throughout compilation.
44 44
constant TEMP_ARENA_SIZE: u32 = 67108864;
45 45
/// Per-function lowering and register-allocation arena size (16 MB).
46 46
constant FN_ARENA_SIZE: u32 = 16777216;
47 -
/// Main arena size (96 MB) - lives throughout compilation.
47 +
/// Main arena size (160 MB) - lives throughout compilation.
48 48
/// Used for: resolver data, types, symbols, global IL data, and codegen output.
49 -
constant MAIN_ARENA_SIZE: u32 = 100663296;
49 +
constant MAIN_ARENA_SIZE: u32 = 167772160;
50 50
51 51
/// AST storage arena.
52 52
static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = [0; TEMP_ARENA_SIZE];
53 53
/// Scratch storage reclaimed after each generated function.
54 54
static FN_ARENA: [u8; FN_ARENA_SIZE] = [0; FN_ARENA_SIZE];
56 56
static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = [0; MAIN_ARENA_SIZE];
57 57
58 58
/// Module source code.
59 59
static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE];
60 60
/// Module entries for all packages.
61 -
unsafe static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined;
61 +
unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; module::MAX_MODULES] = [nil; module::MAX_MODULES];
62 62
/// String pool.
63 63
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
64 64
65 65
/// Package scope.
66 66
unsafe static RESOLVER_PKG_SCOPE: resolver::Scope = undefined;
69 69
70 70
/// Code generation storage.
71 71
unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined;
72 72
/// Hash table entries for data symbol lookup.
73 73
unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
74 +
/// Emitted instruction storage.
75 +
static CODEGEN_INSTRUCTIONS: [u32; emit::MAX_INSTRS] = [0; emit::MAX_INSTRS];
76 +
/// Local branch patch storage.
77 +
unsafe static CODEGEN_PENDING_BRANCHES: [emit::PendingBranch; emit::MAX_PENDING] = undefined;
78 +
/// Function call patch storage.
79 +
unsafe static CODEGEN_PENDING_CALLS: [emit::PendingCall; emit::MAX_PENDING] = undefined;
80 +
/// Assembly jump patch storage.
81 +
unsafe static CODEGEN_PENDING_JUMPS: [emit::PendingJump; emit::MAX_PENDING] = undefined;
82 +
/// Address load patch storage.
83 +
unsafe static CODEGEN_PENDING_ADDR_LOADS: [emit::PendingAddrLoad; emit::MAX_PENDING] = undefined;
84 +
/// Per-function block offset storage.
85 +
static CODEGEN_BLOCK_OFFSETS: [i32; labels::MAX_BLOCKS_PER_FN] = [0; labels::MAX_BLOCKS_PER_FN];
86 +
/// Function label hash table storage.
87 +
unsafe static CODEGEN_FUNC_ENTRIES: [dict::Entry; labels::FUNC_TABLE_SIZE] = undefined;
88 +
/// Printed function address storage.
89 +
unsafe static CODEGEN_FUNCS: [types::FuncAddr; emit::MAX_FUNCS] = undefined;
90 +
/// Debug location storage.
91 +
unsafe static CODEGEN_DEBUG_ENTRIES: [types::DebugEntry; emit::MAX_DEBUG_ENTRIES] = undefined;
74 92
75 93
/// Maximum number of exported definitions in one binary package.
76 94
constant MAX_PACKAGE_EXPORTS: u32 = 8192;
77 95
/// Maximum number of indexed names in one binary package.
78 96
constant MAX_PACKAGE_SYMBOLS: u32 = 16384;
265 283
266 284
    let moduleId = try package::registerModule(pkg, graph, &mut STRING_POOL, path) catch {
267 285
        throw error(&["error registering module"]);
268 286
    };
269 287
    // Read file into remaining arena space.
270 -
    let buffer = alloc::remainingBuf(sourceArena);
288 +
    let buffer = alloc::remainingBuf(&mut *sourceArena);
271 289
    if buffer.len == 0 {
272 290
        throw error(&["fatal:", "source arena exhausted"]);
273 291
    }
274 292
    let sourceLen = unix::readFile(path, buffer) else {
275 293
        throw error(&["error reading file"]);
487 505
        throw error(&["no root module found"]);
488 506
    };
489 507
    let rootEntry = module::get(graph, rootId) else {
490 508
        throw error(&["root module entry not found"]);
491 509
    };
492 -
    let rootAst = rootEntry.ast else {
510 +
    let rootAst = module::astFor(rootEntry) else {
493 511
        throw error(&["root module has no AST"]);
494 512
    };
495 513
    return RootModule { entry: rootEntry, ast: rootAst };
496 514
}
497 515
510 528
    ast::printer::printTree(root.ast, &mut arena);
511 529
}
512 530
513 531
/// Lower all packages into a single IL program.
514 532
/// Dependencies are lowered first, then the entry package.
515 -
unsafe fn lowerAllPackages(
533 +
unsafe fn lowerAllPackages 'arena (
516 534
    ctx: *unsafe mut CompileContext,
517 -
    res: *unsafe mut resolver::Resolver
535 +
    res: *unsafe mut resolver::Resolver 'arena
518 536
) -> il::Program throws (Error) {
519 537
    let entryIdx = ctx.entryPkgIdx else {
520 538
        panic "lowerAllPackages: no entry package";
521 539
    };
522 540
    let entryPkg = &ctx.packages[entryIdx];
523 541
524 542
    // Create the lowerer accumulator using entry package's name.
525 543
    let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest };
526 -
    let mut low = lower::lowerer(
527 -
        res, &ctx.graph, entryPkg.name, &mut res.arena, &mut res.arena, options
528 -
    );
529 -
    try lowerAllPackagesInto(ctx, res, &mut low);
544 +
    let arena = (&mut *res.arena) as *unsafe mut alloc::Arena;
545 +
    let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in {
546 +
        let mut low = lower::lowerer(
547 +
            resolved, graph, entryPkg.name, arena, options
548 +
        );
549 +
        try lowerAllPackagesInto(ctx, &mut low, &mut *arena);
530 550
531 -
    // Finalize and return the unified program.
532 -
    return lower::finalize(low);
551 +
        // Finalize and return the unified program.
552 +
        return lower::finalize(low);
553 +
    }
533 554
}
534 555
535 556
/// Lower all packages into an existing lowerer.
536 -
unsafe fn lowerAllPackagesInto(
537 -
    ctx: *unsafe mut CompileContext,
538 -
    res: *unsafe mut resolver::Resolver,
539 -
    low: &mut lower::Lowerer
540 -
) throws (Error) {
557 +
unsafe fn lowerAllPackagesInto 'arena 'phase (
558 +
    ctx: &CompileContext,
559 +
    low: &mut lower::Lowerer 'arena 'phase,
560 +
    functionArena: &mut alloc::Arena
561 +
) throws (Error) where 'arena: 'phase {
541 562
    let entryIdx = ctx.entryPkgIdx else {
542 563
        panic "lowerAllPackagesInto: no entry package";
543 564
    };
544 565
    // Lower all packages except entry.
545 566
    for i in 0..ctx.packageCount {
546 567
        if i <> entryIdx {
547 -
            try lowerPackage(ctx, res, low, &ctx.packages[i], false);
568 +
            try lowerPackage(ctx, low, &ctx.packages[i], false, functionArena);
548 569
        }
549 570
    }
550 571
    // Lower entry package.
551 -
    try lowerPackage(ctx, res, low, &ctx.packages[entryIdx], true);
572 +
    try lowerPackage(ctx, low, &ctx.packages[entryIdx], true, functionArena);
552 573
}
553 574
554 575
/// Lower all modules in a package into the lowerer accumulator.
555 -
unsafe fn lowerPackage(
576 +
unsafe fn lowerPackage 'arena 'phase (
556 577
    ctx: &CompileContext,
557 -
    res: *unsafe mut resolver::Resolver,
558 -
    low: &mut lower::Lowerer,
578 +
    low: &mut lower::Lowerer 'arena 'phase,
559 579
    pkg: &package::Package,
560 -
    isEntry: bool
561 -
) throws (Error) {
580 +
    isEntry: bool,
581 +
    functionArena: &mut alloc::Arena
582 +
) throws (Error) where 'arena: 'phase {
562 583
    let rootId = pkg.rootModuleId else {
563 584
        throw error(&["no root module found"]);
564 585
    };
565 586
    // Set lowerer's package context for qualified name generation.
566 587
    // TODO: We shouldn't have to call this manually.
567 -
    lower::setPackage(low, &ctx.graph, pkg.name);
588 +
    lower::setPackage(low, pkg.name);
568 589
569 -
    try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg);
590 +
    try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg, functionArena);
570 591
}
571 592
572 593
/// Recursively lower a module and all its children into the accumulator.
573 -
unsafe fn lowerModuleTreeInto(
594 +
unsafe fn lowerModuleTreeInto 'arena 'phase (
574 595
    ctx: &CompileContext,
575 -
    low: &mut lower::Lowerer,
596 +
    low: &mut lower::Lowerer 'arena 'phase,
576 597
    graph: &module::ModuleGraph,
577 598
    modId: u16,
578 599
    isRoot: bool,
579 -
    pkg: &package::Package
580 -
) throws (Error) {
600 +
    pkg: &package::Package,
601 +
    functionArena: &mut alloc::Arena
602 +
) throws (Error) where 'arena: 'phase {
581 603
    let entry = module::get(graph, modId) else {
582 604
        throw error(&["module entry not found"]);
583 605
    };
584 -
    let modAst = entry.ast else {
606 +
    let modAst = module::astFor(entry) else {
585 607
        throw error(&["module has no AST"]);
586 608
    };
587 609
    pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]);
588 610
589 -
    try lower::lowerModule(low, modId, modAst, isRoot) catch err {
611 +
    try lower::lowerModule(low, modId, modAst, isRoot, functionArena) catch err {
590 612
        io::printError("radiance: ");
591 613
        io::printError("internal error during lowering: ");
592 614
        lower::printError(err);
593 615
        io::printError("\n");
594 616
595 617
        throw Error::Other;
596 618
    };
597 619
    // Recurse into children.
598 -
    for i in 0..entry.childrenLen {
620 +
    for i in 0..module::childCount(entry) {
599 621
        let childId = module::childAt(entry, i);
600 -
        try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg);
622 +
        try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg, functionArena);
601 623
    }
602 624
}
603 625
604 626
/// Build a scope access chain: a::b::c from a slice of identifiers.
605 627
unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node {
635 657
fn collectModuleTests(
636 658
    entry: *module::ModuleEntry,
637 659
    tests: &mut [TestDesc],
638 660
    testCount: &mut u32
639 661
) {
640 -
    let modAst = entry.ast else {
662 +
    let modAst = module::astFor(entry) else {
641 663
        return;
642 664
    };
643 665
    let case ast::NodeValue::Block(block) = modAst.value else {
644 666
        return;
645 667
    };
687 709
    return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args }));
688 710
}
689 711
690 712
/// Inject a test runner into the entry package's root module.
691 713
///
692 -
/// Scans all parsed modules for `@test fn` declarations, then appends
714 +
/// Scans the entry package for `@test fn` declarations, then appends
693 715
/// a synthetic entry point to the root module's AST block:
694 716
///
695 717
/// ```
696 718
/// @default fn #testMain() -> i32 {
697 719
///     return testing::runAllTests(&[
707 729
    arena: &mut ast::NodeArena
708 730
) throws (Error) {
709 731
    let entryPkg = try getEntryPackage(ctx);
710 732
    let root = try getRootModule(&entryPkg, &ctx.graph);
711 733
712 -
    // Collect all test functions across all modules.
734 +
    // Collect test functions from the entry package's modules.
713 735
    let mut tests: [TestDesc; MAX_TESTS] = undefined;
714 736
    let mut testCount: u32 = 0;
715 737
716 738
    for modIdx in 0..ctx.graph.entriesLen {
717 739
        if let entry = module::get(&ctx.graph, modIdx as u16) {
718 -
            collectModuleTests(entry, &mut tests[..], &mut testCount);
740 +
            if entry.packageId == entryPkg.id {
741 +
                collectModuleTests(entry, &mut tests[..], &mut testCount);
742 +
            }
719 743
        }
720 744
    }
721 745
    if testCount == 0 {
722 746
        throw error(&["fatal:", "no test functions found"]);
723 747
    }
781 805
    let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe));
782 806
    let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a);
783 807
    let fnAttrs = ast::Attributes { list: attrList };
784 808
785 809
    return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl {
786 -
        name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs,
810 +
        name: fnName, regions: &[], sig: fnSig, body: fnBody, attrs: fnAttrs,
787 811
    }));
788 812
}
789 813
790 814
/// Build a block node with a declaration appended to its statement list.
791 815
unsafe fn injectIntoBlock(
856 880
) throws (Error) {
857 881
    if entries.len == 0 {
858 882
        return;
859 883
    }
860 884
    // Use remaining arena space as serialization buffer.
861 -
    let buf = alloc::remainingBuf(arena);
885 +
    let buf = alloc::remainingBuf(&mut *arena);
862 886
    let mut pos: u32 = 0;
863 887
864 888
    for i in 0..entries.len {
865 889
        let entry = &entries[i];
866 890
        let modEntry = module::get(graph, entry.moduleId) else {
875 899
    }
876 900
    try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT);
877 901
}
878 902
879 903
/// Run the resolver on the parsed modules.
880 -
unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) {
881 -
    let mut mainArena = alloc::new(&mut MAIN_ARENA[..]);
904 +
unsafe fn runResolver 'arena (ctx: *unsafe mut CompileContext, mainArena: &'arena mut alloc::Arena, nodeCount: u32) -> resolver::Resolver 'arena throws (Error) {
882 905
    let entryPkg = try getEntryPackage(ctx);
883 906
884 907
    pkgLog(&entryPkg, &["resolving", ".."]);
885 908
886 909
    let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData);
887 -
    let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData));
910 +
    let nodeDataPtr = try! alloc::alloc(&mut *mainArena, nodeDataSize, @alignOf(resolver::NodeData));
888 911
    let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount);
889 912
    let storage = resolver::ResolverStorage {
890 -
        arena: mainArena,
891 913
        nodeData,
892 914
        pkgScope: &mut RESOLVER_PKG_SCOPE,
893 915
        errors: &mut RESOLVER_ERRORS[..],
894 916
    };
895 -
    let mut res = resolver::resolver(storage, ctx.config);
917 +
    let mut res = resolver::resolver(mainArena, storage, ctx.config);
896 918
897 919
    // Build the semantic package list consumed by the resolver.
898 920
    let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined;
899 921
    let mut resolverPackageCount: u32 = 0;
900 922
    for i in 0..ctx.packageCount {
942 964
    pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]);
943 965
944 966
    let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else {
945 967
        throw error(&["error reading assembly file"]);
946 968
    };
947 -
    let source = &ASM_SOURCE_BUF[..sourceLen];
948 -
    if source.len == ASM_SOURCE_BUF.len {
969 +
    let input = &ASM_SOURCE_BUF[..sourceLen];
970 +
    if input.len == ASM_SOURCE_BUF.len {
949 971
        throw error(&["fatal:", "assembly source too large:", path]);
950 972
    }
973 +
    // Assembly symbols borrow source bytes until final linking.
974 +
    let buffer = try alloc::allocSlice(arena, 1, 1, input.len) catch {
975 +
        throw error(&["assembly source workspace exhausted"]);
976 +
    };
977 +
    let source = buffer as *mut [u8];
978 +
    try! mem::copy(source, input);
951 979
    let program = try asm::assemble(
952 980
        asm::scanner::SourceKind::File { path },
953 981
        source,
954 982
        &mut ASM_TEXT_BUF[..],
955 983
        &mut ASM_DATA_BUF[..],
987 1015
        }
988 1016
    }
989 1017
    return &ASM_RO_DATA_BUF[..*asmDataLen];
990 1018
}
991 1019
1020 +
/// Generate dependency packages before the entry package.
1021 +
unsafe fn generateAllPackagesInto 'arena 'phase (
1022 +
    ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase,
1023 +
    generator: &mut rv64::Generator, fnArena: &mut alloc::Arena
1024 +
) throws (Error) where 'arena: 'phase {
1025 +
    let entryIdx = ctx.entryPkgIdx else panic "generateAllPackagesInto: no entry package";
1026 +
    for i in 0..ctx.packageCount {
1027 +
        if i <> entryIdx {
1028 +
            try generatePackageInto(ctx, low, &ctx.packages[i], false, generator, fnArena);
1029 +
        }
1030 +
    }
1031 +
    try generatePackageInto(ctx, low, &ctx.packages[entryIdx], true, generator, fnArena);
1032 +
}
1033 +
1034 +
/// Generate all functions in one package.
1035 +
unsafe fn generatePackageInto 'arena 'phase (
1036 +
    ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase,
1037 +
    pkg: &package::Package, isEntry: bool,
1038 +
    generator: &mut rv64::Generator, fnArena: &mut alloc::Arena
1039 +
) throws (Error) where 'arena: 'phase {
1040 +
    let rootId = pkg.rootModuleId else throw error(&["no root module found"]);
1041 +
    lower::setPackage(low, pkg.name);
1042 +
    try generateModuleTree(ctx, low, rootId, isEntry, pkg, generator, fnArena);
1043 +
}
1044 +
1045 +
/// Lower and consume each function before visiting the next declaration.
1046 +
unsafe fn generateModuleTree 'arena 'phase (
1047 +
    ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase,
1048 +
    modId: u16, isRoot: bool, pkg: &package::Package,
1049 +
    generator: &mut rv64::Generator, fnArena: &mut alloc::Arena
1050 +
) throws (Error) where 'arena: 'phase {
1051 +
    let entry = module::get(&ctx.graph, modId) else throw error(&["module entry not found"]);
1052 +
    let modAst = module::astFor(entry) else throw error(&["module has no AST"]);
1053 +
    pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]);
1054 +
    set low.currentMod = modId;
1055 +
    let mut cursor = try lower::moduleCursor(modAst, isRoot) catch err {
1056 +
        io::printError("radiance: internal error during lowering: ");
1057 +
        lower::printError(err);
1058 +
        io::printError("\n");
1059 +
        throw Error::Other;
1060 +
    };
1061 +
    loop {
1062 +
        let result = try lower::lowerNext(low, &mut cursor, fnArena) catch err {
1063 +
            io::printError("radiance: internal error during lowering: ");
1064 +
            lower::printError(err);
1065 +
            io::printError("\n");
1066 +
            throw Error::Other;
1067 +
        };
1068 +
        let next = result else break;
1069 +
        codegen::emit(generator, fnArena, &*next.function, next.role);
1070 +
    }
1071 +
    for i in 0..module::childCount(entry) {
1072 +
        let childId = module::childAt(entry, i);
1073 +
        try generateModuleTree(ctx, low, childId, false, pkg, generator, fnArena);
1074 +
    }
1075 +
}
1076 +
992 1077
/// Lower all packages while streaming each lowered function into RV64 codegen.
993 -
unsafe fn lowerAndGenerateAllPackages(
1078 +
unsafe fn lowerAndGenerateAllPackages 'arena (
994 1079
    ctx: *unsafe mut CompileContext,
995 -
    res: *unsafe mut resolver::Resolver,
1080 +
    res: *unsafe mut resolver::Resolver 'arena,
996 1081
    fnArena: &mut alloc::Arena,
997 1082
    codegenOptions: CodegenOptions
998 1083
) -> rv64::Program throws (Error) {
999 1084
    let entryIdx = ctx.entryPkgIdx else {
1000 1085
        panic "lowerAndGenerateAllPackages: no entry package";
1011 1096
        case CodegenEntryMode::DefaultEntry => {
1012 1097
            set entryPatch = rv64::EntryPatch::Reserved(nil);
1013 1098
        }
1014 1099
        else => {}
1015 1100
    }
1016 -
    let mut generator = try rv64::beginProgram(
1017 -
        rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug, placement: rv64::image::Placement::Hosted },
1018 -
        &mut res.arena
1019 -
    ) catch { throw error(&["code generation workspace exhausted"]); };
1020 -
    let mut codegenCtx = codegen::Context {
1021 -
        generator: &mut generator,
1022 -
        fnArena: (&mut *fnArena) as *unsafe mut alloc::Arena,
1101 +
    let emitterStorage = emit::Storage {
1102 +
        code: &mut CODEGEN_INSTRUCTIONS[..],
1103 +
        pendingBranches: &mut CODEGEN_PENDING_BRANCHES[..],
1104 +
        pendingCalls: &mut CODEGEN_PENDING_CALLS[..],
1105 +
        pendingJumps: &mut CODEGEN_PENDING_JUMPS[..],
1106 +
        pendingAddrLoads: &mut CODEGEN_PENDING_ADDR_LOADS[..],
1107 +
        blockOffsets: &mut CODEGEN_BLOCK_OFFSETS[..],
1108 +
        funcEntries: &mut CODEGEN_FUNC_ENTRIES[..],
1109 +
        funcs: &mut CODEGEN_FUNCS[..],
1110 +
        debugEntries: &mut CODEGEN_DEBUG_ENTRIES[..],
1023 1111
    };
1024 -
    let mut low = lower::lowerer(
1025 -
        res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options
1112 +
    let mut generator = rv64::beginProgramWithStorage(
1113 +
        rv64::ProgramOptions {
1114 +
            entryPatch,
1115 +
            debug: codegenOptions.debug,
1116 +
            placement: rv64::image::Placement::Hosted,
1117 +
        },
1118 +
        emitterStorage
1026 1119
    );
1027 -
    set low.output = lower::FnOutput::Stream(lower::FnSink {
1028 -
        ctx: &mut codegenCtx as *unsafe mut opaque,
1029 -
        emitFn: codegen::emit,
1030 -
    });
1031 -
    let mut asmDataLen: u32 = 0;
1032 -
    if let path = startupPath {
1033 -
        try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena);
1034 -
    }
1035 -
    try lowerAllPackagesInto(ctx, res, &mut low);
1036 -
    let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena);
1120 +
    let arena = (&mut *res.arena) as *unsafe mut alloc::Arena;
1121 +
    let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in {
1122 +
        let mut low = lower::lowerer(
1123 +
            resolved, graph, entryPkg.name, arena, options
1124 +
        );
1125 +
        let mut asmDataLen: u32 = 0;
1126 +
        if let path = startupPath {
1127 +
            try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, arena);
1128 +
        }
1129 +
        try generateAllPackagesInto(ctx, &mut low, &mut generator, fnArena);
1130 +
        let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, arena);
1037 1131
1038 -
    match generator.entryPatch {
1039 -
        case rv64::EntryPatch::Reserved(targetName) => {
1040 -
            if targetName == nil {
1041 -
                throw error(&["fatal:", "no default function found"]);
1132 +
        match generator.entryPatch {
1133 +
            case rv64::EntryPatch::Reserved(targetName) => {
1134 +
                if targetName == nil {
1135 +
                    throw error(&["fatal:", "no default function found"]);
1136 +
                }
1137 +
            }
1138 +
            else => {
1042 1139
            }
1043 1140
        }
1044 -
        else => {}
1045 -
    }
1046 -
    if let path = codegenOptions.logPath {
1047 -
        pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1141 +
        if let path = codegenOptions.logPath {
1142 +
            pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1143 +
        }
1144 +
        return try rv64::finishProgram(
1145 +
            generator, low.data, storage, asmData,
1146 +
            &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]
1147 +
        ) catch err {
1148 +
            match err {
1149 +
                case rv64::Error::Allocation => throw error(&["code generation workspace exhausted"]),
1150 +
                case rv64::Error::Capacity => throw error(&["code generation output capacity exceeded"]),
1151 +
                case rv64::Error::Symbol => throw error(&["code generation has an unresolved symbol"]),
1152 +
                case rv64::Error::Relocation => throw error(&["code generation relocation is out of range"]),
1153 +
                case rv64::Error::Image(_) => throw error(&["code generation image layout is invalid"]),
1154 +
                case rv64::Error::Data(_) => throw error(&["code generation data layout is invalid"]),
1155 +
            }
1156 +
        };
1048 1157
    }
1049 -
    return try rv64::finishProgram(&mut generator, low.data, storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]) catch {
1050 -
        throw error(&["code generation failed: capacity, symbol, relocation, or image layout"]);
1051 -
    };
1052 1158
}
1053 1159
1054 1160
/// Source exports selected for one binary RIL package.
1055 1161
record PackageExports: Copy {
1056 1162
    /// Number of initialized entries in the caller's export table.
1069 1175
) -> PackageExports throws (Error) {
1070 1176
    let mut count: u32 = 0;
1071 1177
    let mut entryName: ?*[u8] = nil;
1072 1178
    for i in 0..ctx.graph.entriesLen {
1073 1179
        let modEntry = module::get(&ctx.graph, i as u16) else continue;
1074 -
        if modEntry.packageId <> pkg.id { continue; }
1075 -
        let root = modEntry.ast else continue;
1180 +
        if modEntry.packageId <> pkg.id {
1181 +
            continue;
1182 +
        }
1183 +
        let root = module::astFor(modEntry) else continue;
1076 1184
        let case ast::NodeValue::Block(block) = root.value else continue;
1077 1185
        for node in block.statements {
1078 1186
            let mut ident: ?*ast::Node = nil;
1079 1187
            let mut attrs: ?ast::Attributes = nil;
1080 1188
            let mut kind = binary::ExportKind::Function;
1081 1189
            match node.value {
1082 -
                case ast::NodeValue::FnDecl(decl) => { set ident = decl.name; set attrs = decl.attrs; },
1190 +
                case ast::NodeValue::FnDecl(decl) => {
1191 +
                    set ident = decl.name;
1192 +
                    set attrs = decl.attrs;
1193 +
                },
1083 1194
                case ast::NodeValue::ConstDecl(decl) => {
1084 1195
                    set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data;
1085 1196
                },
1086 1197
                case ast::NodeValue::StaticDecl(decl) => {
1087 1198
                    set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data;
1088 1199
                },
1089 1200
                else => continue,
1090 1201
            }
1091 1202
            let attributes = attrs else continue;
1203 +
            if ast::attributesContains(&attributes, ast::Attribute::Intrinsic) {
1204 +
                continue;
1205 +
            }
1092 1206
            let isDefault = ast::attributesContains(&attributes, ast::Attribute::Default)
1093 1207
                and pkg.rootModuleId == modEntry.id;
1094 1208
            if not isDefault and not ast::attributesContains(&attributes, ast::Attribute::Export) {
1095 1209
                continue;
1096 1210
            }
1099 1213
            let qualified = il::formatQualifiedName(arena, module::moduleQualifiedPath(modEntry), name);
1100 1214
            let mut present = false;
1101 1215
            match kind {
1102 1216
                case binary::ExportKind::Function => {
1103 1217
                    for func in ilProgram.fns {
1104 -
                        if mem::eq(func.name, qualified) { set present = true; }
1218 +
                        if mem::eq(func.name, qualified) {
1219 +
                            set present = true;
1220 +
                        }
1105 1221
                    }
1106 1222
                },
1107 1223
                case binary::ExportKind::Data => {
1108 1224
                    for item in ilProgram.data {
1109 -
                        if mem::eq(item.name, qualified) { set present = true; }
1225 +
                        if mem::eq(item.name, qualified) {
1226 +
                            set present = true;
1227 +
                        }
1110 1228
                    }
1111 1229
                },
1112 1230
            }
1113 -
            if not present { continue; }
1114 -
            if count == exports.len { throw error(&["too many package exports"]); }
1231 +
            if not present {
1232 +
                continue;
1233 +
            }
1234 +
            if count == exports.len {
1235 +
                throw error(&["too many package exports"]);
1236 +
            }
1115 1237
            set exports[count] = binary::Export { name: qualified, kind };
1116 1238
            set count += 1;
1117 -
            if isDefault { set entryName = qualified; }
1239 +
            if isDefault {
1240 +
                set entryName = qualified;
1241 +
            }
1118 1242
        }
1119 1243
    }
1120 1244
    return PackageExports { count, entry: entryName };
1121 1245
}
1122 1246
1123 1247
/// Emit one binary RIL file per package into an existing directory.
1124 -
unsafe fn emitPackages(ctx: *unsafe mut CompileContext, res: *unsafe mut resolver::Resolver, directory: *[u8]) throws (Error) {
1248 +
unsafe fn emitPackages 'arena (
1249 +
    ctx: *unsafe mut CompileContext,
1250 +
    res: *unsafe mut resolver::Resolver 'arena,
1251 +
    directory: *[u8]
1252 +
) throws (Error) {
1125 1253
    for i in 0..ctx.packageCount {
1126 1254
        if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil {
1127 1255
            throw error(&["binary RIL output requires Radiance source modules"]);
1128 1256
        }
1129 1257
    }
1130 1258
    let unified = try lowerAllPackages(ctx, res);
1131 -
    let allocator = alloc::arenaAllocator(&mut res.arena);
1259 +
    let allocator = alloc::arenaAllocator(&mut *res.arena);
1132 1260
    for i in 0..ctx.packageCount {
1133 1261
        let pkg = &ctx.packages[i];
1134 1262
        let mut dataItems: *mut [il::Data] = &mut [];
1135 1263
        let mut functions: *unsafe mut [*unsafe il::Fn] = &mut [];
1136 1264
        for item in unified.data {
1137 -
            if ownsSymbol(pkg.name, item.name) { dataItems.append(item, allocator); }
1265 +
            if ownsSymbol(pkg.name, item.name) {
1266 +
                dataItems.append(item, allocator);
1267 +
            }
1138 1268
        }
1139 1269
        for func in unified.fns {
1140 -
            if ownsSymbol(pkg.name, func.name) { functions.append(func, allocator); }
1270 +
            if ownsSymbol(pkg.name, func.name) {
1271 +
                functions.append(func, allocator);
1272 +
            }
1141 1273
        }
1142 1274
        let local = il::Program { data: &dataItems[..], fns: functions };
1143 -
        let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut res.arena);
1275 +
        let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut *res.arena);
1144 1276
        let mut dependencies: [*[u8]; MAX_PACKAGES] = undefined;
1145 1277
        let mut names = collect::new(&mut PACKAGE_SYMBOLS[..], &mut dependencies[..]);
1146 1278
        let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch {
1147 1279
            throw error(&["cannot collect binary RIL package", pkg.name]);
1148 1280
        };
1168 1300
    let suffix = mem::stripPrefix(owner, name) else return false;
1169 1301
    return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':';
1170 1302
}
1171 1303
1172 1304
/// Lower, optionally dump, and optionally generate binary output.
1173 -
unsafe fn compile(
1305 +
unsafe fn compile 'arena (
1174 1306
    ctx: *unsafe mut CompileContext,
1175 -
    res: *unsafe mut resolver::Resolver,
1307 +
    res: *unsafe mut resolver::Resolver 'arena,
1176 1308
    fnArena: &mut alloc::Arena
1177 1309
) throws (Error) {
1178 1310
    let entryPkg = try getEntryPackage(ctx);
1179 1311
    if let directory = ctx.rilDirectory {
1180 1312
        try emitPackages(ctx, res, directory);
1193 1325
        let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions {
1194 1326
            logPath: nil,
1195 1327
            debug: false,
1196 1328
            entryMode: CodegenEntryMode::None,
1197 1329
        });
1198 -
        printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs, &mut res.arena);
1330 +
        printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs, res.arena);
1199 1331
        io::print("\n");
1200 1332
1201 1333
        return;
1202 1334
    }
1203 1335
    // Generate binary output if path specified.
1223 1355
        throw error(&["fatal:", "failed to write output file"]);
1224 1356
    }
1225 1357
1226 1358
    // Write debug info file if enabled.
1227 1359
    if ctx.debug {
1228 -
        try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena);
1360 +
        try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, res.arena);
1229 1361
    }
1230 1362
    pkgLog(&entryPkg, &["ok", "(", outPath, ")"]);
1231 1363
}
1232 1364
1233 1365
@default unsafe fn main(env: *sys::Env) -> i32 {
1253 1385
        try generateTestRunner(&mut ctx, &mut arena) catch {
1254 1386
            return 1;
1255 1387
        };
1256 1388
    }
1257 1389
    // Run resolution phase.
1258 -
    let mut res = try runResolver(&mut ctx, arena.nextId) catch {
1259 -
        return 1;
1260 -
    };
1261 -
    let mut fnArena = alloc::new(&mut FN_ARENA[..]);
1390 +
    let mut mainArena = alloc::new(&mut MAIN_ARENA[..]);
1391 +
    let arenaRef: 'arena = &mut mainArena in {
1392 +
        let mut res = try runResolver(&mut ctx, arenaRef, arena.nextId) catch {
1393 +
            return 1;
1394 +
        };
1395 +
        let mut fnArena = alloc::new(&mut FN_ARENA[..]);
1262 1396
1263 -
    // Lower, dump, and/or generate output.
1264 -
    try compile(&mut ctx, &mut res, &mut fnArena) catch {
1265 -
        return 1;
1266 -
    };
1267 -
    return 0;
1397 +
        // Lower, dump, and/or generate output.
1398 +
        try compile(&mut ctx, &mut res, &mut fnArena) catch {
1399 +
            return 1;
1400 +
        };
1401 +
        return 0;
1402 +
    }
1268 1403
}
compiler/radiance/codegen.rad +9 -18
3 3
use std::arch::rv64;
4 4
use std::lang::alloc;
5 5
use std::lang::il;
6 6
use std::lang::lower;
7 7
8 -
/// State carried by the streaming lowerer/codegen callback.
9 -
export record Context: Copy {
10 -
    /// Generator receiving functions. It must remain valid for every callback.
11 -
    generator: *unsafe mut rv64::Generator,
12 -
    /// Arena for function IL. It must remain valid for every callback.
13 -
    fnArena: *unsafe mut alloc::Arena,
14 -
}
15 -
16 8
/// Emit one lowered function to machine code and reclaim its IL arena.
17 -
/// The context must point to a valid `Context` with live generator and arena storage.
18 -
export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *unsafe il::Fn, role: lower::FnRole) {
19 -
    let ctx = ctxPtr as *unsafe mut Context;
20 -
9 +
/// The function's storage can be reclaimed after code generation returns.
10 +
export unsafe fn emit(
11 +
    generator: &mut rv64::Generator, arena: &mut alloc::Arena,
12 +
    func: &il::Fn, role: lower::FnRole
13 +
) {
21 14
    match role {
22 15
        case lower::FnRole::Default => {
23 -
            rv64::recordFunctionAlias(ctx.generator, super::DEFAULT_ENTRY_SYMBOL);
24 -
            match ctx.generator.entryPatch {
16 +
            rv64::recordFunctionAlias(generator, super::DEFAULT_ENTRY_SYMBOL);
17 +
            match generator.entryPatch {
25 18
                case rv64::EntryPatch::Reserved(_) => {
26 -
                    set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name);
19 +
                    set generator.entryPatch = rv64::EntryPatch::Reserved(func.name);
27 20
                }
28 21
                // Startup assembly calls the default function through its
29 22
                // default entry symbol when no entry jump is reserved.
30 23
                case rv64::EntryPatch::None => {}
31 24
            }
32 25
        }
33 26
        else => {}
34 27
    }
35 -
    let generator = ctx.generator;
36 -
    let arena = ctx.fnArena;
37 28
    rv64::generateFunction(generator, func, arena);
38 -
    alloc::reset(ctx.fnArena);
29 +
    alloc::reset(arena);
39 30
}
lib/std/arch/rv64.rad +73 -25
10 10
//! * isel: Instruction selection (IL to RV64 instructions)
11 11
//! * printer: Assembly text output
12 12
13 13
export mod image;
14 14
export mod atomics;
15 -
export mod shared;
16 15
export mod encode;
17 16
export mod decode;
18 17
export mod emit;
19 18
export mod isel;
20 19
export mod printer;
21 20
export mod asm;
21 +
export mod shared;
22 22
23 23
@test mod tests;
24 24
@test mod bounds;
25 25
@test mod atomicTests;
26 26
110 110
export constant INSTR_SIZE: i32 = 4;
111 111
/// Stack alignment requirement in bytes.
112 112
export constant STACK_ALIGNMENT: i32 = 16;
113 113
114 114
/// Minimum blit size (in bytes) to use a loop instead of inline copy.
115 -
/// Blits below this threshold are fully unrolled as LD/SD pairs.
116 -
export constant BLIT_LOOP_THRESHOLD: i32 = 256;
115 +
/// Blits below this threshold use unrolled byte loads and stores.
116 +
export constant BLIT_LOOP_THRESHOLD: i32 = 33;
117 117
118 118
/////////////////////////
119 119
// Codegen Allocation  //
120 120
/////////////////////////
121 121
247 247
    let mut e = try emit::emitter(arena, options.debug) catch {
248 248
        alloc::restore(arena, checkpoint);
249 249
        throw Error::Allocation;
250 250
    };
251 251
252 +
    return beginProgramWithEmitter(options, e);
253 +
}
254 +
255 +
/// Begin RV64 code generation with caller-owned emitter storage.
256 +
export fn beginProgramWithStorage(
257 +
    options: ProgramOptions,
258 +
    storage: emit::Storage
259 +
) -> Generator {
260 +
    let e = emit::emitterWithStorage(storage, options.debug);
261 +
    return beginProgramWithEmitter(options, e);
262 +
}
263 +
264 +
/// Initialize a program generator from an emitter.
265 +
fn beginProgramWithEmitter(options: ProgramOptions, input: emit::Emitter) -> Generator {
266 +
    let mut e = input;
267 +
252 268
    // Emit placeholder entry jump when requested.
253 269
    // We'll patch this at the end once we know where the function is.
254 270
    match options.entryPatch {
255 271
        case EntryPatch::Reserved(_) => {
256 272
            emit::emit(&mut e, encode::nop()); // Placeholder for two-instruction jump.
268 284
269 285
/// Generate code for one IL function.
270 286
/// Record failures in the emitter and restore the function arena offset.
271 287
export unsafe fn generateFunction(
272 288
    generator: &mut Generator,
273 -
    func: *unsafe il::Fn,
289 +
    func: &il::Fn,
274 290
    arena: &mut alloc::Arena
275 291
) {
276 292
    if generator.e.error <> nil or func.isExtern {
277 293
        return;
278 294
    }
279 295
    let checkpoint = alloc::save(arena);
280 296
    let config = targetConfig();
281 -
    let ralloc = try regalloc::allocate(func, &config, arena) catch {
282 -
        alloc::restore(arena, checkpoint);
283 -
        set generator.e.error = Error::Allocation;
284 -
        return;
285 -
    };
286 -
287 -
    isel::selectFn(&mut generator.e, &ralloc, func);
297 +
    use *arena as scratch in {
298 +
        try generateFunctionWithStorage(generator, func, &config, &scratch) catch {
299 +
            set generator.e.error = Error::Allocation;
300 +
        };
301 +
    }
288 302
289 303
    // Reclaim unused memory after instruction selection.
290 304
    alloc::restore(arena, checkpoint);
291 305
}
292 306
307 +
/// Allocate registers and select one function within a scratch session.
308 +
unsafe fn generateFunctionWithStorage 'scratch (
309 +
    generator: &mut Generator,
310 +
    func: &il::Fn,
311 +
    config: &regalloc::TargetConfig,
312 +
    storage: &Session 'scratch
313 +
) throws (alloc::AllocError) {
314 +
    let ralloc = try regalloc::allocate(func, config, storage);
315 +
    isel::selectFn(&mut generator.e, &ralloc, func);
316 +
}
317 +
293 318
/// Record an alternate name for the next function emitted.
294 319
export fn recordFunctionAlias(generator: &mut Generator, name: *[u8]) {
295 320
    let codeLen = generator.e.codeLen;
296 321
    emit::recordFuncOffsetAt(&mut generator.e, name, codeLen);
297 322
}
336 361
    }
337 362
}
338 363
339 364
/// Finish RV64 code generation and return the emitted program.
340 365
export unsafe fn finishProgram(
341 -
    generator: &mut Generator,
366 +
    input: Generator,
342 367
    globalData: &[il::Data],
343 368
    storage: Storage,
344 369
    roDataPrefix: *[u8],
345 370
    roDataBuf: &mut [u8],
346 371
    rwDataBuf: &mut [u8]
347 372
) -> Program throws (Error) {
373 +
    let mut generator = input;
348 374
    try emit::check(&generator.e);
349 375
    let mut roBase: u64 = RO_DATA_BASE as u64;
350 376
    let mut rwBase: u64 = RW_DATA_BASE as u64;
351 377
    match generator.placement {
352 378
        case image::Placement::Physical { roData, rwData, .. } => {
353 379
            set roBase = roData;
354 380
            set rwBase = rwData;
355 381
        },
356 -
        else => {},
382 +
        else => {
383 +
        },
357 384
    }
358 385
    // Build data map after function lowering. Function-local literals can add
359 386
    // global data while functions are lowered, so final layout belongs here.
360 387
    let case Storage { dataSyms: symbolBuf, dataSymEntries } = storage
361 388
        else panic "expected code generation storage";
362 389
    let mut dataSymCount: u32 = 0;
363 390
    let roLayoutSize = try data::layoutSectionAtOffset(
364 391
        globalData, symbolBuf, &mut dataSymCount, roBase, roDataPrefix.len, true
365 -
    ) catch err { throw Error::Data(err); };
366 -
    let rwLayoutSize = try data::layoutSection(globalData, symbolBuf, &mut dataSymCount, rwBase, false) catch err { throw Error::Data(err); };
392 +
    ) catch err {
393 +
        throw Error::Data(err);
394 +
    };
395 +
    let rwLayoutSize = try data::layoutSection(globalData, symbolBuf, &mut dataSymCount, rwBase, false) catch err {
396 +
        throw Error::Data(err);
397 +
    };
367 398
368 399
    let dataSyms = &symbolBuf[..dataSymCount];
369 -
    let dataSymMap = try data::buildMap(dataSyms, dataSymEntries) catch err { throw Error::Data(err); };
400 +
    let dataSymMap = try data::buildMap(dataSyms, dataSymEntries) catch err {
401 +
        throw Error::Data(err);
402 +
    };
370 403
    if roBase > 0xffffffffffffffff - roLayoutSize as u64 - 7 {
371 404
        throw Error::Image(image::Error::Overflow);
372 405
    }
373 406
    let mut codeBase: u64 = (roBase + roLayoutSize as u64 + 7) & ~7;
374 407
    let mut entry = codeBase;
375 408
    match generator.placement {
376 409
        case image::Placement::Physical { code, entry: address, .. } => {
377 410
            set codeBase = code;
378 411
            set entry = address;
379 412
        },
380 -
        else => {},
413 +
        else => {
414 +
        },
381 415
    }
382 416
    let codeBytes = generator.e.codeLen * 4;
383 417
    let mut layout = image::Layout {
384 418
        entry,
385 419
        code: image::Segment { address: codeBase, initialized: codeBytes, memory: codeBytes },
386 420
        roData: image::Segment { address: roBase, initialized: 0, memory: roLayoutSize },
387 421
        rwData: image::Segment { address: rwBase, initialized: 0, memory: rwLayoutSize },
388 422
    };
389 -
    try image::validate(layout) catch err { throw Error::Image(err); };
423 +
    try image::validate(layout) catch err {
424 +
        throw Error::Image(err);
425 +
    };
390 426
391 427
    match generator.entryPatch {
392 428
        case EntryPatch::Reserved(targetName) => {
393 429
            let target = targetName else {
394 430
                throw Error::Symbol;
407 443
    try emit::patchAddrLoads(&mut generator.e, &dataSymMap, codeBase);
408 444
409 445
    try emit::check(&generator.e);
410 446
411 447
    // Emit data sections.
412 -
    if roDataPrefix.len > roDataBuf.len { throw Error::Capacity; }
448 +
    if roDataPrefix.len > roDataBuf.len {
449 +
        throw Error::Capacity;
450 +
    }
413 451
    try! mem::copy(roDataBuf, roDataPrefix);
414 452
415 453
    let roDataSize = try data::emitSectionAtOffset(
416 454
        globalData, &dataSymMap, &generator.e.labels, codeBase, roDataBuf, true, roDataPrefix.len
417 -
    ) catch err { throw Error::Data(err); };
455 +
    ) catch err {
456 +
        throw Error::Data(err);
457 +
    };
418 458
    let rwDataSize = try data::emitSection(
419 459
        globalData, &dataSymMap, &generator.e.labels, codeBase, rwDataBuf, false
420 -
    ) catch err { throw Error::Data(err); };
460 +
    ) catch err {
461 +
        throw Error::Data(err);
462 +
    };
421 463
    set layout.roData.initialized = roDataSize;
422 464
    set layout.rwData.initialized = rwDataSize;
423 -
    try image::validate(layout) catch err { throw Error::Image(err); };
465 +
    try image::validate(layout) catch err {
466 +
        throw Error::Image(err);
467 +
    };
468 +
    let case Generator { e, .. } = generator else panic "expected program generator";
469 +
    let case emit::Emitter {
470 +
        code, codeLen, funcs, funcsLen, debugEntries, debugEntriesLen, ..
471 +
    } = e else panic "expected program emitter";
424 472
    return Program {
425 -
        code: emit::getCode(&generator.e),
426 -
        funcs: &generator.e.funcs[..],
473 +
        code: &code[..codeLen],
474 +
        funcs: &funcs[..funcsLen],
427 475
        roDataSize,
428 476
        rwDataSize,
429 -
        debugEntries: emit::getDebugEntries(&generator.e),
477 +
        debugEntries: &debugEntries[..debugEntriesLen],
430 478
        layout,
431 479
    };
432 480
}
lib/std/arch/rv64/asm.rad +50 -9
406 406
    { name: "tp",   reg: rv64::TP },
407 407
    { name: "zero", reg: rv64::ZERO },
408 408
];
409 409
410 410
/// Sorted CSR-name lookup table used by the assembler parser.
411 -
export constant CSRS: [CsrEntry; 9] = [
411 +
export constant CSRS: [CsrEntry; 10] = [
412 +
    { name: "instret",  csr: 0xC02 },
412 413
    { name: "mcause",   csr: 0x342 },
413 414
    { name: "mepc",     csr: 0x341 },
414 415
    { name: "mhartid",  csr: 0xF14 },
415 416
    { name: "mie",      csr: 0x304 },
416 417
    { name: "mip",      csr: 0x344 },
460 461
    arena: *unsafe mut alloc::Arena,
461 462
    /// Assembler lexical scanner.
462 463
    scan: scanner::Scanner,
463 464
    /// Output text buffer.
464 465
    text: *mut [u32],
466 +
    /// Number of emitted text words.
467 +
    textLen: u32,
465 468
    /// Output data buffer.
466 469
    data: *mut [u8],
470 +
    /// Number of emitted data bytes.
471 +
    dataLen: u32,
467 472
    /// Current output section.
468 473
    section: Section,
469 474
    /// Defined symbols.
470 475
    symbols: *mut [Symbol],
476 +
    /// Number of defined symbols.
477 +
    symbolsLen: u32,
471 478
    /// Name-to-symbol index map.
472 479
    symbolMap: dict::Dict,
473 480
    /// Name-to-integer map.
474 481
    constMap: dict::Dict,
475 482
    /// Names marked by `.export`.
476 483
    exportMap: dict::Dict,
477 484
    /// Pending fixups.
478 485
    fixups: *mut [Fixup],
486 +
    /// Number of pending fixups.
487 +
    fixupsLen: u32,
479 488
    /// Fixups that reference text outside this assembly fragment.
480 489
    externalFixups: *mut [Fixup],
490 +
    /// Number of external fixups.
491 +
    externalFixupsLen: u32,
481 492
    /// Absolute runtime address of data-section offset zero.
482 493
    dataBase: u32,
483 494
}
484 495
485 496
/// Assemble source using `dataBase` as the runtime address of the data-section.
490 501
    dataBuf: *mut [u8],
491 502
    arena: &mut alloc::Arena,
492 503
    pool: *unsafe mut strings::Pool,
493 504
    dataBase: u32
494 505
) -> Program throws (Error) {
495 -
    let slotCap = source.len + SOURCE_CAP_PADDING;
506 +
    let slotCap = tokenCapacity(sourceKind, source, pool);
496 507
    let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE);
497 508
498 509
    let symbols = try! alloc::allocSlice(arena, @sizeOf(Symbol), @alignOf(Symbol), slotCap);
499 510
    let fixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap);
500 511
    let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap);
506 517
    let fixupBuf = fixups as *mut [Fixup];
507 518
    let externalBuf = externalFixups as *mut [Fixup];
508 519
    let mut a = Assembler {
509 520
        arena: arena as *unsafe mut alloc::Arena,
510 521
        scan: scanner::scanner(sourceKind, source, pool),
511 -
        text: &mut textBuf[..0],
512 -
        data: &mut dataBuf[..0],
522 +
        text: textBuf,
523 +
        textLen: 0,
524 +
        data: dataBuf,
525 +
        dataLen: 0,
513 526
        section: Section::Text,
514 -
        symbols: &mut symbolBuf[..0],
527 +
        symbols: symbolBuf,
528 +
        symbolsLen: 0,
515 529
        symbolMap: dict::init(entries as *mut [dict::Entry]),
516 530
        constMap: dict::init(constEntries as *mut [dict::Entry]),
517 531
        exportMap: dict::init(exportEntries as *mut [dict::Entry]),
518 -
        fixups: &mut fixupBuf[..0],
519 -
        externalFixups: &mut externalBuf[..0],
532 +
        fixups: fixupBuf,
533 +
        fixupsLen: 0,
534 +
        externalFixups: externalBuf,
535 +
        externalFixupsLen: 0,
520 536
        dataBase,
521 537
    };
522 538
    // Parse assembly source and emit instructions.
523 539
    try parser::parseProgram(&mut a);
524 540
    // Resolve fixups and finalize program.
525 541
    try emit::finishProgram(&mut a);
526 542
527 -
    let case Assembler { text, data, symbols: definedSymbols, externalFixups: pendingFixups, .. } = a
543 +
    let case Assembler {
544 +
        text, textLen, data, dataLen, symbols: definedSymbols, symbolsLen,
545 +
        externalFixups: pendingFixups, externalFixupsLen, ..
546 +
    } = a
528 547
        else panic "expected assembler state";
529 -
    return Program { text, data, symbols: definedSymbols, externalFixups: pendingFixups };
548 +
    return Program {
549 +
        text: &text[..textLen],
550 +
        data: &data[..dataLen],
551 +
        symbols: &definedSymbols[..symbolsLen],
552 +
        externalFixups: &pendingFixups[..externalFixupsLen],
553 +
    };
554 +
}
555 +
556 +
/// Bound symbol and fixup counts by lexical tokens, including one spare slot.
557 +
unsafe fn tokenCapacity(kind: scanner::SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> u32 {
558 +
    let mut scan = scanner::scanner(kind, source, pool);
559 +
    let mut count = SOURCE_CAP_PADDING;
560 +
    while true {
561 +
        let token = scanner::next(&mut scan);
562 +
        if token.kind == scanner::TokenKind::Eof {
563 +
            break;
564 +
        }
565 +
        set count += 1;
566 +
        if token.kind == scanner::TokenKind::Invalid {
567 +
            break;
568 +
        }
569 +
    }
570 +
    return count;
530 571
}
531 572
532 573
/// Return the next power of two at least as large as `value`.
533 574
fn nextPowerOfTwo(value: u32) -> u32 {
534 575
    let mut n: u32 = MIN_TABLE_CAPACITY;
lib/std/arch/rv64/asm/emit.rad +25 -12
3 3
use std::arch::rv64::encode;
4 4
use std::arch::rv64;
5 5
use std::fmt;
6 6
7 7
use std::collections::dict;
8 -
use std::lang::alloc;
9 8
use std::lang::gen;
10 9
11 10
/// Define a symbol at the current text or data offset.
12 11
export unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) {
13 -
    let idx = a.symbols.len;
14 -
    let offset: i32 = a.data.len as i32
12 +
    let idx = a.symbolsLen;
13 +
    let offset: i32 = a.dataLen as i32
15 14
        if a.section == super::Section::Data
16 -
        else a.text.len as i32 * rv64::INSTR_SIZE;
15 +
        else a.textLen as i32 * rv64::INSTR_SIZE;
17 16
18 -
    a.symbols.append(super::Symbol {
17 +
    assert a.symbolsLen < a.symbols.len, "defineSymbol: symbol buffer full";
18 +
    set a.symbols[a.symbolsLen] = super::Symbol {
19 19
        name,
20 20
        section: a.section,
21 21
        offset,
22 22
        isExported: dict::get(&a.exportMap, name) <> nil,
23 -
    }, alloc::arenaAllocator(a.arena));
23 +
    };
24 +
    set a.symbolsLen += 1;
24 25
    dict::insert(&mut a.symbolMap, name, idx as i32);
25 26
}
26 27
27 28
/// Append one encoded instruction word to the text section.
28 29
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
29 -
    a.text.append(word, alloc::arenaAllocator(a.arena));
30 +
    if a.textLen >= a.text.len {
31 +
        throw super::Error::TextOverflow;
32 +
    }
33 +
    set a.text[a.textLen] = word;
34 +
    set a.textLen += 1;
30 35
}
31 36
32 37
/// Append `words` no-op instructions to the text section.
33 38
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
34 39
    for _ in 0..words {
36 41
    }
37 42
}
38 43
39 44
/// Append one byte to the data section.
40 45
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
41 -
    a.data.append(byte, alloc::arenaAllocator(a.arena));
46 +
    if a.dataLen >= a.data.len {
47 +
        throw super::Error::DataOverflow;
48 +
    }
49 +
    set a.data[a.dataLen] = byte;
50 +
    set a.dataLen += 1;
42 51
}
43 52
44 53
/// Emit a little-endian integer with `bytes` bytes.
45 54
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
46 55
    for i in 0..bytes {
63 72
    }
64 73
}
65 74
66 75
/// Record a data-section symbol fixup and reserve its bytes.
67 76
export unsafe fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) {
68 -
    let offset = a.data.len;
77 +
    let offset = a.dataLen;
69 78
    match width {
70 79
        case super::DataWidth::Word => {
71 80
            recordFixup(a, target, super::FixupInfo::Word { offset });
72 81
            try emitDataInt(a, 0, rv64::WORD_SIZE as u32);
73 82
        }
78 87
    }
79 88
}
80 89
81 90
/// Record a pending symbol fixup.
82 91
unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
83 -
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
92 +
    assert a.fixupsLen < a.fixups.len, "recordFixup: fixup buffer full";
93 +
    set a.fixups[a.fixupsLen] = super::Fixup { symbol, info };
94 +
    set a.fixupsLen += 1;
84 95
}
85 96
86 97
/// Record a text fixup that must be resolved after all program text is known.
87 98
unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
88 -
    a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
99 +
    assert a.externalFixupsLen < a.externalFixups.len, "recordExternalFixup: fixup buffer full";
100 +
    set a.externalFixups[a.externalFixupsLen] = fixup;
101 +
    set a.externalFixupsLen += 1;
89 102
}
90 103
91 104
/// Record a text-section symbol fixup and reserve its instruction words.
92 105
export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
93 106
    recordFixup(a, symbol, info);
109 122
    return symbol.offset + (a.dataBase as i32);
110 123
}
111 124
112 125
/// Resolve final symbol references and patch all delayed output.
113 126
export unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) {
114 -
    for i in 0..a.fixups.len {
127 +
    for i in 0..a.fixupsLen {
115 128
        let fixup = a.fixups[i];
116 129
        let symbol = findSymbol(a, fixup.symbol) else {
117 130
            match fixup.info {
118 131
                case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => {
119 132
                    recordExternalFixup(a, fixup);
lib/std/arch/rv64/asm/parser.rad +29 -13
286 286
        throw failOnToken(tok, "instructions are only valid in the text section");
287 287
    }
288 288
    if let format = atomics::parse(name) {
289 289
        let rd = try parseRegister(a);
290 290
        let mut rs2 = rv64::ZERO;
291 -
        if format.operation <> 2 { set rs2 = try parseRegister(a); }
291 +
        if format.operation <> 2 {
292 +
            set rs2 = try parseRegister(a);
293 +
        }
292 294
        let memory = try parseMemory(a);
293 -
        if memory.offset <> 0 { throw fail(a, "atomic memory offset must be zero"); }
295 +
        if memory.offset <> 0 {
296 +
            throw fail(a, "atomic memory offset must be zero");
297 +
        }
294 298
        try emit::emitText(a, atomics::encode(atomics::Instruction { format, rd, rs1: memory.base, rs2 }));
295 299
        return;
296 300
    }
297 301
    let form = lookupInstruction(name) else {
298 302
        throw failOnToken(tok, "unknown instruction");
346 350
347 351
/// Parse the `la` pseudo-instruction.
348 352
unsafe fn parseLa(a: &mut super::Assembler) throws (super::Error) {
349 353
    let rd = try parseRegister(a);
350 354
    let target = try parseLabelName(a);
351 -
    let index = a.text.len;
355 +
    let index = a.textLen;
352 356
353 357
    try emit::recordTextFixup(a, target, super::FixupInfo::Addr { rd, index }, 2);
354 358
}
355 359
356 360
/// Parse a CSR read-like instruction with destination register then CSR.
468 472
    return try parseLabelName(a);
469 473
}
470 474
471 475
/// Parse a branch target as either a label fixup or immediate offset.
472 476
unsafe fn parseBranchLabel(a: &mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) {
473 -
    let index = a.text.len;
477 +
    let index = a.textLen;
474 478
    if let target = try parseOptionalLabel(a) {
475 479
        try emit::recordTextFixup(a, target, super::FixupInfo::Branch { op, rs1, rs2, index }, 1);
476 480
        return;
477 481
    }
478 482
    let imm = try parseBranchImm(a);
491 495
    try parseJ(a, rd);
492 496
}
493 497
494 498
/// Parse a jump target for `jal` or a jump pseudo-instruction.
495 499
unsafe fn parseJ(a: &mut super::Assembler, rd: gen::Reg) throws (super::Error) {
496 -
    let index = a.text.len;
500 +
    let index = a.textLen;
497 501
    if let target = try parseOptionalLabel(a) {
498 502
        try emit::recordTextFixup(a, target, super::FixupInfo::Jal { rd, index }, 1);
499 503
        return;
500 504
    }
501 505
    let imm = try parseJumpImm(a);
580 584
    if count < 0 {
581 585
        throw fail(a, "space size must be non-negative");
582 586
    }
583 587
    // The data section grows on demand; only reject sizes that cannot be
584 588
    // represented as a section offset.
585 -
    if count > super::U32_MAX_VALUE - a.data.len as i64 {
589 +
    if count > super::U32_MAX_VALUE - a.dataLen as i64 {
586 590
        throw super::Error::DataOverflow;
587 591
    }
588 592
    for _ in 0..count as u32 {
589 593
        try emit::emitByte(a, 0);
590 594
    }
606 610
    match a.section {
607 611
        case super::Section::Text => {
608 612
            if amount % rv64::INSTR_SIZE as u32 <> 0 {
609 613
                throw fail(a, "text alignment must be a multiple of 4");
610 614
            }
611 -
            let bytes = a.text.len * rv64::INSTR_SIZE as u32;
615 +
            let bytes = a.textLen * rv64::INSTR_SIZE as u32;
612 616
            let aligned = checkedAlignUp(bytes, amount) else {
613 617
                throw super::Error::TextOverflow;
614 618
            };
615 619
            let words = (aligned - bytes) / rv64::INSTR_SIZE as u32;
616 620
            try emit::emitTextPadding(a, words);
617 621
        }
618 622
        case super::Section::Data => {
619 -
            let aligned = checkedAlignUp(a.data.len, amount) else {
623 +
            let aligned = checkedAlignUp(a.dataLen, amount) else {
620 624
                throw super::Error::DataOverflow;
621 625
            };
622 -
            for _ in a.data.len..aligned {
626 +
            for _ in a.dataLen..aligned {
623 627
                try emit::emitByte(a, 0);
624 628
            }
625 629
        }
626 630
    }
627 631
}
871 875
872 876
/// Parse an access-class mask without duplicate fields.
873 877
unsafe fn parseFenceMask(a: &mut super::Assembler) -> u32 throws (super::Error) {
874 878
    if a.scan.current.kind == scanner::TokenKind::Number {
875 879
        let value = try parseValue(a);
876 -
        if value <> 0 { throw fail(a, "numeric fence mask must be zero"); }
880 +
        if value <> 0 {
881 +
            throw fail(a, "numeric fence mask must be zero");
882 +
        }
877 883
        return 0;
878 884
    }
879 885
    let token = try expectToken(a, scanner::TokenKind::Ident, "expected fence access classes");
880 886
    let mut mask: u32 = 0;
881 887
    for ch in token.source {
882 888
        let mut bit: u32 = 0;
883 889
        match ch {
884 -
            case 'i' => { set bit = 8; }, case 'o' => { set bit = 4; },
885 -
            case 'r' => { set bit = 2; }, case 'w' => { set bit = 1; },
890 +
            case 'i' => {
891 +
                set bit = 8;
892 +
            }, case 'o' => {
893 +
                set bit = 4;
894 +
            },
895 +
            case 'r' => {
896 +
                set bit = 2;
897 +
            }, case 'w' => {
898 +
                set bit = 1;
899 +
            },
886 900
            else => throw failOnToken(token, "invalid fence access class"),
887 901
        }
888 -
        if (mask & bit) <> 0 { throw failOnToken(token, "duplicate fence access class"); }
902 +
        if (mask & bit) <> 0 {
903 +
            throw failOnToken(token, "duplicate fence access class");
904 +
        }
889 905
        set mask |= bit;
890 906
    }
891 907
    return mask;
892 908
}
893 909
lib/std/arch/rv64/asm/tests.rad +62 -0
16 16
static ASM_DATA_STORAGE: [u8; 1024] = [0; 1024];
17 17
unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18 18
static PRINT_ARENA_STORAGE: [u8; 1024] = [0; 1024];
19 19
static PRINT_BUFFER: [u8; 128] = [0; 128];
20 20
21 +
/// The retired-instruction CSR assembles to its exact architectural encoding.
22 +
@test unsafe fn retiredCounter() throws (testing::TestError) {
23 +
    let program = try assembleSource(".text;\ncsrr %a0 instret;\n");
24 +
    assert program.text.len == 1 and program.text[0] == 0xc0202573;
25 +
}
26 +
27 +
/// Source buffer for comment capacity tests.
28 +
static SOURCE: [u8; 4096] = [0; 4096];
29 +
30 +
/// Long comments consume source space while leaving symbol and fixup demand small.
31 +
@test unsafe fn commentStorage() throws (testing::TestError) {
32 +
    let source = &mut SOURCE[..];
33 +
    for i in 0..source.len {
34 +
        set source[i] = 32;
35 +
    }
36 +
    set source[0] = '/'; set source[1] = '/';
37 +
    let text = "\n.text;\nret;\n";
38 +
    for byte, i in text {
39 +
        set source[source.len - text.len + i] = byte;
40 +
    }
41 +
    let program = try assembleSource(&SOURCE[..]);
42 +
    try testing::expect(program.text.len == 1 and program.text[0] == encode::ret());
43 +
}
44 +
21 45
unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) {
22 46
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
23 47
    return try super::assemble(
24 48
        scanner::SourceKind::String,
25 49
        source,
122 146
    try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default"));
123 147
    try testing::expect(addrRd == rv64::T0);
124 148
    try testing::expect(addrIndex == 1);
125 149
}
126 150
151 +
@test unsafe fn testAssembleTextOverflow() throws (testing::TestError) {
152 +
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
153 +
    try super::assemble(
154 +
        scanner::SourceKind::String,
155 +
        ".text;\nret;\nret;\n",
156 +
        &mut ASM_TEXT_STORAGE[..1],
157 +
        &mut ASM_DATA_STORAGE[..1],
158 +
        &mut arena,
159 +
        &mut ASM_STRING_POOL,
160 +
        rv64::RO_DATA_BASE
161 +
    ) catch err {
162 +
        match err {
163 +
            case super::Error::TextOverflow => return,
164 +
            else => throw testing::TestError::Failed,
165 +
        }
166 +
    };
167 +
    throw testing::TestError::Failed;
168 +
}
169 +
170 +
@test unsafe fn testAssembleDataOverflow() throws (testing::TestError) {
171 +
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
172 +
    try super::assemble(
173 +
        scanner::SourceKind::String,
174 +
        ".data;\n.byte 1, 2;\n",
175 +
        &mut ASM_TEXT_STORAGE[..1],
176 +
        &mut ASM_DATA_STORAGE[..1],
177 +
        &mut arena,
178 +
        &mut ASM_STRING_POOL,
179 +
        rv64::RO_DATA_BASE
180 +
    ) catch err {
181 +
        match err {
182 +
            case super::Error::DataOverflow => return,
183 +
            else => throw testing::TestError::Failed,
184 +
        }
185 +
    };
186 +
    throw testing::TestError::Failed;
187 +
}
188 +
127 189
@test unsafe fn testAssembleInvalidOperandsFail() throws (testing::TestError) {
128 190
    try expectAssembleFail(
129 191
        ".text;\nbeq %a0 %a1 @missing;\n"
130 192
    );
131 193
    try expectAssembleFail(
lib/std/arch/rv64/atomicTests.rad +18 -6
30 30
                    format: atomics::Format { operation, width: 2 + wide, order },
31 31
                    rd: super::A0, rs1: super::A1, rs2: super::ZERO if operation == 2 else super::A2,
32 32
                };
33 33
                let word = words[i] | (wide << 12) | (order << 25);
34 34
                try testing::expect(atomics::encode(item) == word);
35 -
                let decoded = atomics::decode(word) else { throw testing::TestError::Failed; };
35 +
                let decoded = atomics::decode(word) else {
36 +
                    throw testing::TestError::Failed;
37 +
                };
36 38
                try testing::expect(decoded == item);
37 -
                let case decode::Instr::Atomic(instruction) = decode::decode(word) else { throw testing::TestError::Failed; };
39 +
                let case decode::Instr::Atomic(instruction) = decode::decode(word) else {
40 +
                    throw testing::TestError::Failed;
41 +
                };
38 42
                try testing::expect(instruction == item);
39 43
            }
40 44
        }
41 45
    }
42 46
}
47 51
        try testing::expect(atomics::decode(word) == nil);
48 52
    }
49 53
    for name in &["lr", "lr.q", "lr.w.aq.rl", "sc.d.rl.aq", "amoswap.d.bad", "amoadd.wextra"] {
50 54
        try testing::expect(atomics::parse(name) == nil);
51 55
    }
52 -
    let format = atomics::parse("amoswap.d.aqrl") else { throw testing::TestError::Failed; };
56 +
    let format = atomics::parse("amoswap.d.aqrl") else {
57 +
        throw testing::TestError::Failed;
58 +
    };
53 59
    try testing::expect(format.operation == 1 and format.width == 3 and format.order == 3);
54 60
}
55 61
56 62
/// Assemble one instruction and return its exact word.
57 63
unsafe fn assemble(source: *[u8]) -> u32 throws (testing::TestError) {
58 64
    let mut arena = alloc::new(&mut MEMORY[..]);
59 65
    let words = &mut WORDS[..];
60 66
    let data: *mut [u8] = &mut [];
61 67
    let result = try asm::assemble(asm::scanner::SourceKind::String, source,
62 68
        &mut words[..], &mut data[..], &mut arena, &mut STRINGS, 0)
63 -
        catch { throw testing::TestError::Failed; };
69 +
        catch {
70 +
            throw testing::TestError::Failed;
71 +
        };
64 72
    try testing::expect(result.text.len == 1);
65 73
    return result.text[0];
66 74
}
67 75
68 76
/// Check instruction suffix scanning and assembler operand order.
81 89
/// Decode fence classes and local instruction synchronization.
82 90
@test fn fences() throws (testing::TestError) {
83 91
    try testing::expect(encode::fenceI() == 0x0000100f);
84 92
    try testing::expect(decode::decode(encode::fenceI()) == decode::Instr::FenceI);
85 93
    let case decode::Instr::Fence { predecessor, successor } = decode::decode(encode::fenceOrder(15, 15))
86 -
        else { throw testing::TestError::Failed; };
94 +
        else {
95 +
            throw testing::TestError::Failed;
96 +
        };
87 97
    try testing::expect(predecessor == 15 and successor == 15);
88 98
    let case decode::Instr::Unknown { .. } = decode::decode(0x0000200f)
89 -
        else { throw testing::TestError::Failed; };
99 +
        else {
100 +
            throw testing::TestError::Failed;
101 +
        };
90 102
}
91 103
92 104
/// Reject invalid atomic operands and fence access classes.
93 105
@test unsafe fn invalidAssembly() throws (testing::TestError) {
94 106
    for source in &[
lib/std/arch/rv64/atomics.rad +32 -10
43 43
    { name: "amomaxu", code: 28 },
44 44
];
45 45
46 46
/// Look up an operation's canonical mnemonic stem.
47 47
export fn name(code: u32) -> ?*[u8] {
48 -
    for operation in &OPERATIONS[..] { if operation.code == code { return operation.name; } }
48 +
    for operation in &OPERATIONS[..] {
49 +
        if operation.code == code {
50 +
            return operation.name;
51 +
        }
52 +
    }
49 53
    return nil;
50 54
}
51 55
52 56
/// Parse an atomic mnemonic with mandatory width and optional ordering suffix.
53 57
export fn parse(text: *[u8]) -> ?Format {
54 58
    for operation in &OPERATIONS[..] {
55 59
        let n = operation.name.len;
56 -
        if text.len < n + 2 or not mem::eq(&text[..n], operation.name) or text[n] <> '.' { continue; }
60 +
        if text.len < n + 2 or not mem::eq(&text[..n], operation.name) or text[n] <> '.' {
61 +
            continue;
62 +
        }
57 63
        let mut width: u32 = 2;
58 -
        if text[n + 1] == 'd' { set width = 3; }
59 -
        else if text[n + 1] <> 'w' { return nil; }
64 +
        if text[n + 1] == 'd' {
65 +
            set width = 3;
66 +
        }
67 +
        else if text[n + 1] <> 'w' {
68 +
            return nil;
69 +
        }
60 70
        let suffix = &text[n + 2..];
61 71
        let mut order: u32 = 0;
62 -
        if mem::eq(suffix, ".aq") { set order = 2; }
63 -
        else if mem::eq(suffix, ".rl") { set order = 1; }
64 -
        else if mem::eq(suffix, ".aqrl") { set order = 3; }
65 -
        else if suffix.len <> 0 { return nil; }
72 +
        if mem::eq(suffix, ".aq") {
73 +
            set order = 2;
74 +
        }
75 +
        else if mem::eq(suffix, ".rl") {
76 +
            set order = 1;
77 +
        }
78 +
        else if mem::eq(suffix, ".aqrl") {
79 +
            set order = 3;
80 +
        }
81 +
        else if suffix.len <> 0 {
82 +
            return nil;
83 +
        }
66 84
        return Format { operation: operation.code, width, order };
67 85
    }
68 86
    return nil;
69 87
}
70 88
81 99
/// Decode a supported atomic word and reject reserved width and LR fields.
82 100
export fn decode(word: u32) -> ?Instruction {
83 101
    let operation = word >> 27;
84 102
    let width = (word >> 12) & 7;
85 103
    let rs2 = super::reg(((word >> 20) & 31) as u8);
86 -
    if (word & 127) <> 0x2f or name(operation) == nil or (width <> 2 and width <> 3) { return nil; }
87 -
    if operation == 2 and rs2 <> super::ZERO { return nil; }
104 +
    if (word & 127) <> 0x2f or name(operation) == nil or (width <> 2 and width <> 3) {
105 +
        return nil;
106 +
    }
107 +
    if operation == 2 and rs2 <> super::ZERO {
108 +
        return nil;
109 +
    }
88 110
    return Instruction {
89 111
        format: Format { operation, width, order: (word >> 25) & 3 },
90 112
        rd: super::reg(((word >> 7) & 31) as u8), rs1: super::reg(((word >> 15) & 31) as u8), rs2,
91 113
    };
92 114
}
lib/std/arch/rv64/bounds.rad +39 -24
34 34
@test unsafe fn emissionCapacity() throws (testing::TestError) {
35 35
    for kind in 0..8 {
36 36
        let mut arena = alloc::new(&mut MEMORY[..]);
37 37
        let mut e = try! emit::emitter(&mut arena, false);
38 38
        match kind {
39 -
            case 0 => { set e.code = &mut e.code[..0]; emit::emit(&mut e, encode::nop()); },
39 +
            case 0 => {
40 +
                set e.code = &mut e.code[..0];
41 +
                emit::emit(&mut e, encode::nop());
42 +
            },
40 43
            case 1 => {
41 -
                set e.pendingBranches.len = 0; set e.pendingBranches.cap = 0;
44 +
                set e.pendingBranchesLen = e.pendingBranches.len;
42 45
                emit::recordBranch(&mut e, 0, emit::BranchKind::Jump);
43 46
            },
44 47
            case 2 => {
45 -
                set e.pendingCalls.len = 0; set e.pendingCalls.cap = 0;
48 +
                set e.pendingCallsLen = e.pendingCalls.len;
46 49
                emit::recordCall(&mut e, "p::call");
47 50
            },
48 51
            case 3 => {
49 -
                set e.pendingJumps.len = 0; set e.pendingJumps.cap = 0;
52 +
                set e.pendingJumpsLen = e.pendingJumps.len;
50 53
                emit::recordJumpAt(&mut e, "p::jump", super::ZERO, 0);
51 54
            },
52 55
            case 4 => {
53 -
                set e.pendingAddrLoads.len = 0; set e.pendingAddrLoads.cap = 0;
56 +
                set e.pendingAddrLoadsLen = e.pendingAddrLoads.len;
54 57
                emit::recordDataAddrLoad(&mut e, "p::data", super::A0);
55 58
            },
56 59
            case 5 => {
57 -
                set e.funcs.len = 0; set e.funcs.cap = 0;
60 +
                set e.funcsLen = e.funcs.len;
58 61
                emit::recordFunc(&mut e, "p::call");
59 62
            },
60 63
            case 6 => {
61 64
                let entries = &mut ENTRIES[..2];
62 65
                set e.labels.funcs = dict::init(&mut entries[..]);
63 66
                emit::recordFuncOffset(&mut e, "p::first");
64 67
                emit::recordFuncOffset(&mut e, "p::second");
65 68
            },
66 -
            else => { emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 }); },
69 +
            else => {
70 +
                emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 });
71 +
            },
67 72
        }
68 73
        try testing::expect(e.error == super::Error::Capacity);
69 74
        let count = e.codeLen;
70 75
        emit::emit(&mut e, encode::ebreak());
71 76
        try testing::expect(e.codeLen == count);
136 141
}
137 142
138 143
/// Spill candidate storage fails explicitly when too many values are live.
139 144
@test unsafe fn registerStorage() throws (testing::TestError) {
140 145
    let mut arena = alloc::new(&mut SCRATCH[..]);
141 -
    let mut liveSet = try! bitset::allocate(&mut arena, 257);
142 -
    for i in 0..257 { bitset::put(&mut liveSet, i); }
143 -
    let mut out = [liveSet];
144 -
    let live = regalloc::liveness::LiveInfo {
145 -
        liveIn: &mut [], liveOut: &mut out[..], defs: &mut [], uses: &mut [], blockCount: 1, maxReg: 257,
146 -
    };
147 -
    let func = il::Fn {
148 -
        name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true,
149 -
        blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }],
150 -
    };
151 -
    let mut failed = false;
152 -
    try regalloc::spill::analyze(&func, &live, 23, 11, 8, &mut arena) catch {
153 -
        set failed = true;
154 -
    };
155 -
    try testing::expect(failed);
146 +
    use arena as bits in {
147 +
        let liveSet = try! bitset::allocate(&bits, 257);
148 +
        for i in 0..257 {
149 +
            bitset::put(liveSet, i);
150 +
        }
151 +
        let live = regalloc::liveness::LiveInfo 'bits {
152 +
            liveIn: &liveSet[..0], liveOut: &liveSet[..],
153 +
            defs: &liveSet[..0], uses: &liveSet[..0],
154 +
            words: bitset::wordsFor(257), blockCount: 1, maxReg: 257,
155 +
        };
156 +
        let func = il::Fn {
157 +
            name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true,
158 +
            blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }],
159 +
        };
160 +
        let mut failed = false;
161 +
        try regalloc::spill::analyze(&func, &live, 23, 11, 8, &bits) catch {
162 +
            set failed = true;
163 +
        };
164 +
        try testing::expect(failed);
165 +
    }
156 166
}
157 167
158 168
/// Data output and symbol maps reject insufficient or ambiguous storage.
159 169
@test unsafe fn dataStorage() throws (testing::TestError) {
160 170
    let mut arena = alloc::new(&mut MEMORY[..]);
180 190
    try data::buildMap(&duplicate[..], &mut larger[..]) catch err {
181 191
        try testing::expect(err == data::Error::Symbol); set failed += 1;
182 192
    };
183 193
    try testing::expect(failed == 3);
184 194
    let length = try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..], false)
185 -
        catch { throw testing::TestError::Failed; };
195 +
        catch {
196 +
            throw testing::TestError::Failed;
197 +
        };
186 198
    try testing::expect(length == 8 and bytes[0] == 1);
187 199
}
188 200
189 201
/// Name the failed invariant before returning to the test runner.
190 202
fn check(condition: bool, name: *[u8]) throws (testing::TestError) {
191 -
    if not condition { io::printLn(name); throw testing::TestError::Failed; }
203 +
    if not condition {
204 +
        io::printLn(name);
205 +
        throw testing::TestError::Failed;
206 +
    }
192 207
}
lib/std/arch/rv64/decode.rad +6 -2
216 216
    let rs1 = super::reg(rs1(instr));
217 217
    let rs2 = super::reg(rs2(instr));
218 218
219 219
    match op {
220 220
        case 0x2f => {
221 -
            if let atomic = atomics::decode(instr) { return Instr::Atomic(atomic); }
221 +
            if let atomic = atomics::decode(instr) {
222 +
                return Instr::Atomic(atomic);
223 +
            }
222 224
            return Instr::Unknown { bits: instr };
223 225
        },
224 226
        case 0x0f => {
225 -
            if instr == 0x100f { return Instr::FenceI; }
227 +
            if instr == 0x100f {
228 +
                return Instr::FenceI;
229 +
            }
226 230
            if (instr & 0xf00fffff) == 0x0f {
227 231
                return Instr::Fence { predecessor: (instr >> 24) & 15, successor: (instr >> 20) & 15 };
228 232
            }
229 233
            return Instr::Unknown { bits: instr };
230 234
        },
lib/std/arch/rv64/emit.rad +223 -82
12 12
use std::mem;
13 13
14 14
use super::encode;
15 15
16 16
/// Maximum number of instructions in code buffer.
17 -
constant MAX_INSTRS: u32 = 2097152;
17 +
export constant MAX_INSTRS: u32 = 2097152;
18 18
/// Maximum code length before byte offset overflows signed 32-bits.
19 19
constant MAX_CODE_LEN: u32 = 0x7FFFFFFF / super::INSTR_SIZE as u32;
20 20
/// Maximum number of pending branches awaiting patching.
21 -
constant MAX_PENDING: u32 = 65536;
21 +
export constant MAX_PENDING: u32 = 65536;
22 22
/// Maximum number of function entries.
23 -
constant MAX_FUNCS: u32 = 4096;
23 +
export constant MAX_FUNCS: u32 = 4096;
24 24
/// Maximum number of debug entries.
25 -
constant MAX_DEBUG_ENTRIES: u32 = 524288;
25 +
export constant MAX_DEBUG_ENTRIES: u32 = 524288;
26 26
27 27
//////////////////////
28 28
// Emission Context //
29 29
//////////////////////
30 30
104 104
    code: *mut [u32],
105 105
    /// Current number of emitted instructions.
106 106
    codeLen: u32,
107 107
    /// Local branches needing offset patching.
108 108
    pendingBranches: *mut [PendingBranch],
109 +
    /// Number of local branches that need patching.
110 +
    pendingBranchesLen: u32,
109 111
    /// Function calls needing offset patching.
110 112
    pendingCalls: *mut [PendingCall],
113 +
    /// Number of function calls that need patching.
114 +
    pendingCallsLen: u32,
111 115
    /// Assembly jumps needing offset patching.
112 116
    pendingJumps: *mut [PendingJump],
117 +
    /// Number of assembly jumps that need patching.
118 +
    pendingJumpsLen: u32,
113 119
    /// Function address loads needing offset patching.
114 120
    pendingAddrLoads: *mut [PendingAddrLoad],
121 +
    /// Number of function address loads that need patching.
122 +
    pendingAddrLoadsLen: u32,
115 123
    /// Block label tracking.
116 124
    labels: labels::Labels,
117 125
    /// Function start positions for printing.
118 126
    funcs: *mut [types::FuncAddr],
127 +
    /// Number of function start positions recorded.
128 +
    funcsLen: u32,
119 129
    /// Debug entries mapping PCs to source locations.
120 130
    debugEntries: *mut [types::DebugEntry],
121 131
    /// Number of debug entries recorded.
122 132
    debugEntriesLen: u32,
123 133
}
124 134
135 +
/// Caller-owned storage for program emission.
136 +
export record Storage {
137 +
    /// Emitted instruction buffer.
138 +
    code: *mut [u32],
139 +
    /// Local branch patch buffer.
140 +
    pendingBranches: *mut [PendingBranch],
141 +
    /// Function call patch buffer.
142 +
    pendingCalls: *mut [PendingCall],
143 +
    /// Assembly jump patch buffer.
144 +
    pendingJumps: *mut [PendingJump],
145 +
    /// Address load patch buffer.
146 +
    pendingAddrLoads: *mut [PendingAddrLoad],
147 +
    /// Per-function block offset buffer.
148 +
    blockOffsets: *mut [i32],
149 +
    /// Function label hash table storage.
150 +
    funcEntries: *mut [dict::Entry],
151 +
    /// Printed function address buffer.
152 +
    funcs: *mut [types::FuncAddr],
153 +
    /// Debug location buffer.
154 +
    debugEntries: *mut [types::DebugEntry],
155 +
}
156 +
125 157
/// Computed stack frame layout for a function.
126 158
export record Frame: Copy {
127 159
    /// Total frame size in bytes (aligned).
128 160
    totalSize: i32,
129 161
    /// Callee-saved registers and their offsets.
181 213
    return frame;
182 214
}
183 215
184 216
/// Create a new emitter.
185 217
export unsafe fn emitter(arena: &mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) {
218 +
    let storage = try allocateStorage(arena, debug);
219 +
    return emitterWithStorage(storage, debug);
220 +
}
221 +
222 +
/// Allocate emitter storage from an arena.
223 +
export unsafe fn allocateStorage(arena: &mut alloc::Arena, debug: bool) -> Storage throws (alloc::AllocError) {
186 224
    let code = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_INSTRS);
187 225
    let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING);
188 226
    let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING);
189 227
    let pendingJumps = try alloc::allocSlice(arena, @sizeOf(PendingJump), @alignOf(PendingJump), MAX_PENDING);
190 228
    let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING);
196 234
    if debug {
197 235
        set debugEntries = try alloc::allocSlice(
198 236
            arena, @sizeOf(types::DebugEntry), @alignOf(types::DebugEntry), MAX_DEBUG_ENTRIES
199 237
        ) as *mut [types::DebugEntry];
200 238
    }
201 -
    let mut pendingBranchesBuf = pendingBranches as *mut [PendingBranch];
202 -
    let mut pendingCallsBuf = pendingCalls as *mut [PendingCall];
203 -
    let mut pendingJumpsBuf = pendingJumps as *mut [PendingJump];
204 -
    let mut pendingAddrLoadsBuf = pendingAddrLoads as *mut [PendingAddrLoad];
205 -
    let mut funcsBuf = funcs as *mut [types::FuncAddr];
206 -
    set pendingBranchesBuf.len = 0;
207 -
    set pendingCallsBuf.len = 0;
208 -
    set pendingJumpsBuf.len = 0;
209 -
    set pendingAddrLoadsBuf.len = 0;
210 -
    set funcsBuf.len = 0;
239 +
    return Storage {
240 +
        code: code as *mut [u32],
241 +
        pendingBranches: pendingBranches as *mut [PendingBranch],
242 +
        pendingCalls: pendingCalls as *mut [PendingCall],
243 +
        pendingJumps: pendingJumps as *mut [PendingJump],
244 +
        pendingAddrLoads: pendingAddrLoads as *mut [PendingAddrLoad],
245 +
        blockOffsets: blockOffsets as *mut [i32],
246 +
        funcEntries: funcEntries as *mut [dict::Entry],
247 +
        funcs: funcs as *mut [types::FuncAddr],
248 +
        debugEntries,
249 +
    };
250 +
}
251 +
252 +
/// Create an emitter from caller-owned storage.
253 +
export fn emitterWithStorage(storage: Storage, debug: bool) -> Emitter {
254 +
    let case Storage {
255 +
        code, pendingBranches, pendingCalls, pendingJumps, pendingAddrLoads,
256 +
        blockOffsets, funcEntries, funcs, debugEntries,
257 +
    } = storage else panic "expected emitter storage";
258 +
    assert code.len >= MAX_INSTRS, "emitterWithStorage: code buffer too small";
259 +
    assert pendingBranches.len >= MAX_PENDING, "emitterWithStorage: branch buffer too small";
260 +
    assert pendingCalls.len >= MAX_PENDING, "emitterWithStorage: call buffer too small";
261 +
    assert pendingJumps.len >= MAX_PENDING, "emitterWithStorage: jump buffer too small";
262 +
    assert pendingAddrLoads.len >= MAX_PENDING, "emitterWithStorage: address buffer too small";
263 +
    assert blockOffsets.len >= labels::MAX_BLOCKS_PER_FN, "emitterWithStorage: block buffer too small";
264 +
    assert funcEntries.len >= labels::FUNC_TABLE_SIZE, "emitterWithStorage: label table too small";
265 +
    assert funcs.len >= MAX_FUNCS, "emitterWithStorage: function buffer too small";
266 +
    let mut activeDebugEntries: *mut [types::DebugEntry] = &mut [];
267 +
    if debug {
268 +
        assert debugEntries.len >= MAX_DEBUG_ENTRIES, "emitterWithStorage: debug buffer too small";
269 +
        set activeDebugEntries = debugEntries;
270 +
    }
211 271
    return Emitter {
212 272
        error: nil,
213 273
        sharedData: false,
214 -
        code: code as *mut [u32],
274 +
        code,
215 275
        codeLen: 0,
216 -
        pendingBranches: pendingBranchesBuf,
217 -
        pendingCalls: pendingCallsBuf,
218 -
        pendingJumps: pendingJumpsBuf,
219 -
        pendingAddrLoads: pendingAddrLoadsBuf,
220 -
        labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]),
221 -
        funcs: funcsBuf,
222 -
        debugEntries,
276 +
        pendingBranches,
277 +
        pendingBranchesLen: 0,
278 +
        pendingCalls,
279 +
        pendingCallsLen: 0,
280 +
        pendingJumps,
281 +
        pendingJumpsLen: 0,
282 +
        pendingAddrLoads,
283 +
        pendingAddrLoadsLen: 0,
284 +
        labels: labels::init(blockOffsets, funcEntries),
285 +
        funcs,
286 +
        funcsLen: 0,
287 +
        debugEntries: activeDebugEntries,
223 288
        debugEntriesLen: 0,
224 289
    };
225 290
}
226 291
227 292
///////////////////////
228 293
// Emission Helpers  //
229 294
///////////////////////
230 295
231 296
/// Emit a single instruction.
232 297
export fn emit(e: &mut Emitter, instr: u32) {
233 -
    if e.error <> nil { return; }
234 -
    if e.codeLen == e.code.len { set e.error = super::Error::Capacity; return; }
298 +
    if e.error <> nil {
299 +
        return;
300 +
    }
301 +
    if e.codeLen == e.code.len {
302 +
        set e.error = super::Error::Capacity;
303 +
        return;
304 +
    }
235 305
    set e.code[e.codeLen] = instr;
236 306
    set e.codeLen += 1;
237 307
}
238 308
239 309
/// Compute branch offset to a function by name.
240 310
export fn branchOffsetToFunc(e: &mut Emitter, srcIndex: u32, name: *[u8]) -> i32 {
241 -
    if e.error <> nil { return 0; }
311 +
    if e.error <> nil {
312 +
        return 0;
313 +
    }
242 314
    let target = dict::get(&e.labels.funcs, name) else {
243 315
        set e.error = super::Error::Symbol; return 0;
244 316
    };
245 317
    return target - srcIndex as i32 * super::INSTR_SIZE;
246 318
}
247 319
248 320
/// Patch an instruction at a given index.
249 321
export fn patch(e: &mut Emitter, index: u32, instr: u32) {
250 -
    if e.error <> nil { return; }
251 -
    if index >= e.codeLen { set e.error = super::Error::Capacity; return; }
322 +
    if e.error <> nil {
323 +
        return;
324 +
    }
325 +
    if index >= e.codeLen {
326 +
        set e.error = super::Error::Capacity;
327 +
        return;
328 +
    }
252 329
    set e.code[index] = instr;
253 330
}
254 331
255 332
/// Record a block's address for branch resolution.
256 333
export fn recordBlock(e: &mut Emitter, blockIdx: u32) {
257 -
    if e.error <> nil { return; }
334 +
    if e.error <> nil {
335 +
        return;
336 +
    }
258 337
    if e.codeLen > MAX_CODE_LEN or blockIdx >= e.labels.blockOffsets.len {
259 338
        set e.error = super::Error::Capacity; return;
260 339
    }
261 340
    labels::recordBlock(&mut e.labels, blockIdx, e.codeLen as i32 * super::INSTR_SIZE);
262 341
}
267 346
    recordFuncOffsetAt(e, name, codeLen);
268 347
}
269 348
270 349
/// Record a function's code offset at `index` for call resolution.
271 350
export fn recordFuncOffsetAt(e: &mut Emitter, name: *[u8], index: u32) {
272 -
    if e.error <> nil { return; }
273 -
    if index > MAX_CODE_LEN { set e.error = super::Error::Capacity; return; }
274 -
    if name.len == 0 { set e.error = super::Error::Symbol; return; }
351 +
    if e.error <> nil {
352 +
        return;
353 +
    }
354 +
    if index > MAX_CODE_LEN {
355 +
        set e.error = super::Error::Capacity;
356 +
        return;
357 +
    }
358 +
    if name.len == 0 {
359 +
        set e.error = super::Error::Symbol;
360 +
        return;
361 +
    }
275 362
    if e.labels.funcs.count >= e.labels.funcs.entries.len / 2 and dict::get(&e.labels.funcs, name) == nil {
276 363
        set e.error = super::Error::Capacity; return;
277 364
    }
278 365
    dict::insert(&mut e.labels.funcs, name, index as i32 * super::INSTR_SIZE);
279 366
}
284 371
    recordFuncAt(e, name, codeLen);
285 372
}
286 373
287 374
/// Record a function's start position at `index` for printing.
288 375
export fn recordFuncAt(e: &mut Emitter, name: *[u8], index: u32) {
289 -
    if e.error <> nil { return; }
290 -
    if e.funcs.len == e.funcs.cap { set e.error = super::Error::Capacity; return; }
291 -
    let count = e.funcs.len;
292 -
    unsafe { set e.funcs.len = count + 1; }
293 -
    set e.funcs[count] = types::FuncAddr { name, index };
376 +
    if e.error <> nil {
377 +
        return;
378 +
    }
379 +
    if e.funcsLen == e.funcs.len {
380 +
        set e.error = super::Error::Capacity;
381 +
        return;
382 +
    }
383 +
    set e.funcs[e.funcsLen] = types::FuncAddr { name, index };
384 +
    set e.funcsLen += 1;
294 385
}
295 386
296 387
/// Record a local branch needing later patching.
297 388
/// Unconditional jumps use a single slot (J-type, +-1MB range).
298 389
/// Conditional branches use two slots (B-type has only +-4KB range,
299 390
/// so large functions may need the inverted-branch + JAL fallback).
300 391
export fn recordBranch(e: &mut Emitter, targetBlock: u32, kind: BranchKind) {
301 -
    if e.error <> nil { return; }
302 -
    if e.pendingBranches.len == e.pendingBranches.cap { set e.error = super::Error::Capacity; return; }
303 -
    let count = e.pendingBranches.len;
304 -
    unsafe { set e.pendingBranches.len = count + 1; }
305 -
    set e.pendingBranches[count] = PendingBranch {
392 +
    if e.error <> nil {
393 +
        return;
394 +
    }
395 +
    if e.pendingBranchesLen == e.pendingBranches.len {
396 +
        set e.error = super::Error::Capacity;
397 +
        return;
398 +
    }
399 +
    set e.pendingBranches[e.pendingBranchesLen] = PendingBranch {
306 400
        index: e.codeLen,
307 401
        target: targetBlock,
308 -
        kind: kind,
402 +
        kind,
309 403
    };
404 +
    set e.pendingBranchesLen += 1;
310 405
311 406
    emit(e, encode::nop()); // First slot, always needed.
312 407
313 408
    match kind {
314 409
        case BranchKind::Jump => {},
318 413
319 414
/// Record a function call needing later patching.
320 415
/// Emits placeholder instructions that will be patched later.
321 416
/// Uses two slots to support long-distance calls.
322 417
export fn recordCall(e: &mut Emitter, target: *[u8]) {
323 -
    if e.error <> nil { return; }
324 -
    if e.pendingCalls.len == e.pendingCalls.cap { set e.error = super::Error::Capacity; return; }
325 -
    let count = e.pendingCalls.len;
326 -
    unsafe { set e.pendingCalls.len = count + 1; }
327 -
    set e.pendingCalls[count] = PendingCall {
418 +
    if e.error <> nil {
419 +
        return;
420 +
    }
421 +
    if e.pendingCallsLen == e.pendingCalls.len {
422 +
        set e.error = super::Error::Capacity;
423 +
        return;
424 +
    }
425 +
    set e.pendingCalls[e.pendingCallsLen] = PendingCall {
328 426
        index: e.codeLen,
329 427
        target,
330 428
    };
429 +
    set e.pendingCallsLen += 1;
331 430
332 431
    emit(e, encode::nop()); // Placeholder for AUIPC.
333 432
    emit(e, encode::nop()); // Placeholder for JALR.
334 433
}
335 434
336 435
/// Record a jump emitted by assembly that needs whole-program patching.
337 436
export fn recordJumpAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
338 -
    if e.error <> nil { return; }
339 -
    if e.pendingJumps.len == e.pendingJumps.cap { set e.error = super::Error::Capacity; return; }
340 -
    let count = e.pendingJumps.len;
341 -
    unsafe { set e.pendingJumps.len = count + 1; }
342 -
    set e.pendingJumps[count] = PendingJump {
437 +
    if e.error <> nil {
438 +
        return;
439 +
    }
440 +
    if e.pendingJumpsLen == e.pendingJumps.len {
441 +
        set e.error = super::Error::Capacity;
442 +
        return;
443 +
    }
444 +
    set e.pendingJumps[e.pendingJumpsLen] = PendingJump {
343 445
        index,
344 446
        target,
345 447
        rd,
346 448
    };
449 +
    set e.pendingJumpsLen += 1;
347 450
}
348 451
349 452
/// Record a function address load needing later patching.
350 453
/// Emits placeholder instructions that will be patched to load the function's address.
351 454
/// Uses two slots to compute long-distance addresses.
357 460
    emit(e, encode::nop()); // Placeholder for ADDI.
358 461
}
359 462
360 463
/// Record a function address load already reserved by assembly.
361 464
export fn recordAddrLoadAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
362 -
    if e.error <> nil { return; }
363 -
    if e.pendingAddrLoads.len == e.pendingAddrLoads.cap { set e.error = super::Error::Capacity; return; }
364 -
    let count = e.pendingAddrLoads.len;
365 -
    unsafe { set e.pendingAddrLoads.len = count + 1; }
366 -
    set e.pendingAddrLoads[count] = PendingAddrLoad {
465 +
    if e.error <> nil {
466 +
        return;
467 +
    }
468 +
    if e.pendingAddrLoadsLen == e.pendingAddrLoads.len {
469 +
        set e.error = super::Error::Capacity;
470 +
        return;
471 +
    }
472 +
    set e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad {
367 473
        index,
368 474
        target,
369 -
        rd: rd,
475 +
        rd,
370 476
        isData: false,
371 477
    };
478 +
    set e.pendingAddrLoadsLen += 1;
372 479
}
373 480
374 481
/// Record a data address load needing later patching.
375 482
/// Reserves two instructions for a PC-relative address load.
376 483
export fn recordDataAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) {
377 -
    if e.error <> nil { return; }
378 -
    if e.pendingAddrLoads.len == e.pendingAddrLoads.cap { set e.error = super::Error::Capacity; return; }
379 -
    let count = e.pendingAddrLoads.len;
380 -
    unsafe { set e.pendingAddrLoads.len = count + 1; }
381 -
    set e.pendingAddrLoads[count] = PendingAddrLoad {
484 +
    if e.error <> nil {
485 +
        return;
486 +
    }
487 +
    if e.pendingAddrLoadsLen == e.pendingAddrLoads.len {
488 +
        set e.error = super::Error::Capacity;
489 +
        return;
490 +
    }
491 +
    set e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad {
382 492
        index: e.codeLen,
383 493
        target,
384 -
        rd: rd,
494 +
        rd,
385 495
        isData: true,
386 496
    };
497 +
    set e.pendingAddrLoadsLen += 1;
387 498
388 499
    emit(e, encode::nop()); // Address-load upper instruction.
389 500
    emit(e, encode::nop()); // Address-load lower instruction.
390 501
    if e.sharedData {
391 502
        emit(e, encode::nop()); // Package offset addition.
398 509
/// Called after each function.
399 510
///
400 511
/// Uses two-instruction sequences: short branches use `branch` and `nop`,
401 512
/// long branches use inverted branch  and `jal` or `auipc` and `jalr`.
402 513
export unsafe fn patchLocalBranches(e: &mut Emitter) {
403 -
    if e.error <> nil { return; }
404 -
    for i in 0..e.pendingBranches.len {
514 +
    if e.error <> nil {
515 +
        return;
516 +
    }
517 +
    for i in 0..e.pendingBranchesLen {
405 518
        let p = e.pendingBranches[i];
406 -
        if p.target >= e.labels.blockCount { set e.error = super::Error::Symbol; return; }
519 +
        if p.target >= e.labels.blockCount {
520 +
            set e.error = super::Error::Symbol;
521 +
            return;
522 +
        }
407 523
        let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE);
408 524
        match p.kind {
409 525
            case BranchKind::Cond { op, rs1, rs2 } => {
410 526
                if encode::isBranchImm(offset) {
411 527
                    patch(e, p.index, encodeCondBranch(op, rs1, rs2, offset));
412 528
                    patch(e, p.index + 1, encode::nop());
413 529
                } else {
414 530
                    let adj = offset - super::INSTR_SIZE;
415 -
                    if not encode::isJumpImm(adj) { set e.error = super::Error::Relocation; return; }
531 +
                    if not encode::isJumpImm(adj) {
532 +
                        set e.error = super::Error::Relocation;
533 +
                        return;
534 +
                    }
416 535
                    patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, super::INSTR_SIZE * 2));
417 536
                    patch(e, p.index + 1, encode::jal(super::ZERO, adj));
418 537
                }
419 538
            },
420 539
            case BranchKind::InvertedCond { op, rs1, rs2 } => {
421 540
                if encode::isBranchImm(offset) {
422 541
                    patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, offset));
423 542
                    patch(e, p.index + 1, encode::nop());
424 543
                } else {
425 544
                    let adj = offset - super::INSTR_SIZE;
426 -
                    if not encode::isJumpImm(adj) { set e.error = super::Error::Relocation; return; }
545 +
                    if not encode::isJumpImm(adj) {
546 +
                        set e.error = super::Error::Relocation;
547 +
                        return;
548 +
                    }
427 549
                    patch(e, p.index, encodeCondBranch(op, rs1, rs2, super::INSTR_SIZE * 2));
428 550
                    patch(e, p.index + 1, encode::jal(super::ZERO, adj));
429 551
                }
430 552
            },
431 553
            case BranchKind::Jump => {
432 554
                // Single-slot jump (J-type, +-1MB range).
433 -
                if not encode::isJumpImm(offset) { set e.error = super::Error::Relocation; return; }
555 +
                if not encode::isJumpImm(offset) {
556 +
                    set e.error = super::Error::Relocation;
557 +
                    return;
558 +
                }
434 559
                patch(e, p.index, encode::jal(super::ZERO, offset));
435 560
            },
436 561
        }
437 562
    }
438 -
    set e.pendingBranches.len = 0;
563 +
    set e.pendingBranchesLen = 0;
439 564
}
440 565
441 566
/// Encode a conditional branch instruction.
442 567
fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 {
443 568
    match op {
459 584
}
460 585
461 586
/// Patch all pending function calls.
462 587
/// Called after all functions have been generated.
463 588
export fn patchCalls(e: &mut Emitter) {
464 -
    for i in 0..e.pendingCalls.len {
589 +
    for i in 0..e.pendingCallsLen {
465 590
        let p = e.pendingCalls[i];
466 591
        let offset = branchOffsetToFunc(e, p.index, p.target);
467 -
        if offset > 0x7ffff7ff { set e.error = super::Error::Relocation; return; }
592 +
        if offset > 0x7ffff7ff {
593 +
            set e.error = super::Error::Relocation;
594 +
            return;
595 +
        }
468 596
        let s = splitImm(offset);
469 597
470 598
        // `AUIPC scratch, hi(offset)`.
471 599
        patch(e, p.index, encode::auipc(super::SCRATCH1, s.hi));
472 600
        // `JALR ra, scratch, lo(offset)`.
474 602
    }
475 603
}
476 604
477 605
/// Patch all pending assembly jumps.
478 606
export fn patchJumps(e: &mut Emitter) {
479 -
    for i in 0..e.pendingJumps.len {
607 +
    for i in 0..e.pendingJumpsLen {
480 608
        let p = e.pendingJumps[i];
481 609
        let offset = branchOffsetToFunc(e, p.index, p.target);
482 610
483 -
        if not encode::isJumpImm(offset) { set e.error = super::Error::Relocation; return; }
611 +
        if not encode::isJumpImm(offset) {
612 +
            set e.error = super::Error::Relocation;
613 +
            return;
614 +
        }
484 615
        patch(e, p.index, encode::jal(p.rd, offset));
485 616
    }
486 617
}
487 618
488 619
/// Patch all pending function and data address loads.
489 620
/// Called after all functions have been generated and data layout is known.
490 621
export fn patchAddrLoads(e: &mut Emitter, dataSymMap: &data::DataSymMap, codeBase: u64) throws (super::Error) {
491 622
    try check(e);
492 -
    for i in 0..e.pendingAddrLoads.len {
623 +
    for i in 0..e.pendingAddrLoadsLen {
493 624
        let p = e.pendingAddrLoads[i];
494 625
        if p.isData {
495 626
            let addr = data::lookupAddr(dataSymMap, p.target) else {
496 627
                throw super::Error::Symbol;
497 628
            };
505 636
506 637
            continue;
507 638
        }
508 639
509 640
        let offset = branchOffsetToFunc(e, p.index, p.target);
510 -
        if offset > 0x7ffff7ff { set e.error = super::Error::Relocation; return; }
641 +
        if offset > 0x7ffff7ff {
642 +
            set e.error = super::Error::Relocation;
643 +
            return;
644 +
        }
511 645
        let s = splitImm(offset);
512 646
        // `AUIPC rd, hi(offset)`.
513 647
        patch(e, p.index, encode::auipc(p.rd, s.hi));
514 648
        // `ADDI rd, rd, lo(offset)`.
515 649
        patch(e, p.index + 1, encode::addi(p.rd, p.rd, s.lo));
780 914
//////////////////
781 915
// Code Access  //
782 916
//////////////////
783 917
784 918
/// Get emitted code as a slice.
785 -
export fn getCode(e: &Emitter) -> *[u32] {
919 +
export fn getCode 'code (e: &'code Emitter) -> &'code [u32] {
786 920
    return &e.code[..e.codeLen];
787 921
}
788 922
789 923
/// Record a debug entry mapping the current PC to a source location.
790 924
/// Deduplicates consecutive entries with the same location.
791 925
export fn recordSrcLoc(e: &mut Emitter, loc: il::SrcLoc) {
792 -
    if e.error <> nil { return; }
926 +
    if e.error <> nil {
927 +
        return;
928 +
    }
793 929
    let pc = e.codeLen * super::INSTR_SIZE as u32;
794 930
795 931
    // Skip if this is the same location as the previous entry.
796 932
    if e.debugEntriesLen > 0 {
797 933
        let prev = &e.debugEntries[e.debugEntriesLen - 1];
798 934
        if prev.offset == loc.offset and prev.moduleId == loc.moduleId {
799 935
            return;
800 936
        }
801 937
    }
802 -
    if e.debugEntriesLen == e.debugEntries.len { set e.error = super::Error::Capacity; return; }
938 +
    if e.debugEntriesLen == e.debugEntries.len {
939 +
        set e.error = super::Error::Capacity;
940 +
        return;
941 +
    }
803 942
    set e.debugEntries[e.debugEntriesLen] = types::DebugEntry {
804 943
        pc,
805 944
        moduleId: loc.moduleId,
806 945
        offset: loc.offset,
807 946
    };
808 947
    set e.debugEntriesLen += 1;
809 948
}
810 949
811 950
/// Get debug entries as a slice.
812 -
export fn getDebugEntries(e: &Emitter) -> *[types::DebugEntry] {
951 +
export fn getDebugEntries 'code (e: &'code Emitter) -> &'code [types::DebugEntry] {
813 952
    return &e.debugEntries[..e.debugEntriesLen];
814 953
}
815 954
816 955
/// Return the first emission failure before any generated output is published.
817 956
export fn check(e: &Emitter) throws (super::Error) {
818 -
    if let error = e.error { throw error; }
957 +
    if let error = e.error {
958 +
        throw error;
959 +
    }
819 960
}
lib/std/arch/rv64/image.rad +6 -2
125 125
/// Compute a signed displacement for a two-instruction AUIPC/ADDI load.
126 126
export fn displacement(source: u64, target: u64) -> ?i32 {
127 127
    if target >= source {
128 128
        let distance = target - source;
129 129
        // AUIPC sign-extends its upper immediate before ADDI applies the low part.
130 -
        if distance > 0x7ffff7ff { return nil; }
130 +
        if distance > 0x7ffff7ff {
131 +
            return nil;
132 +
        }
131 133
        return distance as i32;
132 134
    }
133 135
    let distance = source - target;
134 -
    if distance > 0x80000000 { return nil; }
136 +
    if distance > 0x80000000 {
137 +
        return nil;
138 +
    }
135 139
    return (-(distance as i64)) as i32;
136 140
}
lib/std/arch/rv64/image/tests.rad +14 -6
25 25
    };
26 26
}
27 27
28 28
/// Check the exact 64-byte little-endian header.
29 29
@test fn header() throws (testing::TestError) {
30 -
    let bytes = try image::header(layout()) catch { throw testing::TestError::Failed; };
30 +
    let bytes = try image::header(layout()) catch {
31 +
        throw testing::TestError::Failed;
32 +
    };
31 33
    try testing::expectBytesEq(&bytes[..], &[
32 34
        82, 65, 68, 48, 2, 0, 0, 0, 4, 0, 0, 128, 0, 0, 0, 0,
33 35
        0, 0, 0, 128, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
34 36
        0, 16, 0, 128, 0, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0,
35 37
        0, 32, 0, 128, 0, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0,
69 71
    set item = layout();
70 72
    set item.rwData.address = 0xfffffffffffffff8;
71 73
    try invalid(item, image::Error::Overflow);
72 74
    set item = layout();
73 75
    set item.roData.address = item.code.address + 8;
74 -
    try image::validate(item) catch { throw testing::TestError::Failed; };
76 +
    try image::validate(item) catch {
77 +
        throw testing::TestError::Failed;
78 +
    };
75 79
}
76 80
77 81
/// Check AUIPC/ADDI limits without overflowing unsigned address arithmetic.
78 82
@test fn displacements() throws (testing::TestError) {
79 83
    try testing::expect(image::displacement(0x180000000, 0x180002000) == 8192);
108 112
        },
109 113
    ];
110 114
    unsafe static syms: [data::DataSym; 2] = undefined;
111 115
    let mut ro: [u8; 8] = [0; 8];
112 116
    let mut rw: [u8; 8] = [0; 8];
113 -
    let result = try rv64::finishProgram(&mut generator, globals,
117 +
    let result = try rv64::finishProgram(generator, globals,
114 118
        rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] },
115 119
        &[], &mut ro[..], &mut rw[..]
116 -
    ) catch { throw testing::TestError::Failed; };
120 +
    ) catch {
121 +
        throw testing::TestError::Failed;
122 +
    };
117 123
    try testing::expect(result.layout.code.address == 0x180000000);
118 124
    try testing::expect(result.layout.rwData.initialized == 8);
119 125
    try testing::expect(result.layout.rwData.memory == 4104);
120 126
    try testing::expect(syms[1].addr == 0x180002008);
121 127
    try testing::expectBytesEq(&rw[..], &[0, 0, 0, 128, 1, 0, 0, 0]);
171 177
        },
172 178
    ];
173 179
    unsafe static syms: [data::DataSym; 2] = undefined;
174 180
    let mut ro: [u8; 0] = [];
175 181
    let mut rw: [u8; 16] = [255; 16];
176 -
    let result = try rv64::finishProgram(&mut generator, globals,
182 +
    let result = try rv64::finishProgram(generator, globals,
177 183
        rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] },
178 184
        &[], &mut ro[..], &mut rw[..]
179 -
    ) catch { throw testing::TestError::Failed; };
185 +
    ) catch {
186 +
        throw testing::TestError::Failed;
187 +
    };
180 188
    try testing::expect(result.layout.rwData.initialized == 16);
181 189
    try testing::expect(syms[1].addr == 0x80002008);
182 190
    try testing::expectBytesEq(&rw[..], &[97, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0]);
183 191
}
lib/std/arch/rv64/isel.rad +172 -198
82 82
////////////////////
83 83
// Selector State //
84 84
////////////////////
85 85
86 86
/// Instruction selector state.
87 -
export record Selector: Copy {
87 +
export record Selector: 'scratch + 'selection where 'scratch: 'selection {
88 88
    /// Emitter for outputting instructions.
89 -
    e: *unsafe mut emit::Emitter,
89 +
    e: &'selection mut emit::Emitter,
90 90
    /// Register allocation result.
91 -
    ralloc: *unsafe regalloc::AllocResult,
91 +
    ralloc: &'selection regalloc::AllocResult 'scratch,
92 92
    /// Total stack frame size.
93 93
    frameSize: i32,
94 94
    /// Running offset into the reserve region of the frame.
95 95
    /// Tracks current position within the pre-allocated reserve slots.
96 96
    reserveOffset: i32,
105 105
/////////////////////////
106 106
// Register Allocation //
107 107
/////////////////////////
108 108
109 109
/// Get the physical register for an already-allocated SSA register.
110 -
unsafe fn getReg(s: &Selector, ssa: il::Reg) -> gen::Reg {
110 +
unsafe fn getReg 'scratch 'selection (s: &Selector 'scratch 'selection, ssa: il::Reg) -> gen::Reg where 'scratch: 'selection {
111 111
    let phys = s.ralloc.assignments[ssa.n] else {
112 112
        panic "getReg: spilled register has no physical assignment";
113 113
    };
114 114
    return phys;
115 115
}
116 116
117 117
/// Compute the offset for a spill slot.
118 118
/// When using FP (dynamic): offset from FP = `slot - totalSize`.
119 119
/// When using SP: offset from SP = `slot`.
120 -
fn spillOffset(s: &Selector, slot: i32) -> i32 {
120 +
fn spillOffset 'scratch 'selection (s: &Selector 'scratch 'selection, slot: i32) -> i32 where 'scratch: 'selection {
121 121
    if s.isDynamic {
122 122
        return slot - s.frameSize;
123 123
    }
124 124
    return slot;
125 125
}
126 126
127 127
/// Get the base register for spill slot addressing (FP or SP).
128 -
fn spillBase(s: &Selector) -> gen::Reg {
128 +
fn spillBase 'scratch 'selection (s: &Selector 'scratch 'selection) -> gen::Reg where 'scratch: 'selection {
129 129
    if s.isDynamic {
130 130
        return super::FP;
131 131
    }
132 132
    return super::SP;
133 133
}
134 134
135 135
/// Get the destination register for an SSA register.
136 136
/// If the register is spilled, records a pending spill and returns the scratch
137 137
/// register. The pending spill is auto-committed by [`selectBlock`] after each
138 138
/// instruction. If not spilled, returns the physical register.
139 -
unsafe fn getDstReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
139 +
unsafe fn getDstReg 'scratch 'selection (s: &mut Selector 'scratch 'selection, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg where 'scratch: 'selection {
140 140
    if let _ = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
141 141
        set s.pendingSpill = PendingSpill { ssa, rd: scratch };
142 142
        return scratch;
143 143
    }
144 144
    return getReg(s, ssa);
145 145
}
146 146
147 147
/// Get the source register for an SSA register.
148 148
/// If the register is spilled, loads the value from the spill slot into the
149 149
/// scratch register and returns it. Otherwise returns the physical register.
150 -
unsafe fn getSrcReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
150 +
unsafe fn getSrcReg 'scratch 'selection (s: &mut Selector 'scratch 'selection, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg where 'scratch: 'selection {
151 151
    if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
152 152
        emit::emitLd(s.e, scratch, spillBase(s), spillOffset(s, slot));
153 153
        return scratch;
154 154
    }
155 155
    return getReg(s, ssa);
156 156
}
157 157
158 158
/// Resolve an IL value to the physical register holding it.
159 159
/// For non-spilled register values, returns the physical register directly.
160 160
/// For immediates, symbols, and spilled registers, materializes into `scratch`.
161 -
unsafe fn resolveVal(s: &mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg {
161 +
unsafe fn resolveVal 'scratch 'selection (s: &mut Selector 'scratch 'selection, scratch: gen::Reg, val: il::Val) -> gen::Reg where 'scratch: 'selection {
162 162
    match val {
163 163
        case il::Val::Reg(r) => {
164 164
            return getSrcReg(s, r, scratch);
165 165
        },
166 166
        case il::Val::Imm(imm) => {
184 184
    }
185 185
}
186 186
187 187
/// Load an IL value into a specific physical register.
188 188
/// Like [`resolveVal`], but ensures the value ends up in `rd`.
189 -
unsafe fn loadVal(s: &mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg {
189 +
unsafe fn loadVal 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, val: il::Val) -> gen::Reg where 'scratch: 'selection {
190 190
    let rs = resolveVal(s, rd, val);
191 191
    emitMv(s, rd, rs);
192 192
    return rd;
193 193
}
194 194
195 195
/// Emit a move instruction if source and destination differ.
196 -
unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) {
196 +
unsafe fn emitMv 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs: gen::Reg) where 'scratch: 'selection {
197 197
    if *rd <> *rs {
198 198
        emit::emit(s.e, encode::mv(rd, rs));
199 199
    }
200 200
}
201 201
233 233
    }
234 234
}
235 235
236 236
/// Resolve a divisor in its declared width, trap if it becomes zero, and
237 237
/// return the canonicalized register.
238 -
unsafe fn resolveAndTrapIfZero(
239 -
    s: &mut Selector,
238 +
unsafe fn resolveAndTrapIfZero 'scratch 'selection (
239 +
    s: &mut Selector 'scratch 'selection,
240 240
    b: il::Val,
241 241
    typ: il::Type,
242 242
    signed: bool
243 -
) -> gen::Reg {
243 +
) -> gen::Reg where 'scratch: 'selection {
244 244
    let mut divisor = b;
245 245
    if let case il::Val::Imm(imm) = b {
246 246
        set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed));
247 247
    }
248 248
    let rs2 = resolveVal(s, super::SCRATCH2, divisor);
272 272
    isDynamic: bool,
273 273
}
274 274
275 275
/// Pre-scan all blocks for constant-sized reserve instructions.
276 276
/// Returns the total size needed for all static reserves, respecting alignment.
277 -
unsafe fn computeReserveInfo(func: *unsafe il::Fn) -> ?ReserveInfo {
277 +
unsafe fn computeReserveInfo(func: &il::Fn) -> ?ReserveInfo {
278 278
    let mut offset: i32 = 0;
279 279
    let mut isDynamic = false;
280 280
281 281
    for b in 0..func.blocks.len {
282 282
        let block = &func.blocks[b];
283 283
        for instr in block.instrs {
284 284
            match instr {
285 285
                case il::Instr::Reserve { size, alignment, .. } => {
286 286
                    if let case il::Val::Imm(sz) = size {
287 -
                        if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 { return nil; }
287 +
                        if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 {
288 +
                            return nil;
289 +
                        }
288 290
                        let aligned = (offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1);
289 -
                        if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned { return nil; }
291 +
                        if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned {
292 +
                            return nil;
293 +
                        }
290 294
                        set offset = (aligned + sz as u64) as i32;
291 295
                    } else {
292 296
                        set isDynamic = true;
293 297
                    }
294 298
                },
298 302
    }
299 303
    return ReserveInfo { size: offset, isDynamic };
300 304
}
301 305
302 306
/// Select instructions for a function.
303 -
export unsafe fn selectFn(
307 +
export unsafe fn selectFn 'scratch (
304 308
    e: &mut emit::Emitter,
305 -
    ralloc: &regalloc::AllocResult,
306 -
    func: *unsafe il::Fn
309 +
    ralloc: &regalloc::AllocResult 'scratch,
310 +
    func: &il::Fn
307 311
) {
308 -
    if e.error <> nil { return; }
312 +
    if e.error <> nil {
313 +
        return;
314 +
    }
309 315
    // Reset block offsets for this function.
310 316
    labels::resetBlocks(&mut e.labels);
311 317
    // Pre-scan for constant-sized reserves to promote to fixed frame slots.
312 -
    let reserveInfo = computeReserveInfo(func) else { set e.error = super::Error::Capacity; return; };
318 +
    let reserveInfo = computeReserveInfo(func) else {
319 +
        set e.error = super::Error::Capacity;
320 +
        return;
321 +
    };
313 322
    if reserveInfo.size as u64 + ralloc.spill.frameSize as u64 > 0x7fffff00 {
314 323
        set e.error = super::Error::Capacity; return;
315 324
    }
316 325
    let isLeaf = func.isLeaf;
317 326
    // Compute frame layout from spill slots, reserve slots, and used callee-saved registers.
321 330
        func.blocks.len,
322 331
        isLeaf,
323 332
        reserveInfo.isDynamic
324 333
    );
325 334
    // Synthetic block indices start after real blocks and the epilogue block.
326 -
    let mut s = Selector {
327 -
        e: e as *unsafe mut emit::Emitter,
328 -
        ralloc: ralloc as *unsafe regalloc::AllocResult,
329 -
        frameSize: frame.totalSize,
330 -
        reserveOffset: 0, pendingSpill: nil,
331 -
        nextSynthBlock: func.blocks.len + 1,
332 -
        isDynamic: frame.isDynamic,
333 -
    };
334 -
    // Record function name for printing.
335 -
    emit::recordFunc(s.e, func.name);
336 -
    // Record function code offset for call patching.
337 -
    emit::recordFuncOffset(s.e, func.name);
338 -
    // Emit prologue.
339 -
    emit::emitPrologue(s.e, &frame);
340 -
341 -
    // Move function params from arg registers to assigned registers.
342 -
    // Cross-call params may have been assigned to callee-saved registers
343 -
    // instead of their natural arg registers. Spilled params are stored
344 -
    // directly to their spill slots.
345 -
    for funcParam, i in func.params {
346 -
        if i < super::ARG_REGS.len {
347 -
            let param = funcParam.value;
348 -
            let argReg = super::ARG_REGS[i];
349 -
350 -
            if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
351 -
                // Spilled parameter: store arg register to spill slot.
352 -
                emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot));
353 -
            } else if let assigned = ralloc.assignments[param.n] {
354 -
                emitMv(&mut s, assigned, argReg);
335 +
    let resultRef: 'selection = &*ralloc, emitterRef = &mut *e where 'scratch: 'selection in {
336 +
        let mut s = Selector 'scratch 'selection {
337 +
            e: emitterRef,
338 +
            ralloc: resultRef,
339 +
            frameSize: frame.totalSize,
340 +
            reserveOffset: 0, pendingSpill: nil,
341 +
            nextSynthBlock: func.blocks.len + 1,
342 +
            isDynamic: frame.isDynamic,
343 +
        };
344 +
        // Record function name for printing.
345 +
        emit::recordFunc(s.e, func.name);
346 +
        // Record function code offset for call patching.
347 +
        emit::recordFuncOffset(s.e, func.name);
348 +
        // Emit prologue.
349 +
        emit::emitPrologue(s.e, &frame);
350 +
351 +
        // Move function params from arg registers to assigned registers.
352 +
        // Cross-call params may have been assigned to callee-saved registers
353 +
        // instead of their natural arg registers. Spilled params are stored
354 +
        // directly to their spill slots.
355 +
        for funcParam, i in func.params {
356 +
            if i < super::ARG_REGS.len {
357 +
                let param = funcParam.value;
358 +
                let argReg = super::ARG_REGS[i];
359 +
360 +
                if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
361 +
                    // Spilled parameter: store arg register to spill slot.
362 +
                    emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot));
363 +
                } else if let assigned = ralloc.assignments[param.n] {
364 +
                    emitMv(&mut s, assigned, argReg);
365 +
                }
355 366
            }
356 367
        }
357 -
    }
358 368
359 -
    // Emit each block.
360 -
    for i in 0..func.blocks.len {
361 -
        if s.e.error <> nil { return; }
362 -
        selectBlock(&mut s, i, &func.blocks[i], &frame, func);
369 +
        // Emit each block.
370 +
        for i in 0..func.blocks.len {
371 +
            if s.e.error <> nil {
372 +
                return;
373 +
            }
374 +
            selectBlock(&mut s, i, &func.blocks[i], &frame, func);
375 +
        }
376 +
        // Emit epilogue.
377 +
        emit::emitEpilogue(s.e, &frame);
378 +
        // Patch local branches now that all blocks are emitted.
379 +
        emit::patchLocalBranches(s.e);
363 380
    }
364 -
    // Emit epilogue.
365 -
    emit::emitEpilogue(s.e, &frame);
366 -
    // Patch local branches now that all blocks are emitted.
367 -
    emit::patchLocalBranches(s.e);
368 381
}
369 382
370 383
/// Select instructions for a block.
371 -
unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *unsafe il::Block, frame: &emit::Frame, func: *unsafe il::Fn) {
384 +
unsafe fn selectBlock 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, block: &il::Block, frame: &emit::Frame, func: &il::Fn) where 'scratch: 'selection {
372 385
    // Record block address for branch patching.
373 386
    emit::recordBlock(s.e, blockIdx);
374 387
375 388
    // Block parameters are handled at jump sites (in `Jmp`/`Br`).
376 389
    // By the time we enter the block, the arguments have already been
377 390
    // moved to the parameter registers by the predecessor's terminator.
378 391
379 392
    // Process each instruction, auto-committing any pending spill after each.
380 393
    let hasLocs = block.locs.len > 0;
381 394
    for instr, i in block.instrs {
382 -
        if s.e.error <> nil { return; }
395 +
        if s.e.error <> nil {
396 +
            return;
397 +
        }
383 398
        // Record debug location before emitting machine instructions.
384 399
        if hasLocs {
385 400
            emit::recordSrcLoc(s.e, block.locs[i]);
386 401
        }
387 402
        set s.pendingSpill = nil;
396 411
        }
397 412
    }
398 413
}
399 414
400 415
/// Select instructions for a single IL instruction.
401 -
unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *unsafe il::Fn) {
416 +
unsafe fn selectInstr 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: &il::Fn) where 'scratch: 'selection {
402 417
    match instr {
403 418
        case il::Instr::BinOp { op, typ, dst, a, b } => {
404 419
            let rd = getDstReg(s, dst, super::SCRATCH1);
405 420
            let rs1 = resolveVal(s, super::SCRATCH1, a);
406 421
            selectAluBinOp(s, op, typ, rd, rs1, b);
461 476
                else =>
462 477
                    panic "selectInstr: invalid reserve operand",
463 478
            }
464 479
        },
465 480
        case il::Instr::Blit { dst, src, size } => {
466 -
            let case il::Val::Imm(staticSize) = size
467 -
                else panic "selectInstr: blit requires immediate size";
468 -
469 -
            let bothSpilled = regalloc::spill::isSpilled(&s.ralloc.spill, dst)
470 -
                and regalloc::spill::isSpilled(&s.ralloc.spill, src);
471 -
472 -
            // When both are spilled, offsets must fit 12-bit immediates
473 -
            // since we can't advance base registers (they live in spill
474 -
            // slots, not real registers we can mutate).
475 -
            assert not (bothSpilled and staticSize as i32 > super::MAX_IMM), "selectInstr: blit both-spilled with large size";
476 -
477 -
            // Resolve dst/src base registers.
478 -
            let mut rdst = super::SCRATCH2;
479 -
            let mut rsrc = super::SCRATCH1;
480 -
            let mut srcReload: ?i32 = nil;
481 -
482 -
            if bothSpilled {
483 -
                let dstSlot = regalloc::spill::spillSlot(&s.ralloc.spill, dst) else {
484 -
                    panic "selectInstr: blit dst not spilled";
485 -
                };
486 -
                let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else {
487 -
                    panic "selectInstr: blit src not spilled";
488 -
                };
489 -
                emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot));
490 -
                set srcReload = spillOffset(s, srcSlot);
491 -
            } else {
492 -
                set rdst = getSrcReg(s, dst, super::SCRATCH2);
493 -
                set rsrc = getSrcReg(s, src, super::SCRATCH2);
494 -
            }
495 -
            let mut offset: i32 = 0;
496 -
            let mut remaining = staticSize as i32;
497 -
498 -
            // For large blits where both pointers are in real registers,
499 -
            // use an inline loop instead of unrolled LD/SD pairs.
500 -
            let dwordBytes = remaining & ~(super::DWORD_SIZE - 1);
501 -
            let canLoop = not bothSpilled
502 -
                and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2
503 -
                and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2;
504 -
505 -
            if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD {
506 -
                emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes);
507 -
508 -
                let loopStart = s.e.codeLen;
509 -
510 -
                emit::emitLd(s.e, super::SCRATCH2, rsrc, 0);
511 -
                emit::emitSd(s.e, super::SCRATCH2, rdst, 0);
512 -
                emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE));
513 -
514 -
                if *rdst <> *rsrc {
515 -
                    emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE));
516 -
                }
517 -
                let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE;
518 -
519 -
                emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff));
520 -
                set remaining -= dwordBytes;
521 -
            }
522 -
523 -
            // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time.
524 -
            // Before each load/store pair, check whether the offset is
525 -
            // about to exceed the 12-bit signed immediate range. When
526 -
            // it does, advance the base registers by the accumulated
527 -
            // offset and reset to zero.
528 -
            while remaining >= super::DWORD_SIZE {
529 -
                if offset > super::MAX_IMM - super::DWORD_SIZE {
530 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
531 -
                    if *rdst <> *rsrc {
532 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
533 -
                    }
534 -
                    set offset = 0;
535 -
                }
536 -
                if let off = srcReload {
537 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
538 -
                    emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset);
539 -
                } else {
540 -
                    emit::emitLd(s.e, super::SCRATCH1, rsrc, offset);
541 -
                }
542 -
                emit::emitSd(s.e, super::SCRATCH1, rdst, offset);
543 -
                set offset += super::DWORD_SIZE;
544 -
                set remaining -= super::DWORD_SIZE;
545 -
            }
546 -
            if remaining >= super::WORD_SIZE {
547 -
                if offset > super::MAX_IMM - super::WORD_SIZE {
548 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
549 -
                    if *rdst <> *rsrc {
550 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
551 -
                    }
552 -
                    set offset = 0;
553 -
                }
554 -
                if let off = srcReload {
555 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
556 -
                    emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset);
557 -
                } else {
558 -
                    emit::emitLw(s.e, super::SCRATCH1, rsrc, offset);
559 -
                }
560 -
                emit::emitSw(s.e, super::SCRATCH1, rdst, offset);
561 -
                set offset += super::WORD_SIZE;
562 -
                set remaining -= super::WORD_SIZE;
481 +
            let case il::Val::Imm(staticSize) = size else {
482 +
                set s.e.error = super::Error::Capacity; return;
483 +
            };
484 +
            if staticSize < 0 or staticSize > 0x7fffffff {
485 +
                set s.e.error = super::Error::Capacity; return;
563 486
            }
564 -
            while remaining > 0 {
565 -
                if offset > super::MAX_IMM - 1 {
566 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
567 -
                    if *rdst <> *rsrc {
568 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
569 -
                    }
570 -
                    set offset = 0;
571 -
                }
572 -
                if let off = srcReload {
573 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
574 -
                    emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset);
575 -
                } else {
576 -
                    emit::emitLb(s.e, super::SCRATCH1, rsrc, offset);
577 -
                }
578 -
                emit::emitSb(s.e, super::SCRATCH1, rdst, offset);
579 -
                set offset += 1;
580 -
                set remaining -= 1;
487 +
            if staticSize == 0 {
488 +
                return;
581 489
            }
582 -
            // Restore base registers if they were advanced (never happens
583 -
            // in the both-spilled case since size <= MAX_IMM).
584 -
            if not bothSpilled {
585 -
                let advanced = staticSize as i32 - offset;
586 -
                if advanced <> 0 {
587 -
                    emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced);
588 -
                    if *rdst <> *rsrc {
589 -
                        emit::emitAddImm(s.e, rdst, rdst, 0 - advanced);
590 -
                    }
490 +
            let rdst = getSrcReg(s, dst, super::SCRATCH2);
491 +
            let rsrc = getSrcReg(s, src, super::SCRATCH1);
492 +
            // Blit addresses have byte alignment. Small copies need no loop state.
493 +
            if staticSize < super::BLIT_LOOP_THRESHOLD as i64 {
494 +
                for offset in 0..staticSize as u32 {
495 +
                    emit::emitLb(s.e, super::ADDR_SCRATCH, rsrc, offset as i32);
496 +
                    emit::emitSb(s.e, super::ADDR_SCRATCH, rdst, offset as i32);
591 497
                }
498 +
            } else {
499 +
                // Private cursors preserve both input pointers. Save the count
500 +
                // register because it can hold a live allocated value.
501 +
                emit::emit(s.e, encode::addi(super::ADDR_SCRATCH, rsrc, 0));
502 +
                emit::emit(s.e, encode::addi(super::SCRATCH2, rdst, 0));
503 +
                emit::emit(s.e, encode::addi(super::SP, super::SP, -16));
504 +
                emit::emitSd(s.e, super::T3, super::SP, 0);
505 +
                emit::loadImm(s.e, super::T3, staticSize);
506 +
                let start = s.e.codeLen;
507 +
                emit::emitLb(s.e, super::SCRATCH1, super::ADDR_SCRATCH, 0);
508 +
                emit::emitSb(s.e, super::SCRATCH1, super::SCRATCH2, 0);
509 +
                emit::emit(s.e, encode::addi(super::ADDR_SCRATCH, super::ADDR_SCRATCH, 1));
510 +
                emit::emit(s.e, encode::addi(super::SCRATCH2, super::SCRATCH2, 1));
511 +
                emit::emit(s.e, encode::addi(super::T3, super::T3, -1));
512 +
                let offset = (start as i32 - s.e.codeLen as i32) * super::INSTR_SIZE;
513 +
                emit::emit(s.e, encode::bne(super::T3, super::ZERO, offset));
514 +
                emit::emitLd(s.e, super::T3, super::SP, 0);
515 +
                emit::emit(s.e, encode::addi(super::SP, super::SP, 16));
592 516
            }
593 517
        },
594 518
        case il::Instr::Zext { typ, dst, val } => {
595 519
            let rd = getDstReg(s, dst, super::SCRATCH1);
596 520
            let rs = resolveVal(s, super::SCRATCH1, val);
717 641
            if let case il::Val::Reg(r) = func {
718 642
                let target = getSrcReg(s, r, super::SCRATCH2);
719 643
                emitMv(s, super::SCRATCH2, target);
720 644
            }
721 645
            // Move arguments to A0-A7 using parallel move resolution.
722 -
            if args.len > super::ARG_REGS.len { set s.e.error = super::Error::Capacity; return; }
646 +
            if args.len > super::ARG_REGS.len {
647 +
                set s.e.error = super::Error::Capacity;
648 +
                return;
649 +
            }
723 650
            emitParallelMoves(s, &super::ARG_REGS[..], args);
724 651
725 652
            // Emit call.
726 653
            match func {
727 654
                case il::Val::FnAddr(name) => {
752 679
753 680
            // Result in A0.
754 681
            let ecallRd = getDstReg(s, dst, super::SCRATCH1);
755 682
            emitMv(s, ecallRd, super::A0);
756 683
        },
684 +
        case il::Instr::DeviceRead { typ, dst, handle, offset } => {
685 +
            deviceAddress(s, typ, handle, offset, il::Val::Imm(0), false);
686 +
            let rd = getDstReg(s, dst, super::SCRATCH1);
687 +
            match typ {
688 +
                case il::Type::W8 => emit::emit(s.e, encode::lbu(rd, super::A0, 0)),
689 +
                case il::Type::W16 => emit::emit(s.e, encode::lhu(rd, super::A0, 0)),
690 +
                case il::Type::W32 => emit::emit(s.e, encode::lwu(rd, super::A0, 0)),
691 +
                case il::Type::W64 => emit::emit(s.e, encode::ld(rd, super::A0, 0)),
692 +
            }
693 +
            emit::emit(s.e, encode::fence());
694 +
        },
695 +
        case il::Instr::DeviceWrite { typ, handle, offset, value } => {
696 +
            deviceAddress(s, typ, handle, offset, value, true);
697 +
            match typ {
698 +
                case il::Type::W8 => emit::emit(s.e, encode::sb(super::A3, super::A0, 0)),
699 +
                case il::Type::W16 => emit::emit(s.e, encode::sh(super::A3, super::A0, 0)),
700 +
                case il::Type::W32 => emit::emit(s.e, encode::sw(super::A3, super::A0, 0)),
701 +
                case il::Type::W64 => emit::emit(s.e, encode::sd(super::A3, super::A0, 0)),
702 +
            }
703 +
            emit::emit(s.e, encode::fence());
704 +
        },
757 705
        case il::Instr::Ebreak => {
758 706
            emit::emit(s.e, encode::ebreak());
759 707
        },
760 708
        case il::Instr::MemoryFence => {
761 709
            emit::emit(s.e, encode::fence());
840 788
    return false;
841 789
}
842 790
843 791
/// Select a binary ALU operation, dispatching to the appropriate
844 792
/// instruction pattern based on the operation kind and type.
845 -
unsafe fn selectAluBinOp(s: &mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) {
793 +
unsafe fn selectAluBinOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) where 'scratch: 'selection {
846 794
    match op {
847 795
        case il::BinOp::Add => {
848 796
            if typ == il::Type::W32 {
849 797
                // Inline W32 ADD with immediate optimization.
850 798
                if let case il::Val::Imm(imm) = b {
968 916
            selectCmp(s, typ, rd, rs1, b, CmpOp::Ult, true, super::SCRATCH2),
969 917
    }
970 918
}
971 919
972 920
/// Select a unary ALU operation.
973 -
unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) {
921 +
unsafe fn selectAluUnOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) where 'scratch: 'selection {
974 922
    match op {
975 923
        case il::UnOp::Neg => {
976 924
            if typ == il::Type::W32 {
977 925
                emit::emit(s.e, encode::subw(rd, super::ZERO, rs));
978 926
            } else {
983 931
            emit::emit(s.e, encode::not_(rd, rs)),
984 932
    }
985 933
}
986 934
987 935
/// Select binary operation with immediate optimization.
988 -
unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) {
936 +
unsafe fn selectBinOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) where 'scratch: 'selection {
989 937
    // Try immediate optimization first.
990 938
    if let case il::Val::Imm(imm) = b {
991 939
        if encode::isSmallImm64(imm) {
992 940
            let simm = imm as i32;
993 941
            match op {
1010 958
}
1011 959
1012 960
/// Select shift operation with immediate optimization.
1013 961
/// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits
1014 962
/// and sign-extend the result.
1015 -
unsafe fn selectShift(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) {
963 +
unsafe fn selectShift 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) where 'scratch: 'selection {
1016 964
    let isW32: bool = typ == il::Type::W32;
1017 965
1018 966
    // Try immediate optimization first.
1019 967
    if let case il::Val::Imm(shamt) = b {
1020 968
        // Keep immediate forms only for encodable shift amounts.
1063 1011
/// 1. Identifies "ready" moves.
1064 1012
/// 2. Executes ready moves.
1065 1013
/// 3. Breaks cycles using scratch register.
1066 1014
///
1067 1015
/// Entries with `ZERO` destination are skipped, as they are handled by caller.
1068 -
unsafe fn emitParallelMoves(s: &mut Selector, dsts: &[gen::Reg], args: &[il::Val]) {
1016 +
unsafe fn emitParallelMoves 'scratch 'selection (s: &mut Selector 'scratch 'selection, dsts: &[gen::Reg], args: &[il::Val]) where 'scratch: 'selection {
1069 1017
    let n: u32 = args.len;
1070 1018
    if n == 0 {
1071 1019
        return;
1072 1020
    }
1073 -
    if n > MAX_BLOCK_ARGS { set s.e.error = super::Error::Capacity; return; }
1021 +
    if n > MAX_BLOCK_ARGS {
1022 +
        set s.e.error = super::Error::Capacity;
1023 +
        return;
1024 +
    }
1074 1025
    // Source registers for each arg.
1075 1026
    let mut srcRegs: [gen::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
1076 1027
    // If this is a register-to-register move.
1077 1028
    let mut isRegMove: [bool; MAX_BLOCK_ARGS] = [false; MAX_BLOCK_ARGS];
1078 1029
    // If this move still needs to be executed.
1174 1125
/// Emit moves from block arguments to target block's parameter registers.
1175 1126
///
1176 1127
/// Handles spilled destinations directly, then delegates to [`emitParallelMoves`]
1177 1128
/// for the remaining register-to-register parallel move resolution. Edges that
1178 1129
/// would overwrite an unconsumed spill source are unsupported.
1179 -
unsafe fn emitBlockArgs(s: &mut Selector, func: *unsafe il::Fn, target: u32, args: &[il::Val]) {
1130 +
unsafe fn emitBlockArgs 'scratch 'selection (s: &mut Selector 'scratch 'selection, func: &il::Fn, target: u32, args: &[il::Val]) where 'scratch: 'selection {
1180 1131
    if args.len == 0 {
1181 1132
        return;
1182 1133
    }
1183 1134
    let block = &func.blocks[target];
1184 1135
    assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch";
1185 -
    if args.len > MAX_BLOCK_ARGS { set s.e.error = super::Error::Capacity; return; }
1136 +
    if args.len > MAX_BLOCK_ARGS {
1137 +
        set s.e.error = super::Error::Capacity;
1138 +
        return;
1139 +
    }
1186 1140
1187 1141
    // The parallel-move resolver only handles register destinations. Keep eager
1188 1142
    // stores for independent spill slots, but reject dependencies that would
1189 1143
    // require stack staging rather than silently miscompiling them.
1190 1144
    for arg, i in args {
1240 1194
    }
1241 1195
    emitParallelMoves(s, &dsts[..], args);
1242 1196
}
1243 1197
1244 1198
/// Select a comparison with immediate optimization.
1245 -
unsafe fn selectCmp(
1246 -
    s: &mut Selector,
1199 +
unsafe fn selectCmp 'scratch 'selection (
1200 +
    s: &mut Selector 'scratch 'selection,
1247 1201
    typ: il::Type,
1248 1202
    rd: gen::Reg,
1249 1203
    rs1: gen::Reg,
1250 1204
    b: il::Val,
1251 1205
    op: CmpOp,
1252 1206
    invert: bool,
1253 1207
    scratch: gen::Reg
1254 -
) {
1208 +
) where 'scratch: 'selection {
1255 1209
    let mut signed = false;
1256 1210
    if let case CmpOp::Slt = op {
1257 1211
        set signed = true;
1258 1212
    }
1259 1213
    let useSext = cmpUsesSext(typ, signed);
1288 1242
    }
1289 1243
    if invert {
1290 1244
        emit::emit(s.e, encode::xori(rd, rd, 1));
1291 1245
    }
1292 1246
}
1247 +
1248 +
/// Resolve one live Device access before an ordered user-mode register instruction.
1249 +
unsafe fn deviceAddress 'scratch 'selection (
1250 +
    s: &mut Selector 'scratch 'selection,
1251 +
    typ: il::Type,
1252 +
    handle: il::Val,
1253 +
    offset: il::Val,
1254 +
    value: il::Val,
1255 +
    writing: bool
1256 +
) where 'scratch: 'selection {
1257 +
    let mut flags = il::typeSize(typ) as i64;
1258 +
    if writing {
1259 +
        set flags |= 0x100;
1260 +
    }
1261 +
    let dsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3];
1262 +
    let args: [il::Val; 5] = [il::Val::Imm(il::DEVICE_ACCESS as i64), handle, offset, il::Val::Imm(flags), value];
1263 +
    emitParallelMoves(s, &dsts[..], &args[..]);
1264 +
    emit::emit(s.e, encode::ecall());
1265 +
    emit::emit(s.e, encode::fence());
1266 +
}
lib/std/arch/rv64/printer.rad +22 -7
301 301
            let stem = atomics::name(instruction.format.operation) else panic "invalid atomic operation";
302 302
            write(out, stem);
303 303
            write(out, ".w" if instruction.format.width == 2 else ".d");
304 304
            match instruction.format.order {
305 305
                case 1 => write(out, ".rl"), case 2 => write(out, ".aq"), case 3 => write(out, ".aqrl"),
306 -
                else => {},
306 +
                else => {
307 +
                },
307 308
            }
308 309
            write(out, " "); write(out, regNameR(instruction.rd)); write(out, ", ");
309 -
            if instruction.format.operation <> 2 { write(out, regNameR(instruction.rs2)); write(out, ", "); }
310 +
            if instruction.format.operation <> 2 {
311 +
                write(out, regNameR(instruction.rs2));
312 +
                write(out, ", ");
313 +
            }
310 314
            write(out, "0("); write(out, regNameR(instruction.rs1)); write(out, ")");
311 315
        },
312 316
        case decode::Instr::Fence { predecessor, successor } => {
313 317
            write(out, "fence "); fenceMask(out, predecessor); write(out, ", "); fenceMask(out, successor);
314 318
        },
350 354
    return nil;
351 355
}
352 356
353 357
/// Print a memory-ordering mask in canonical order.
354 358
unsafe fn fenceMask(out: &mut sexpr::Output, mask: u32) {
355 -
    if mask == 0 { write(out, "0"); return; }
356 -
    if (mask & 8) <> 0 { write(out, "i"); }
357 -
    if (mask & 4) <> 0 { write(out, "o"); }
358 -
    if (mask & 2) <> 0 { write(out, "r"); }
359 -
    if (mask & 1) <> 0 { write(out, "w"); }
359 +
    if mask == 0 {
360 +
        write(out, "0");
361 +
        return;
362 +
    }
363 +
    if (mask & 8) <> 0 {
364 +
        write(out, "i");
365 +
    }
366 +
    if (mask & 4) <> 0 {
367 +
        write(out, "o");
368 +
    }
369 +
    if (mask & 2) <> 0 {
370 +
        write(out, "r");
371 +
    }
372 +
    if (mask & 1) <> 0 {
373 +
        write(out, "w");
374 +
    }
360 375
}
lib/std/arch/rv64/shared.rad +191 -56
1 1
//! Shared package code, private data templates, and qualified symbol linking.
2 2
3 3
@test export mod tests;
4 +
export mod catalog;
4 5
5 6
use std::mem;
6 7
use std::lang::alloc;
7 8
use std::lang::il;
8 9
use std::lang::il::binary;
9 10
use std::lang::gen;
10 11
use std::lang::gen::data;
11 12
use super::emit;
12 13
use super::encode;
13 14
use super::image;
15 +
use super::asm;
14 16
15 17
/// Maximum number of resident package slots in a domain state table.
16 18
export constant MAX_PACKAGES: u32 = 256;
17 19
18 20
/// Package linking or instance storage failure.
106 108
}
107 109
108 110
/// Find exactly one qualified symbol in a bounded table.
109 111
export fn lookup(symbols: &[Symbol], name: &[u8]) -> ?Target {
110 112
    for symbol in symbols {
111 -
        if mem::eq(symbol.name, name) { return symbol.target; }
113 +
        if mem::eq(symbol.name, name) {
114 +
            return symbol.target;
115 +
        }
112 116
    }
113 117
    return nil;
114 118
}
115 119
116 120
/// Resolve a local or imported symbol.
117 121
fn resolve(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> Target throws (Error) {
118 -
    if let target = lookup(local, name) { return target; }
119 -
    let target = lookup(imports, name) else { throw Error::Symbol; };
122 +
    if let target = lookup(local, name) {
123 +
        return target;
124 +
    }
125 +
    let target = lookup(imports, name) else {
126 +
        throw Error::Symbol;
127 +
    };
120 128
    return target;
121 129
}
122 130
123 131
/// Append a unique local definition to bounded symbol storage.
124 132
fn define(symbols: &mut [Symbol], count: &mut u32, name: *[u8], target: Target) throws (Error) {
125 -
    if lookup(&symbols[..*count], name) <> nil { throw Error::Symbol; }
126 -
    if *count == symbols.len { throw Error::Capacity; }
133 +
    if lookup(&symbols[..*count], name) <> nil {
134 +
        throw Error::Symbol;
135 +
    }
136 +
    if *count == symbols.len {
137 +
        throw Error::Capacity;
138 +
    }
127 139
    set symbols[*count] = Symbol { name, target };
128 140
    set *count += 1;
129 141
}
130 142
131 143
/// Resolve a shared function address with its required kind.
146 158
147 159
/// Patch a PC-relative call or function address load.
148 160
unsafe fn relative(e: &mut emit::Emitter, index: u32, base: u64, target: u64, rd: gen::Reg, call: bool)
149 161
    throws (Error)
150 162
{
151 -
    let delta = image::displacement(base + index as u64 * 4, target) else { throw Error::Range; };
163 +
    let delta = image::displacement(base + index as u64 * 4, target) else {
164 +
        throw Error::Range;
165 +
    };
152 166
    let parts = emit::splitImm(delta);
153 167
    emit::patch(e, index, encode::auipc(rd, parts.hi));
154 168
    if call {
155 169
        emit::patch(e, index + 1, encode::jalr(super::RA, rd, parts.lo));
156 170
    } else {
158 172
    }
159 173
}
160 174
161 175
/// Link shared code against local definitions and dependency exports.
162 176
unsafe fn link(e: &mut emit::Emitter, base: u64, local: &[Symbol], imports: &[Symbol]) throws (Error) {
163 -
    for i in 0..e.pendingCalls.len {
177 +
    for i in 0..e.pendingCallsLen {
164 178
        let pending = e.pendingCalls[i];
165 179
        let address = try function(local, imports, pending.target);
166 180
        try relative(e, pending.index, base, address, super::SCRATCH1, true);
167 181
    }
168 -
    for i in 0..e.pendingJumps.len {
182 +
    for i in 0..e.pendingJumpsLen {
169 183
        let pending = e.pendingJumps[i];
170 184
        let address = try function(local, imports, pending.target);
171 -
        let delta = image::displacement(base + pending.index as u64 * 4, address) else { throw Error::Range; };
172 -
        if not encode::isJumpImm(delta) { throw Error::Range; }
185 +
        let delta = image::displacement(base + pending.index as u64 * 4, address) else {
186 +
            throw Error::Range;
187 +
        };
188 +
        if not encode::isJumpImm(delta) {
189 +
            throw Error::Range;
190 +
        }
173 191
        emit::patch(e, pending.index, encode::jal(pending.rd, delta));
174 192
    }
175 -
    for i in 0..e.pendingAddrLoads.len {
193 +
    for i in 0..e.pendingAddrLoadsLen {
176 194
        let pending = e.pendingAddrLoads[i];
177 195
        if not pending.isData {
178 196
            try relative(e, pending.index, base, try function(local, imports, pending.target), pending.rd, false);
179 197
            continue;
180 198
        }
181 199
        let target = try resolve(local, imports, pending.target);
182 200
        let location = try dataRef(target);
183 -
        if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff { throw Error::Range; }
201 +
        if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff {
202 +
            throw Error::Range;
203 +
        }
184 204
        if pending.rd == super::ADDR_SCRATCH or pending.rd == super::GP or pending.rd == super::ZERO {
185 205
            throw Error::Symbol;
186 206
        }
187 207
        let parts = emit::splitImm(location.offset as i32);
188 208
        emit::patch(e, pending.index, encode::ld(pending.rd, super::GP, location.slot as i32 * 8));
192 212
    }
193 213
}
194 214
195 215
/// Write a little-endian integer into a validated byte extent.
196 216
fn integer(bytes: &mut [u8], offset: u32, value: u64, width: u32) {
197 -
    for i in 0..width { set bytes[offset + i] = (value >> (i as u64 * 8)) as u8; }
217 +
    for i in 0..width {
218 +
        set bytes[offset + i] = (value >> (i as u64 * 8)) as u8;
219 +
    }
198 220
}
199 221
200 222
/// Initialized output extents.
201 223
record TemplateSize: Copy {
202 224
    /// Initialized byte count.
210 232
    -> TemplateSize throws (Error)
211 233
{
212 234
    let mut initialized: u32 = 0;
213 235
    let mut count: u32 = 0;
214 236
    for item in items {
215 -
        if item.isZeroInit { continue; }
237 +
        if item.isZeroInit {
238 +
            continue;
239 +
        }
216 240
        let location = try dataRef(try resolve(local, &[], item.name));
217 -
        if location.offset > bytes.len or item.size > bytes.len - location.offset { throw Error::Capacity; }
241 +
        if location.offset > bytes.len or item.size > bytes.len - location.offset {
242 +
            throw Error::Capacity;
243 +
        }
218 244
        let end = location.offset + item.size;
219 -
        if end > initialized { set initialized = end; }
245 +
        if end > initialized {
246 +
            set initialized = end;
247 +
        }
248 +
    }
249 +
    for i in 0..initialized {
250 +
        set bytes[i] = 0;
220 251
    }
221 -
    for i in 0..initialized { set bytes[i] = 0; }
222 252
    for item in items {
223 -
        if item.isZeroInit { continue; }
253 +
        if item.isZeroInit {
254 +
            continue;
255 +
        }
224 256
        let location = try dataRef(try resolve(local, &[], item.name));
225 257
        let mut offset = location.offset;
226 258
        let end = offset + item.size;
227 259
        for value in item.values {
228 260
            let mut width: u32 = 1;
229 261
            let mut number: u64 = 0;
230 262
            match value.item {
231 -
                case il::DataItem::Val { typ, val } => { set width = il::typeSize(typ); set number = val as u64; },
232 -
                case il::DataItem::Fn(name) => { set width = 8; set number = try function(local, imports, name); },
263 +
                case il::DataItem::Val { typ, val } => {
264 +
                    set width = il::typeSize(typ);
265 +
                    set number = val as u64;
266 +
                },
267 +
                case il::DataItem::Fn(name) => {
268 +
                    set width = 8;
269 +
                    set number = try function(local, imports, name);
270 +
                },
233 271
                case il::DataItem::Sym(name) => {
234 272
                    set width = 8;
235 273
                    let target = try dataRef(try resolve(local, imports, name));
236 274
                    if value.count > 0 {
237 -
                        if count == relocs.len { throw Error::Capacity; }
275 +
                        if count == relocs.len {
276 +
                            throw Error::Capacity;
277 +
                        }
238 278
                        set relocs[count] = Relocation { offset, count: value.count, target };
239 279
                        set count += 1;
240 280
                    }
241 281
                },
242 -
                case il::DataItem::Str(s) => { set width = s.len; },
243 -
                case il::DataItem::Undef => {},
282 +
                case il::DataItem::Str(s) => {
283 +
                    set width = s.len;
284 +
                },
285 +
                case il::DataItem::Undef => {
286 +
                },
287 +
            }
288 +
            if width > 0 and value.count > (end - offset) / width {
289 +
                throw Error::Range;
290 +
            }
291 +
            if width == 0 {
292 +
                continue;
244 293
            }
245 -
            if width > 0 and value.count > (end - offset) / width { throw Error::Range; }
246 -
            if width == 0 { continue; }
247 294
            for _ in 0..value.count {
248 295
                match value.item {
249 -
                    case il::DataItem::Str(s) => { try! mem::copy(&mut bytes[offset..offset + width], s); },
250 -
                    else => { integer(bytes, offset, number, width); },
296 +
                    case il::DataItem::Str(s) => {
297 +
                        try! mem::copy(&mut bytes[offset..offset + width], s);
298 +
                    },
299 +
                    else => {
300 +
                        integer(bytes, offset, number, width);
301 +
                    },
251 302
                }
252 303
                set offset += width;
253 304
            }
254 305
        }
255 306
    }
256 307
    return TemplateSize { bytes: initialized, relocations: count };
257 308
}
258 309
310 +
/// Package definitions and their native assembly boundaries.
311 +
export record AssemblyInput: Copy {
312 +
    /// Trusted binary RIL definitions that remain valid during compilation.
313 +
    package: *unsafe binary::Package,
314 +
    /// Text-only assembly prefix with exported native boundaries.
315 +
    assembly: asm::Program,
316 +
}
317 +
259 318
/// Compile one package into caller-owned code, symbol, and private template storage.
260 319
/// Imports must contain unique exports from the package's admitted dependencies.
261 320
/// Arena storage and binary package names must outlive the returned catalog entry.
262 321
export unsafe fn compile(input: &binary::Package, slot: u32, codeAddress: u64, imports: &[Symbol],
263 322
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
323 +
{
324 +
    return try compileAssembly(AssemblyInput { package: input as *unsafe binary::Package,
325 +
        assembly: asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] } },
326 +
        slot, codeAddress, imports, storage, arena, scratch);
327 +
}
328 +
329 +
/// Compile a package with a text-only assembly prefix and its exported boundaries.
330 +
/// Assembly and RIL definitions share one native code extent and package-state slot.
331 +
export unsafe fn compileAssembly(source: AssemblyInput, slot: u32, codeAddress: u64, imports: &[Symbol],
332 +
    storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error)
264 333
{
265 334
    let case Storage { data: dataStorage, symbols: symbolStorage, exports: exportStorage, template: templateStorage, relocations: relocationStorage } = storage
266 335
        else panic "expected shared linker storage";
267 -
    if slot >= MAX_PACKAGES { throw Error::Range; }
268 -
    if (codeAddress & 3) <> 0 { throw Error::Alignment; }
336 +
    let input = source.package;
337 +
    let assembly = source.assembly;
338 +
    if assembly.data.len <> 0 {
339 +
        throw Error::Range;
340 +
    }
341 +
    if slot >= MAX_PACKAGES {
342 +
        throw Error::Range;
343 +
    }
344 +
    if (codeAddress & 3) <> 0 {
345 +
        throw Error::Alignment;
346 +
    }
269 347
    for imported, i in imports {
270 -
        if lookup(&imports[..i], imported.name) <> nil { throw Error::Symbol; }
348 +
        if lookup(&imports[..i], imported.name) <> nil {
349 +
            throw Error::Symbol;
350 +
        }
271 351
    }
272 352
    let mut generator = try super::beginProgram(super::ProgramOptions {
273 353
        entryPatch: super::EntryPatch::None, debug: false, placement: image::Placement::Hosted,
274 -
    }, arena) catch err { throw Error::Codegen(err); };
354 +
    }, arena) catch err {
355 +
        throw Error::Codegen(err);
356 +
    };
275 357
    set generator.e.sharedData = true;
276 -
    for func in input.program.fns { super::generateFunction(&mut generator, func, scratch); }
277 -
    try emit::check(&generator.e) catch err { throw Error::Codegen(err); };
278 -
    if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 { throw Error::Range; }
358 +
    super::addAssembly(&mut generator, assembly);
359 +
    for func in input.program.fns {
360 +
        super::generateFunction(&mut generator, func, scratch);
361 +
    }
362 +
    try emit::check(&generator.e) catch err {
363 +
        throw Error::Codegen(err);
364 +
    };
365 +
    if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 {
366 +
        throw Error::Range;
367 +
    }
279 368
    let mut symbols: u32 = 0;
280 -
    for func in &generator.e.funcs[..] {
369 +
    for func in &generator.e.funcs[..generator.e.funcsLen] {
281 370
        try define(symbolStorage, &mut symbols, func.name, Target::Function(codeAddress + func.index as u64 * 4));
282 371
    }
283 372
    let mut dataCount: u32 = 0;
284 373
    let roSize = try data::layoutSection(input.program.data, dataStorage, &mut dataCount, 0, true)
285 -
        catch { throw Error::Range; };
374 +
        catch {
375 +
            throw Error::Range;
376 +
        };
286 377
    let memory = try data::layoutSectionAtOffset(input.program.data, dataStorage, &mut dataCount, 0, roSize, false)
287 -
        catch { throw Error::Range; };
288 -
    if memory > 0x7ffff7ff { throw Error::Range; }
378 +
        catch {
379 +
            throw Error::Range;
380 +
        };
381 +
    if memory > 0x7ffff7ff {
382 +
        throw Error::Range;
383 +
    }
289 384
    for item in &dataStorage[..dataCount] {
290 385
        try define(symbolStorage, &mut symbols, item.name, Target::Data(DataRef { slot, offset: item.addr as u32 }));
291 386
    }
292 387
    let local = &symbolStorage[..symbols];
293 -
    for symbol in local { if lookup(imports, symbol.name) <> nil { throw Error::Symbol; } }
388 +
    for symbol in local {
389 +
        if lookup(imports, symbol.name) <> nil {
390 +
            throw Error::Symbol;
391 +
        }
392 +
    }
294 393
    try link(&mut generator.e, codeAddress, local, imports);
295 -
    try emit::check(&generator.e) catch err { throw Error::Codegen(err); };
394 +
    try emit::check(&generator.e) catch err {
395 +
        throw Error::Codegen(err);
396 +
    };
296 397
    let size = try template(input.program.data, local, imports, templateStorage, relocationStorage);
297 -
    if input.exports.len > exportStorage.len { throw Error::Capacity; }
398 +
    if input.exports.len > exportStorage.len {
399 +
        throw Error::Capacity;
400 +
    }
298 401
    for exported, i in input.exports {
299 402
        let target = try resolve(local, &[], exported.name);
300 403
        match exported.kind {
301 -
            case binary::ExportKind::Function => { try function(local, &[], exported.name); },
302 -
            case binary::ExportKind::Data => { try dataRef(target); },
404 +
            case binary::ExportKind::Function => {
405 +
                try function(local, &[], exported.name);
406 +
            },
407 +
            case binary::ExportKind::Data => {
408 +
                try dataRef(target);
409 +
            },
410 +
        }
411 +
        if lookup(&exportStorage[..i], exported.name) <> nil {
412 +
            throw Error::Symbol;
303 413
        }
304 -
        if lookup(&exportStorage[..i], exported.name) <> nil { throw Error::Symbol; }
305 414
        set exportStorage[i] = Symbol { name: exported.name, target };
306 415
    }
307 416
    let mut entry: ?u64 = nil;
308 -
    if let name = input.entry { set entry = try function(&exportStorage[..input.exports.len], &[], name); }
417 +
    if let name = input.entry {
418 +
        set entry = try function(&exportStorage[..input.exports.len], &[], name);
419 +
    }
309 420
    let mut alignment: u32 = 8;
310 -
    for item in input.program.data { if item.alignment > alignment { set alignment = item.alignment; } }
421 +
    for item in input.program.data {
422 +
        if item.alignment > alignment {
423 +
            set alignment = item.alignment;
424 +
        }
425 +
    }
426 +
    let case super::Generator { e, .. } = generator else panic "expected package generator";
427 +
    let case emit::Emitter { code, codeLen, .. } = e else panic "expected package emitter";
311 428
    return Package {
312 429
        name: input.name, dependencies: input.dependencies, slot, codeAddress,
313 -
        code: emit::getCode(&generator.e), exports: &exportStorage[..input.exports.len], entry,
430 +
        code: &code[..codeLen], exports: &exportStorage[..input.exports.len], entry,
314 431
        template: &templateStorage[..size.bytes], memory, alignment,
315 432
        relocations: &relocationStorage[..size.relocations],
316 433
    };
317 434
}
318 435
319 436
/// Initialize private package memory after all graph bases have been assigned.
320 437
/// Validate every fixup before writing bytes so a rejected instance stays intact.
321 438
export fn instantiate(package: &Package, bases: &[u64], memory: &mut [u8]) throws (Error) {
322 -
    if package.slot >= bases.len or bases[package.slot] == 0 { throw Error::Instance; }
323 -
    if memory.len < package.memory or package.template.len > package.memory { throw Error::Capacity; }
324 -
    if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 { throw Error::Alignment; }
325 -
    if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 { throw Error::Range; }
439 +
    if package.slot >= bases.len or bases[package.slot] == 0 {
440 +
        throw Error::Instance;
441 +
    }
442 +
    if memory.len < package.memory or package.template.len > package.memory {
443 +
        throw Error::Capacity;
444 +
    }
445 +
    if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 {
446 +
        throw Error::Alignment;
447 +
    }
448 +
    if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 {
449 +
        throw Error::Range;
450 +
    }
326 451
    for fixup in package.relocations {
327 -
        if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 { throw Error::Instance; }
328 -
        if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 { throw Error::Range; }
329 -
        if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 { throw Error::Range; }
452 +
        if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 {
453 +
            throw Error::Instance;
454 +
        }
455 +
        if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 {
456 +
            throw Error::Range;
457 +
        }
458 +
        if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 {
459 +
            throw Error::Range;
460 +
        }
461 +
    }
462 +
    for i in 0..package.memory {
463 +
        set memory[i] = 0;
330 464
    }
331 -
    for i in 0..package.memory { set memory[i] = 0; }
332 465
    try! mem::copy(memory, package.template);
333 466
    for fixup in package.relocations {
334 467
        let address = bases[fixup.target.slot] + fixup.target.offset as u64;
335 -
        for i in 0..fixup.count { integer(memory, fixup.offset + i * 8, address, 8); }
468 +
        for i in 0..fixup.count {
469 +
            integer(memory, fixup.offset + i * 8, address, 8);
470 +
        }
336 471
    }
337 472
}
lib/std/arch/rv64/shared/catalog.rad added +131 -0
1 +
//! Persistent native boot catalogs for the RV64 record ABI.
2 +
3 +
use std::mem;
4 +
use std::arch::rv64::shared;
5 +
6 +
/// Binary package identity and its resident native descriptor.
7 +
export record Entry: Copy {
8 +
    /// Complete immutable binary RIL bytes.
9 +
    source: *[u8],
10 +
    /// Shared code, exports, and private-state template.
11 +
    package: shared::Package,
12 +
}
13 +
14 +
/// Reserve aligned bytes in the native catalog output.
15 +
fn reserve(output: &mut [u8], used: &mut u32, size: u32, alignment: u32) -> u32 throws (shared::Error) {
16 +
    let padding = (alignment - (*used & (alignment - 1))) & (alignment - 1);
17 +
    if padding > output.len - *used {
18 +
        throw shared::Error::Capacity;
19 +
    }
20 +
    let offset = *used + padding;
21 +
    if size > output.len - offset {
22 +
        throw shared::Error::Capacity;
23 +
    }
24 +
    for i in *used..offset {
25 +
        set output[i] = 0;
26 +
    }
27 +
    set *used = offset + size;
28 +
    return offset;
29 +
}
30 +
31 +
/// View the native bytes of a caller-owned record or array.
32 +
unsafe fn raw(value: &opaque, size: u32) -> *unsafe [u8] { return @sliceOf((value as &u8) as *unsafe u8, size); }
33 +
34 +
/// Obtain a field's byte offset from the compiler's native record layout.
35 +
unsafe fn field(container: &opaque, value: &opaque) -> u32 { return (value as u64 - container as u64) as u32; }
36 +
37 +
/// Write one little-endian integer into a reserved native field.
38 +
fn integer(output: &mut [u8], offset: u32, value: u64, width: u32) {
39 +
    for i in 0..width {
40 +
        set output[offset + i] = (value >> (i as u64 * 8)) as u8;
41 +
    }
42 +
}
43 +
44 +
/// Write the RV64 pointer, length, and capacity of a native immutable slice.
45 +
fn slice(output: &mut [u8], offset: u32, address: u64, length: u32) {
46 +
    integer(output, offset, address, 8);
47 +
    integer(output, offset + 8, length as u64, 4);
48 +
    integer(output, offset + 12, length as u64, 4);
49 +
}
50 +
51 +
/// Copy bytes into the catalog and return their target-relative offset.
52 +
fn bytes(input: *[u8], output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) {
53 +
    let offset = try reserve(output, used, input.len, 1);
54 +
    try! mem::copy(&mut output[offset..offset + input.len], input);
55 +
    return offset;
56 +
}
57 +
58 +
/// Copy a dependency list and each name into native catalog storage.
59 +
unsafe fn dependencies(input: *unsafe [*[u8]], base: u64, output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) {
60 +
    if input.len > output.len / @sizeOf(*[u8]) {
61 +
        throw shared::Error::Capacity;
62 +
    }
63 +
    let offset = try reserve(output, used, input.len * @sizeOf(*[u8]), @alignOf(*[u8]));
64 +
    for name, i in input {
65 +
        let at = try bytes(name, output, used);
66 +
        slice(output, offset + i * @sizeOf(*[u8]), base + at as u64, name.len);
67 +
    }
68 +
    return offset;
69 +
}
70 +
71 +
/// Copy public symbols and their names into native catalog storage.
72 +
unsafe fn symbols(input: *[shared::Symbol], base: u64, output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) {
73 +
    if input.len > output.len / @sizeOf(shared::Symbol) {
74 +
        throw shared::Error::Capacity;
75 +
    }
76 +
    let offset = try reserve(output, used, input.len * @sizeOf(shared::Symbol), @alignOf(shared::Symbol));
77 +
    for symbol, i in input {
78 +
        let at = offset + i * @sizeOf(shared::Symbol);
79 +
        try! mem::copy(&mut output[at..at + @sizeOf(shared::Symbol)], raw(&symbol, @sizeOf(shared::Symbol)));
80 +
        let name = try bytes(symbol.name, output, used);
81 +
        slice(output, at + field(&symbol, &symbol.name), base + name as u64, symbol.name.len);
82 +
    }
83 +
    return offset;
84 +
}
85 +
86 +
/// Copy private-state relocation records into native catalog storage.
87 +
unsafe fn relocations(input: *[shared::Relocation], output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) {
88 +
    if input.len > output.len / @sizeOf(shared::Relocation) {
89 +
        throw shared::Error::Capacity;
90 +
    }
91 +
    let size = input.len * @sizeOf(shared::Relocation);
92 +
    let offset = try reserve(output, used, size, @alignOf(shared::Relocation));
93 +
    try! mem::copy(&mut output[offset..offset + size], raw(input.ptr, size));
94 +
    return offset;
95 +
}
96 +
97 +
/// Pack native Entry records followed by all referenced metadata and source bytes.
98 +
/// Code is placed separately at each package's codeAddress. The target base must
99 +
/// have Entry alignment. Return the used extent. Failure leaves partial output.
100 +
export unsafe fn pack(entries: &[Entry], base: u64, output: &mut [u8]) -> u32 throws (shared::Error) {
101 +
    assert @sizeOf(*[u8]) == 16;
102 +
    if (base & (@alignOf(Entry) as u64 - 1)) <> 0 {
103 +
        throw shared::Error::Alignment;
104 +
    }
105 +
    if base > 0xffffffffffffffff - output.len as u64 {
106 +
        throw shared::Error::Range;
107 +
    }
108 +
    if entries.len > output.len / @sizeOf(Entry) {
109 +
        throw shared::Error::Capacity;
110 +
    }
111 +
    let mut used: u32 = 0;
112 +
    let start = try reserve(output, &mut used, entries.len * @sizeOf(Entry), @alignOf(Entry));
113 +
    for entry, i in entries {
114 +
        let at = i * @sizeOf(Entry);
115 +
        try! mem::copy(&mut output[at..at + @sizeOf(Entry)], raw(&entry, @sizeOf(Entry)));
116 +
        let source = try bytes(entry.source, output, &mut used);
117 +
        let name = try bytes(entry.package.name, output, &mut used);
118 +
        let required = try dependencies(entry.package.dependencies, base, output, &mut used);
119 +
        let exports = try symbols(entry.package.exports, base, output, &mut used);
120 +
        let template = try bytes(entry.package.template, output, &mut used);
121 +
        let fixups = try relocations(entry.package.relocations, output, &mut used);
122 +
        slice(output, at + field(&entry, &entry.source), base + source as u64, entry.source.len);
123 +
        slice(output, at + field(&entry, &entry.package.name), base + name as u64, entry.package.name.len);
124 +
        slice(output, at + field(&entry, &entry.package.dependencies), base + required as u64, entry.package.dependencies.len);
125 +
        slice(output, at + field(&entry, &entry.package.code), entry.package.codeAddress, entry.package.code.len);
126 +
        slice(output, at + field(&entry, &entry.package.exports), base + exports as u64, entry.package.exports.len);
127 +
        slice(output, at + field(&entry, &entry.package.template), base + template as u64, entry.package.template.len);
128 +
        slice(output, at + field(&entry, &entry.package.relocations), base + fixups as u64, entry.package.relocations.len);
129 +
    }
130 +
    return used;
131 +
}
lib/std/arch/rv64/shared/tests.rad +115 -8
1 1
//! Private template relocation and bounded linking checks.
2 2
3 3
use std::testing;
4 +
use std::mem;
5 +
use std::arch::rv64::shared::catalog;
6 +
use std::arch::rv64::asm;
4 7
use std::lang::alloc;
5 8
use std::lang::il;
6 9
use std::lang::il::binary;
7 10
use std::lang::gen::data;
8 11
18 21
unsafe static DATA: [data::DataSym; 8] = undefined;
19 22
/// Persistent initialized bytes.
20 23
static TEMPLATE: [u8; 64] = [0; 64];
21 24
/// Persistent private data relocations.
22 25
unsafe static RELOCS: [super::Relocation; 8] = undefined;
26 +
/// Aligned storage for a native boot catalog and its retained payloads.
27 +
static CATALOG: [u64; 512] = [0; 512];
28 +
29 +
/// Exported assembly boundaries occupy the same code extent as their package.
30 +
@test unsafe fn assemblyPrefix() throws (testing::TestError) {
31 +
    let input = binary::Package {
32 +
        symbols: &[], name: "p", dependencies: &[],
33 +
        exports: &[binary::Export { name: "p::entry", kind: binary::ExportKind::Function }],
34 +
        entry: "p::entry", program: il::Program { data: &[], fns: &[] },
35 +
    };
36 +
    let assembly = asm::Program {
37 +
        text: &[0x02a00513 as u32, 0x00008067], data: &[],
38 +
        symbols: &[asm::Symbol { name: "p::entry", section: asm::Section::Text, offset: 0, isExported: true }],
39 +
        externalFixups: &[],
40 +
    };
41 +
    let mut arena = alloc::new(&mut ARENA[..]);
42 +
    let mut scratch = alloc::new(&mut SCRATCH[..]);
43 +
    let package = try super::compileAssembly(super::AssemblyInput { package: &input, assembly }, 0, 0x180000000, &[],
44 +
        super::Storage {
45 +
            data: &mut DATA[..], symbols: &mut SYMBOLS[..], exports: &mut EXPORTS[..],
46 +
            template: &mut TEMPLATE[..], relocations: &mut RELOCS[..],
47 +
        }, &mut arena, &mut scratch) catch {
48 +
            throw testing::TestError::Failed;
49 +
        };
50 +
    let entry = package.entry else {
51 +
        throw testing::TestError::Failed;
52 +
    };
53 +
    try testing::expect(entry == 0x180000000 and package.code.len == 2);
54 +
    try testing::expect(package.code[0] == 0x02a00513 and package.exports[0].target == super::Target::Function(entry));
55 +
}
56 +
57 +
/// Packed catalog pointers resolve to retained metadata at the supplied native base.
58 +
@test unsafe fn nativeCatalog() throws (testing::TestError) {
59 +
    let package = try compile(imports(), 64) catch {
60 +
        throw testing::TestError::Failed;
61 +
    };
62 +
    let output = @sliceOf(&mut CATALOG[0] as *mut u8, @sizeOf([u64; 512]));
63 +
    let base = output.ptr as u64;
64 +
    let used = try catalog::pack(&[catalog::Entry { source: "trusted binary", package }], base, output)
65 +
        catch {
66 +
            throw testing::TestError::Failed;
67 +
        };
68 +
    let storage = &CATALOG[0] as *opaque;
69 +
    let entry = storage as *catalog::Entry;
70 +
    try testing::expect(used > @sizeOf(catalog::Entry) and used <= output.len);
71 +
    try testing::expect(mem::eq(entry.source, "trusted binary") and mem::eq(entry.package.name, "p"));
72 +
    try testing::expect(mem::eq(entry.package.dependencies[0], "dep"));
73 +
    try testing::expect(entry.package.code.ptr as u64 == package.codeAddress and entry.package.code.len == package.code.len);
74 +
    try testing::expect(entry.source.ptr as u64 >= base and entry.source.ptr as u64 < base + used as u64);
75 +
    set TEMPLATE[0] = 91;
76 +
    set EXPORTS[0].name = "changed";
77 +
    try testing::expect(entry.package.template[0] == 0 and mem::eq(entry.package.exports[0].name, "p::refs"));
78 +
    let mut state: [u8; 48] = [255; 48];
79 +
    try super::instantiate(&entry.package, &[0, 0x180002000, 0x180008000], &mut state[..])
80 +
        catch {
81 +
            throw testing::TestError::Failed;
82 +
        };
83 +
    try testing::expect(pointer(&state[..], 0) == 0x180004000 and pointer(&state[..], 16) == 0x180001234);
84 +
    let relocated = try catalog::pack(&[catalog::Entry { source: "trusted binary", package }], 0x180000000, output)
85 +
        catch {
86 +
            throw testing::TestError::Failed;
87 +
        };
88 +
    try testing::expect(entry.source.ptr as u64 >= 0x180000000 and entry.source.ptr as u64 < 0x180000000 + relocated as u64);
89 +
}
90 +
91 +
/// Catalog bounds and native alignment failures are recoverable.
92 +
@test unsafe fn nativeCatalogBounds() throws (testing::TestError) {
93 +
    let package = try compile(imports(), 64) catch {
94 +
        throw testing::TestError::Failed;
95 +
    };
96 +
    let entries = [catalog::Entry { source: "binary", package }];
97 +
    let output = @sliceOf(&mut CATALOG[0] as *mut u8, @sizeOf([u64; 512]));
98 +
    let mut failures: u32 = 0;
99 +
    try catalog::pack(&entries[..], 0x80000000, &mut output[..@sizeOf(catalog::Entry)]) catch err {
100 +
        try testing::expect(err == super::Error::Capacity); set failures += 1;
101 +
    };
102 +
    try catalog::pack(&entries[..], 0x80000001, output) catch err {
103 +
        try testing::expect(err == super::Error::Alignment); set failures += 1;
104 +
    };
105 +
    try catalog::pack(&entries[..], 0xfffffffffffffff8, output) catch err {
106 +
        try testing::expect(err == super::Error::Range); set failures += 1;
107 +
    };
108 +
    try testing::expect(failures == 3);
109 +
    let used = try catalog::pack(&entries[..], 0x80000000, output) catch {
110 +
        throw testing::TestError::Failed;
111 +
    };
112 +
    try testing::expect(used > @sizeOf(catalog::Entry));
113 +
}
23 114
24 115
/// Build a package with repeated dependency pointers and a high code pointer.
25 116
unsafe fn input() -> binary::Package {
26 117
    return binary::Package {
27 118
        symbols: &[], name: "p", dependencies: &["dep"],
66 157
}
67 158
68 159
/// Read a little-endian pointer without requiring buffer alignment.
69 160
fn pointer(bytes: &[u8], offset: u32) -> u64 {
70 161
    let mut value: u64 = 0;
71 -
    for i in 0..8 { set value |= bytes[offset + i] as u64 << (i as u64 * 8); }
162 +
    for i in 0..8 {
163 +
        set value |= bytes[offset + i] as u64 << (i as u64 * 8);
164 +
    }
72 165
    return value;
73 166
}
74 167
75 168
/// Check repeated pointers, full-width code addresses, padding, and zero-filled tails.
76 169
@test unsafe fn privateRelocations() throws (testing::TestError) {
77 -
    let package = try compile(imports(), 64) catch { throw testing::TestError::Failed; };
170 +
    let package = try compile(imports(), 64) catch {
171 +
        throw testing::TestError::Failed;
172 +
    };
78 173
    try testing::expect(package.template.len == 32 and package.memory == 48 and package.alignment == 16);
79 174
    try testing::expect(package.relocations.len == 1 and package.relocations[0].count == 2);
80 175
    try testing::expect(pointer(package.template, 16) == 0x180001234);
81 176
    let mut first: [u8; 48] = [255; 48];
82 177
    let mut second: [u8; 48] = [255; 48];
83 178
    try super::instantiate(&package, &[0, 0x180002000, 0x180008000], &mut first[..])
84 -
        catch { throw testing::TestError::Failed; };
179 +
        catch {
180 +
            throw testing::TestError::Failed;
181 +
        };
85 182
    try super::instantiate(&package, &[0, 0x280002000, 0x280008000], &mut second[..])
86 -
        catch { throw testing::TestError::Failed; };
183 +
        catch {
184 +
            throw testing::TestError::Failed;
185 +
        };
87 186
    try testing::expect(pointer(&first[..], 0) == 0x180004000 and pointer(&first[..], 8) == 0x180004000);
88 187
    try testing::expect(pointer(&second[..], 0) == 0x280004000 and pointer(&second[..], 8) == 0x280004000);
89 188
    try testing::expect(pointer(&first[..], 16) == pointer(&second[..], 16));
90 -
    for i in 24..48 { try testing::expect(first[i] == 0 and second[i] == 0); }
189 +
    for i in 24..48 {
190 +
        try testing::expect(first[i] == 0 and second[i] == 0);
191 +
    }
91 192
}
92 193
93 194
/// A missing instance, bad base, or short buffer leaves instance bytes untouched.
94 195
@test unsafe fn instanceFailures() throws (testing::TestError) {
95 -
    let package = try compile(imports(), 64) catch { throw testing::TestError::Failed; };
196 +
    let package = try compile(imports(), 64) catch {
197 +
        throw testing::TestError::Failed;
198 +
    };
96 199
    let mut bytes: [u8; 48] = [255; 48];
97 200
    let mut failed: u32 = 0;
98 201
    try super::instantiate(&package, &[0, 0, 0x8000], &mut bytes[..]) catch err {
99 202
        try testing::expect(err == super::Error::Instance); set failed += 1;
100 203
    };
106 209
    };
107 210
    try super::instantiate(&package, &[0, 0xffffffffffffffff, 0x8000], &mut bytes[..]) catch err {
108 211
        try testing::expect(err == super::Error::Range); set failed += 1;
109 212
    };
110 213
    try testing::expect(failed == 4);
111 -
    for byte in &bytes[..] { try testing::expect(byte == 255); }
214 +
    for byte in &bytes[..] {
215 +
        try testing::expect(byte == 255);
216 +
    }
112 217
}
113 218
114 219
/// Missing symbols, duplicate imports, kind mismatches, and short output fail explicitly.
115 220
@test unsafe fn linkFailures() throws (testing::TestError) {
116 221
    let mut failed: u32 = 0;
128 233
    };
129 234
    try compile(names, 31) catch err {
130 235
        try testing::expect(err == super::Error::Capacity); set failed += 1;
131 236
    };
132 237
    try testing::expect(failed == 4);
133 -
    let package = try compile(names, 64) catch { throw testing::TestError::Failed; };
238 +
    let package = try compile(names, 64) catch {
239 +
        throw testing::TestError::Failed;
240 +
    };
134 241
    try testing::expect(package.exports.len == 1);
135 242
}
lib/std/collections/dict.rad +2 -2
34 34
export fn insert(m: &mut Dict, key: *[u8], value: i32) {
35 35
    let mask = m.entries.len - 1;
36 36
    let mut idx = hash(key) & mask;
37 37
38 38
    loop {
39 -
        let entry = &m.entries[idx];
39 +
        let entry = m.entries[idx];
40 40
        if entry.key.len == 0 {
41 41
            assert m.count < m.entries.len / 2, "dict::insert: table full";
42 42
            set m.entries[idx] = Entry { key, value };
43 43
            set m.count += 1;
44 44
            return;
55 55
export fn get(m: &Dict, key: *[u8]) -> ?i32 {
56 56
    let mask = m.entries.len - 1;
57 57
    let mut idx = hash(key) & mask;
58 58
59 59
    loop {
60 -
        let entry = &m.entries[idx];
60 +
        let entry = m.entries[idx];
61 61
        if entry.key.len == 0 {
62 62
            return nil;
63 63
        }
64 64
        if mem::eq(entry.key, key) {
65 65
            return entry.value;
lib/std/lang/alloc.rad +54 -12
4 4
//! byte buffer. Memory is never freed individually - the entire arena is
5 5
//! reset at once. This is ideal for compiler passes where all allocations
6 6
//! have the same lifetime.
7 7
@test mod tests;
8 8
9 -
use std::mem;
10 -
11 9
/// Error thrown by allocator.
12 10
export union AllocError: Copy {
13 11
    /// Allocator is out of memory.
14 12
    OutOfMemory,
15 13
}
33 31
/// Allocate `size` bytes with the given alignment.
34 32
///
35 33
/// Returns an opaque pointer to the allocated memory. Throws `AllocError` if
36 34
/// the arena is exhausted. The caller is responsible for casting to the
37 35
/// appropriate type and initializing the memory.
38 -
export fn alloc(arena: &mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
36 +
/// Size must be positive. Alignment must be a positive power of two.
37 +
/// Failure leaves the allocation offset unchanged.
38 +
/// The caller must keep the storage live and must not reclaim live allocations.
39 +
export unsafe fn alloc(arena: *unsafe mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
39 40
    assert alignment > 0;
40 41
    assert size > 0;
41 42
42 -
    let aligned64 = (arena.offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1);
43 -
    if aligned64 > arena.data.len as u64 or size as u64 > arena.data.len as u64 - aligned64 {
43 +
    assert (alignment & (alignment - 1)) == 0;
44 +
45 +
    let capacity = arena.data.len;
46 +
    if arena.offset >= capacity {
47 +
        throw AllocError::OutOfMemory;
48 +
    }
49 +
    let address = (&arena.data[0]) as u64;
50 +
    let mask = (alignment - 1) as u64;
51 +
    let remainder = ((address & mask) + (arena.offset as u64 & mask)) & mask;
52 +
    let padding = ((alignment as u64 - remainder) & mask) as u32;
53 +
    let available = capacity - arena.offset;
54 +
    if padding > available or size > available - padding {
44 55
        throw AllocError::OutOfMemory;
45 56
    }
46 -
    let aligned = aligned64 as u32;
57 +
    let aligned = arena.offset + padding;
47 58
    let newOffset = aligned + size;
48 59
49 60
    let base: *mut u8 = &mut arena.data[aligned];
50 61
    set arena.offset = newOffset;
51 62
79 90
export fn remaining(arena: &Arena) -> u32 {
80 91
    return arena.data.len as u32 - arena.offset;
81 92
}
82 93
83 94
/// Returns the remaining buffer as a mutable slice.
84 -
export fn remainingBuf(arena: &mut Arena) -> *mut [u8] {
95 +
/// The caller must keep the storage live and must commit each written prefix
96 +
/// before another allocation can use that prefix.
97 +
export unsafe fn remainingBuf(arena: *unsafe mut Arena) -> *mut [u8] {
85 98
    return &mut arena.data[arena.offset..];
86 99
}
87 100
88 101
/// Commits `size` bytes of allocation, advancing the offset.
89 102
/// Use after writing to the buffer returned by [`remainingBuf`].
98 111
/// Throws `AllocError` if the arena is exhausted.
99 112
export unsafe fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
100 113
    if count == 0 {
101 114
        return &mut [];
102 115
    }
103 -
    if size > 0xffffffff / count { throw AllocError::OutOfMemory; }
104 -
    let ptr = try alloc(arena, size * count, alignment);
116 +
    let bytes = try allocationSize(size, count);
117 +
    let ptr = try alloc(&mut *arena, bytes, alignment);
105 118
106 119
    return @sliceOf(ptr, count);
107 120
}
108 121
109 122
/// Generic allocator interface.
130 143
}
131 144
132 145
/// Arena allocation function conforming to the `Allocator` interface.
133 146
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
134 147
    let arena = ctx as *unsafe mut Arena;
135 -
    return try! alloc(arena, size, alignment);
148 +
    return try! alloc(&mut *arena, size, alignment);
136 149
}
137 150
138 151
/// Allocate raw storage that remains valid until the arena is reset.
139 152
export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) {
140 -
    let owner = try alloc(arena, size, alignment);
153 +
    let owner = try alloc(&mut *arena, size, alignment);
141 154
    let byte = owner as *mut u8;
142 155
    let raw: *unsafe mut u8 = &mut *byte;
143 156
    return raw as *unsafe mut opaque;
144 157
}
145 158
146 159
/// Allocate a raw slice that remains valid until the arena is reset.
147 160
export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) {
148 161
    if count == 0 {
149 162
        return &mut [];
150 163
    }
151 -
    let raw = try allocRaw(arena, size * count, alignment);
164 +
    let bytes = try allocationSize(size, count);
165 +
    let raw = try allocRaw(arena, bytes, alignment);
152 166
    return @sliceOf(raw, count);
153 167
}
168 +
169 +
/// Raw storage provider for allocation sessions.
170 +
export trait Alloc {
171 +
    /// Reserve one uninitialized object.
172 +
    unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError);
173 +
    /// Reserve an uninitialized slice.
174 +
    unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError);
175 +
}
176 +
177 +
instance Alloc for Arena {
178 +
    /// Reserve one uninitialized object in the arena.
179 +
    unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) {
180 +
        return try allocRaw(arena, size, alignment);
181 +
    }
182 +
183 +
    /// Reserve an uninitialized slice in the arena.
184 +
    unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) {
185 +
        return try allocRawSlice(arena, size, alignment, count);
186 +
    }
187 +
}
188 +
189 +
/// Compute a slice allocation size without unsigned multiplication overflow.
190 +
fn allocationSize(size: u32, count: u32) -> u32 throws (AllocError) {
191 +
    if size <> 0 and count > 4294967295 / size {
192 +
        throw AllocError::OutOfMemory;
193 +
    }
194 +
    return size * count;
195 +
}
lib/std/lang/alloc/tests.rad +83 -14
1 1
//! Tests for the bump allocator.
2 2
3 3
use std::testing;
4 4
5 5
/// Test basic allocation.
6 -
@test fn testAllocBasic() throws (testing::TestError) {
6 +
@test unsafe fn testAllocBasic() throws (testing::TestError) {
7 7
    static STORAGE: [u8; 64] = [0; 64];
8 8
    let mut arena = super::new(&mut STORAGE[..]);
9 9
10 10
    let ptr = try! super::alloc(&mut arena, 4, 4);
11 11
    try testing::expect(super::used(&arena) == 4);
12 12
    try testing::expect(super::remaining(&arena) == 60);
13 13
}
14 14
15 15
/// Test that allocations are properly aligned.
16 -
@test fn testAllocAlignment() throws (testing::TestError) {
16 +
@test unsafe fn testAllocAlignment() throws (testing::TestError) {
17 17
    static STORAGE: [u8; 64] = [0; 64];
18 18
    let mut arena = super::new(&mut STORAGE[..]);
19 19
20 20
    // Allocate 1 byte with 1-byte alignment.
21 21
    let p1 = try! super::alloc(&mut arena, 1, 1);
25 25
    let p2 = try! super::alloc(&mut arena, 4, 4);
26 26
    try testing::expect(super::used(&arena) == 8); // 1 + 3 padding + 4
27 27
}
28 28
29 29
/// Test multiple allocations.
30 -
@test fn testAllocMultiple() throws (testing::TestError) {
30 +
@test unsafe fn testAllocMultiple() throws (testing::TestError) {
31 31
    static STORAGE: [u8; 128] = [0; 128];
32 32
    let mut arena = super::new(&mut STORAGE[..]);
33 33
34 34
    let p1 = try! super::alloc(&mut arena, 8, 4);
35 35
    let p2 = try! super::alloc(&mut arena, 16, 4);
37 37
38 38
    try testing::expect(super::used(&arena) == 28); // 8 + 16 + 4
39 39
}
40 40
41 41
/// Test that arena throws when exhausted.
42 -
@test fn testAllocExhausted() throws (testing::TestError) {
42 +
@test unsafe fn testAllocExhausted() throws (testing::TestError) {
43 43
    static STORAGE: [u8; 16] = [0; 16];
44 44
    let mut arena = super::new(&mut STORAGE[..]);
45 45
46 46
    // This should succeed.
47 47
    let p1 = try! super::alloc(&mut arena, 8, 4);
56 56
    };
57 57
    try testing::expect(failed);
58 58
}
59 59
60 60
/// Test that reset allows reuse of memory.
61 -
@test fn testAllocReset() throws (testing::TestError) {
61 +
@test unsafe fn testAllocReset() throws (testing::TestError) {
62 62
    static STORAGE: [u8; 32] = [0; 32];
63 63
    let mut arena = super::new(&mut STORAGE[..]);
64 64
65 65
    let p1 = try! super::alloc(&mut arena, 16, 4);
66 66
    try testing::expect(super::used(&arena) == 16);
72 72
    // Should be able to allocate again.
73 73
    let p2 = try! super::alloc(&mut arena, 32, 4);
74 74
}
75 75
76 76
/// Test alignment when offset is already aligned.
77 -
@test fn testAllocAlreadyAligned() throws (testing::TestError) {
77 +
@test unsafe fn testAllocAlreadyAligned() throws (testing::TestError) {
78 78
    static STORAGE: [u8; 64] = [0; 64];
79 79
    let mut arena = super::new(&mut STORAGE[..]);
80 80
81 81
    // Allocate 4 bytes - offset becomes 4, already aligned for next 4-byte alloc.
82 82
    let p1 = try! super::alloc(&mut arena, 4, 4);
86 86
    let p2 = try! super::alloc(&mut arena, 4, 4);
87 87
    try testing::expect(super::used(&arena) == 8);
88 88
}
89 89
90 90
/// Test allocation that would overflow with alignment padding.
91 -
@test fn testAllocOverflowWithPadding() throws (testing::TestError) {
91 +
@test unsafe fn testAllocOverflowWithPadding() throws (testing::TestError) {
92 92
    static STORAGE: [u8; 16] = [0; 16];
93 93
    let mut arena = super::new(&mut STORAGE[..]);
94 94
95 95
    // Allocate 1 byte, offset is now 1.
96 96
    let p1 = try! super::alloc(&mut arena, 1, 1);
110 110
    let mut arena = super::new(&mut STORAGE[..]);
111 111
    let a = super::arenaAllocator(&mut arena);
112 112
113 113
    // Allocate through the Allocator indirection.
114 114
    let p1 = a.func(a.ctx, 16, 4);
115 -
    try testing::expect(super::used(&arena) == 16);
115 +
    try testing::expect((p1 as u64 & 3) == 0);
116 116
117 117
    let p2 = a.func(a.ctx, 8, 8);
118 -
    try testing::expect(super::used(&arena) == 24);
118 +
    try testing::expect((p2 as u64 & 7) == 0);
119 +
    try testing::expect(super::used(&arena) as u64 == p2 as u64 - &STORAGE[0] as u64 + 8);
119 120
120 121
    // Verify the pointers are distinct.
121 122
    try testing::expect(p1 as u64 <> p2 as u64);
122 123
}
123 124
125 126
@test unsafe fn testAllocOverflow() throws (testing::TestError) {
126 127
    static bytes: [u8; 64] = [0; 64];
127 128
    let mut arena = super::new(&mut bytes[..]);
128 129
    set arena.offset = 8;
129 130
    let mut failed: u32 = 0;
130 -
    try super::allocSlice(&mut arena, 8, 8, 0x20000000) catch { set failed += 1; };
131 -
    try super::alloc(&mut arena, 0xffffffff, 8) catch { set failed += 1; };
131 +
    try super::allocSlice(&mut arena, 8, 8, 0x20000000) catch {
132 +
        set failed += 1;
133 +
    };
134 +
    try super::alloc(&mut arena, 0xffffffff, 8) catch {
135 +
        set failed += 1;
136 +
    };
132 137
    try testing::expect(failed == 2 and arena.offset == 8);
133 138
    set arena.offset = 0xfffffff8;
134 -
    try super::alloc(&mut arena, 16, 16) catch { set failed += 1; };
139 +
    try super::alloc(&mut arena, 16, 16) catch {
140 +
        set failed += 1;
141 +
    };
135 142
    try testing::expect(failed == 3 and arena.offset == 0xfffffff8);
136 143
    set arena.offset = 8;
137 -
    let storage = try super::alloc(&mut arena, 8, 8) catch { throw testing::TestError::Failed; };
138 -
    try testing::expect(arena.offset == 16);
144 +
    let storage = try super::alloc(&mut arena, 8, 8) catch {
145 +
        throw testing::TestError::Failed;
146 +
    };
147 +
    try testing::expect((storage as u64 & 7) == 0);
148 +
    try testing::expect(arena.offset as u64 == storage as u64 - &bytes[0] as u64 + 8);
149 +
}
150 +
151 +
/// Allocation alignment uses the backing address and preserves distinct objects.
152 +
@test unsafe fn testAllocUnalignedBacking() throws (testing::TestError) {
153 +
    static STORAGE: [u8; 96] = [0; 96];
154 +
    for start in 0..16 {
155 +
        let mut arena = super::new(&mut STORAGE[start..]);
156 +
        let first = try! super::allocRaw(&mut arena, 8, 16) as *unsafe mut u64;
157 +
        set *first = 123;
158 +
        let second = try! super::allocRaw(&mut arena, 8, 16) as *unsafe mut u64;
159 +
        set *second = 456;
160 +
        try testing::expect((first as u64 & 15) == 0);
161 +
        try testing::expect((second as u64 & 15) == 0);
162 +
        try testing::expect(second as u64 >= first as u64 + 8);
163 +
        try testing::expect(*first == 123);
164 +
        try testing::expect(*second == 456);
165 +
    }
166 +
}
167 +
168 +
/// Capacity failure and arithmetic overflow leave the arena and live data intact.
169 +
@test unsafe fn testAllocFailureAtomicity() throws (testing::TestError) {
170 +
    static STORAGE: [u8; 64] = [0; 64];
171 +
    let mut arena = super::new(&mut STORAGE[..]);
172 +
    let first = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8;
173 +
    set *first = 42;
174 +
    for size in [64 as u32, 4294967295 as u32] {
175 +
        let saved = super::used(&arena);
176 +
        let mut failed = false;
177 +
        try super::alloc(&mut arena, size, 1) catch {
178 +
            set failed = true;
179 +
        };
180 +
        try testing::expect(failed);
181 +
        try testing::expect(super::used(&arena) == saved);
182 +
        try testing::expect(*first == 42);
183 +
    }
184 +
    let next = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8;
185 +
    try testing::expect(next as u64 == first as u64 + 1);
186 +
}
187 +
188 +
/// Slice count multiplication fails before either allocation API changes state.
189 +
@test unsafe fn testAllocSliceOverflowAtomicity() throws (testing::TestError) {
190 +
    static STORAGE: [u8; 64] = [0; 64];
191 +
    let mut arena = super::new(&mut STORAGE[..]);
192 +
    let first = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8;
193 +
    set *first = 42;
194 +
    let saved = super::used(&arena);
195 +
    let mut failed = false;
196 +
    try super::allocSlice(&mut arena, 4, 4, 1073741825) catch {
197 +
        set failed = true;
198 +
    };
199 +
    try testing::expect(failed);
200 +
    try testing::expect(super::used(&arena) == saved);
201 +
    set failed = false;
202 +
    try super::allocRawSlice(&mut arena, 4, 4, 1073741825) catch {
203 +
        set failed = true;
204 +
    };
205 +
    try testing::expect(failed);
206 +
    try testing::expect(super::used(&arena) == saved);
207 +
    try testing::expect(*first == 42);
139 208
}
lib/std/lang/ast.rad +91 -11
2 2
export mod printer;
3 3
4 4
use std::io;
5 5
use std::fmt;
6 6
use std::lang::alloc;
7 -
use std::lang::types;
7 +
8 +
/// Ownership and safety qualifier written in a pointer type.
9 +
/// Named reference regions are separate `TypeSig::RegionRef` nodes.
10 +
export union PointerClass: Copy {
11 +
    /// Owned pointer introduced by `*`.
12 +
    Owned,
13 +
    /// Borrowed reference introduced by `&`.
14 +
    Ref,
15 +
    /// Raw pointer introduced by `*unsafe`.
16 +
    Unsafe,
17 +
}
8 18
9 19
/// Maximum number of trait methods.
10 20
export constant MAX_TRAIT_METHODS: u32 = 8;
11 21
12 22
/// Arena for all parser allocations.
180 190
        length: *Node,
181 191
    },
182 192
    /// Slice type, eg. `*[i32]`, `&[i32]`, or `*unsafe [i32]`.
183 193
    Slice {
184 194
        /// Ownership and safety class.
185 -
        class: types::PointerClass,
195 +
        class: PointerClass,
186 196
        /// Slice element type.
187 197
        itemType: *Node,
188 198
        /// Whether the slice is mutable.
189 199
        mutable: bool,
190 200
    },
191 201
    /// Pointer type, eg. `*i32`, `&i32`, or `*unsafe i32`.
192 202
    Pointer {
193 203
        /// Ownership and safety class.
194 -
        class: types::PointerClass,
204 +
        class: PointerClass,
195 205
        /// Pointer target type.
196 206
        valueType: *Node,
197 207
        /// Whether the pointer is mutable.
198 208
        mutable: bool,
199 209
    },
200 210
    /// Optional, eg. `?i32`.
201 211
    Optional {
202 212
        /// Underlying type.
203 213
        valueType: *Node,
204 214
    },
215 +
    /// Shared cell pointer with value access to its payload.
216 +
    Cell {
217 +
        /// Storage lifetime and ownership class.
218 +
        class: PointerClass,
219 +
        /// Copy payload type.
220 +
        payload: *Node,
221 +
    },
205 222
    /// Nominal type, points to identifier node.
206 223
    Nominal(*Node),
224 +
    /// Reference qualified by a lexical region.
225 +
    RegionRef {
226 +
        /// Region name node.
227 +
        region: *Node,
228 +
        /// Reference type node.
229 +
        type: *Node,
230 +
    },
231 +
    /// Nominal type with invariant region arguments.
232 +
    Applied {
233 +
        /// Nominal type path.
234 +
        name: *Node,
235 +
        /// Region arguments in declaration order.
236 +
        regions: *[*Node],
237 +
    },
207 238
    /// Inline record type for union variant payloads.
208 239
    Record {
209 240
        /// Field declaration nodes.
210 241
        fields: *[*Node],
211 242
        /// Whether this record has labeled fields.
220 251
    },
221 252
    /// Trait object type, eg. `*opaque Allocator`, `&opaque Allocator`, or
222 253
    /// `*unsafe opaque Allocator`.
223 254
    TraitObject {
224 255
        /// Ownership and safety class.
225 -
        class: types::PointerClass,
256 +
        class: PointerClass,
226 257
        /// Trait name identifier.
227 258
        traitName: *Node,
228 259
        /// Whether the pointer is mutable.
229 260
        mutable: bool,
230 261
    },
487 518
    name: *Node,
488 519
    /// Field declaration nodes.
489 520
    fields: *[*Node],
490 521
    /// Optional attribute list applied to the record.
491 522
    attrs: ?Attributes,
492 -
    /// Trait derivations attached to the record.
523 +
    /// Region parameters in declaration order.
524 +
    regions: *[*Node],
525 +
    /// Ownership trait derivations attached to the record.
493 526
    derives: *[*Node],
494 527
    /// Whether this record has labeled fields.
495 528
    labeled: bool,
496 529
}
497 530
501 534
    name: *Node,
502 535
    /// Variant nodes making up the union.
503 536
    variants: *[*Node],
504 537
    /// Optional attribute list applied to the union.
505 538
    attrs: ?Attributes,
506 -
    /// Trait derivations attached to the union.
539 +
    /// Region parameters in declaration order.
540 +
    regions: *[*Node],
541 +
    /// Ownership trait derivations attached to the union.
507 542
    derives: *[*Node],
508 543
}
509 544
510 545
/// Union variant declaration.
511 546
export record UnionDeclVariant: Copy {
521 556
522 557
/// Function declaration.
523 558
export record FnDecl: Copy {
524 559
    /// Identifier naming the function.
525 560
    name: *Node,
561 +
    /// Region parameters in declaration order.
562 +
    regions: *[*Node],
526 563
    /// Function type signature.
527 564
    sig: FnSig,
528 565
    /// Optional function body (`nil` for extern functions).
529 566
    body: ?*Node,
530 567
    /// Optional attribute list applied to the function.
531 568
    attrs: ?Attributes,
532 569
}
533 570
571 +
/// Method declaration modifiers.
572 +
export record MethodModifiers: Copy {
573 +
    /// Region parameters in declaration order.
574 +
    regions: *[*Node],
575 +
    /// Optional attribute list.
576 +
    attrs: ?Attributes,
577 +
}
578 +
534 579
/// Array repeat literal metadata.
535 580
export record ArrayRepeatLit: Copy {
536 581
    /// Expression providing the repeated value.
537 582
    item: *Node,
538 583
    /// Expression providing the repetition count.
613 658
    Char(u8),
614 659
    /// String literal like `"Hello World!"`.
615 660
    String(*[u8]),
616 661
    /// Identifier expression.
617 662
    Ident(*[u8]),
663 +
    /// Lexical region name, including its apostrophe prefix.
664 +
    Region {
665 +
        /// Source name.
666 +
        name: *[u8],
667 +
        /// Declared parent for a region parameter.
668 +
        parent: ?*Node,
669 +
    },
670 +
    /// Function or nominal constructor applied to explicit region arguments.
671 +
    RegionApply {
672 +
        /// Function or constructor expression.
673 +
        value: *Node,
674 +
        /// Region argument nodes in declaration order.
675 +
        regions: *[*Node],
676 +
    },
677 +
    /// Named source place for a lexical region binding.
678 +
    RegionBinding(Arg),
679 +
    /// Block with explicit storage bindings for a concrete region.
680 +
    RegionBlock {
681 +
        /// Region name introduced by this block.
682 +
        region: *Node,
683 +
        /// Binding nodes with address expressions as initializers.
684 +
        bindings: *[*Node],
685 +
        /// Statements within the region.
686 +
        body: *Node,
687 +
        /// Whether a `use` block supplies an allocation interface.
688 +
        isSession: bool,
689 +
    },
618 690
    /// Numeric literal such as `42` or `0xFF`.
619 691
    Number(fmt::IntLiteral),
620 692
    /// Range expression such as `0..10` or `..`.
621 693
    Range(Range),
622 694
    /// Array literal expression.
769 841
    },
770 842
    /// Method signature inside a trait declaration.
771 843
    TraitMethodSig {
772 844
        /// Method name identifier.
773 845
        name: *Node,
846 +
        /// Region parameters and attributes.
847 +
        modifiers: *MethodModifiers,
774 848
        /// Receiver type node (eg. `*mut Allocator`).
775 849
        receiver: *Node,
776 850
        /// Function signature.
777 851
        sig: FnSig,
778 -
        /// Optional declaration modifiers.
779 -
        attrs: ?Attributes,
780 852
    },
781 853
    /// Instance block.
782 854
    InstanceDecl {
783 855
        /// Trait name identifier.
784 856
        traitName: *Node,
785 857
        /// Target type identifier.
786 858
        targetType: *Node,
859 +
        /// Region parameters in declaration order.
860 +
        regions: *[*Node],
787 861
        /// Method definition nodes ([`MethodDecl`]).
788 862
        methods: *[*Node],
789 863
    },
790 864
    /// Method definition with a receiver.
791 865
    /// Used both inside `instance` blocks and as standalone methods.
792 866
    MethodDecl {
793 867
        /// Method name identifier.
794 868
        name: *Node,
869 +
        /// Region parameters and attributes.
870 +
        modifiers: *MethodModifiers,
795 871
        /// Receiver binding name ([`Ident`] node).
796 872
        receiverName: *Node,
797 873
        /// Receiver type node (eg. `*mut Arena`).
798 874
        receiverType: *Node,
799 875
        /// Function signature.
800 876
        sig: FnSig,
801 877
        /// Method body.
802 878
        body: *Node,
803 -
        /// Optional attribute list.
804 -
        attrs: ?Attributes,
805 879
    },
806 880
}
807 881
808 882
/// Full AST node with shared metadata and variant-specific payload.
809 883
export record Node: Copy {
864 938
    let params: *[*Node] = &[];
865 939
    let throwList: *[*Node] = &[];
866 940
    let fnSig = FnSig { params, returnType: nil, throwList };
867 941
    let fnBody: *Node = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false }));
868 942
    let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl {
869 -
        name: fnName, sig: fnSig, body: fnBody, attrs: nil,
943 +
        name: fnName, regions: &[], sig: fnSig, body: fnBody, attrs: nil,
870 944
    }));
871 945
    let mut rootStmts: *mut [*Node] = &mut [];
872 946
    rootStmts.append(fnDecl, a);
873 947
    let modBody = synthNode(arena, NodeValue::Block(Block { statements: rootStmts, isUnsafe: false }));
874 948
875 949
    return SynthFnMod { modBody, fnBody };
876 950
}
951 +
952 +
/// Describe the immutable binding introduced by a lexical region header.
953 +
export fn borrowBinding(binding: Arg) -> Let {
954 +
    let ident = binding.label else panic "borrowBinding: missing name";
955 +
    return Let { ident, type: nil, value: binding.value, alignment: nil, mutable: false };
956 +
}
lib/std/lang/ast/printer.rad +66 -10
1 1
//! AST pretty printer using S-expression syntax.
2 2
3 3
use std::io;
4 4
use std::lang::sexpr;
5 5
use std::lang::alloc;
6 -
use std::lang::types;
7 6
8 7
/// Return the symbol for a binary operator.
9 8
fn binOpName(op: super::BinaryOp) -> *[u8] {
10 9
    match op {
11 10
        case super::BinaryOp::Add => return "+",
69 68
    }
70 69
}
71 70
72 71
/// Return the S-expression head for a pointer class.
73 72
fn pointerClassHead(
74 -
    class: types::PointerClass,
73 +
    class: super::PointerClass,
75 74
    ownedHead: *[u8],
76 75
    refHead: *[u8],
77 76
    unsafeHead: *[u8],
78 77
) -> *[u8] {
79 78
    match class {
80 -
        case types::PointerClass::Owned => return ownedHead,
81 -
        case types::PointerClass::Ref => return refHead,
82 -
        case types::PointerClass::Unsafe => return unsafeHead,
79 +
        case super::PointerClass::Owned => return ownedHead,
80 +
        case super::PointerClass::Ref => return refHead,
81 +
        case super::PointerClass::Unsafe => return unsafeHead,
83 82
    }
84 83
}
85 84
86 85
/// Convert a type signature to an S-expression.
87 86
unsafe fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
103 102
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable
104 103
                else sexpr::list(a, head, &[toExpr(a, valueType)]);
105 104
        }
106 105
        case super::TypeSig::Optional { valueType } =>
107 106
            return sexpr::list(a, "?", &[toExpr(a, valueType)]),
107 +
        case super::TypeSig::Cell { class, payload } =>
108 +
            return sexpr::list(a, pointerClassHead(class, "cell-ptr", "cell-ref", "unsafe-cell-ptr"), &[toExpr(a, payload)]),
108 109
        case super::TypeSig::Nominal(name) => return toExpr(a, name),
110 +
        case super::TypeSig::RegionRef { region, type } =>
111 +
            return sexpr::list(a, "region-ref", &[toExpr(a, region), toExpr(a, type)]),
112 +
        case super::TypeSig::Applied { name, regions } =>
113 +
            return sexpr::list(a, "apply", &[toExpr(a, name), sexpr::list(a, "regions", nodeListToExprs(a, regions))]),
109 114
        case super::TypeSig::Record { fields, .. } =>
110 115
            return sexpr::list(a, "record", nodeListToExprs(a, fields)),
111 116
        case super::TypeSig::Fn { sig, isUnsafe } => {
112 117
            let mut ret = sexpr::sym("void");
113 118
            if let rt = sig.returnType {
448 453
            return sexpr::block(a, "match", &[toExpr(a, m.subject)], children);
449 454
        }
450 455
        case super::NodeValue::MatchProng(p) => {
451 456
            return prongToExpr(a, p);
452 457
        }
458 +
        case super::NodeValue::RegionApply { value, regions } =>
459 +
            return sexpr::list(a, "apply", &[toExpr(a, value), sexpr::list(a, "regions", nodeListToExprs(a, regions))]),
460 +
        case super::NodeValue::RegionBinding(b) =>
461 +
            return sexpr::list(a, "binding", &[toExprOrNull(a, b.label), toExpr(a, b.value)]),
462 +
        case super::NodeValue::Region { name, parent } => {
463 +
            if let p = parent {
464 +
                return sexpr::list(a, "region", &[sexpr::sym(name), toExpr(a, p)]);
465 +
            }
466 +
            return sexpr::sym(name);
467 +
        }
468 +
        case super::NodeValue::RegionBlock { region, bindings, body, isSession } => {
469 +
            let head = "use-region" if isSession else "let-region";
470 +
            return sexpr::block(a, head, &[toExpr(a, region), sexpr::list(a, "bindings", nodeListToExprs(a, bindings))], &[toExpr(a, body)]);
471 +
        }
453 472
        case super::NodeValue::FnDecl(f) => {
454 473
            let params = sexpr::list(a, "params", nodeListToExprs(a, f.sig.params));
455 474
            let ret = toExprOrNull(a, f.sig.returnType);
475 +
            if f.regions.len > 0 {
476 +
                let regions = sexpr::list(a, "regions", nodeListToExprs(a, f.regions));
477 +
                if let body = f.body {
478 +
                    return sexpr::block(a, "fn", &[toExpr(a, f.name), regions, params, ret], &[toExpr(a, body)]);
479 +
                }
480 +
                return sexpr::list(a, "fn", &[toExpr(a, f.name), regions, params, ret]);
481 +
            }
456 482
            if let body = f.body {
457 483
                return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]);
458 484
            }
459 485
            return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]);
460 486
        }
461 487
        case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]),
462 488
        case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]),
463 489
        case super::NodeValue::RecordDecl(r) => {
464 490
            let children = fieldListToExprs(a, r.fields);
491 +
            if r.regions.len > 0 or r.derives.len > 0 {
492 +
                return sexpr::block(a, "record", &[
493 +
                    toExpr(a, r.name),
494 +
                    sexpr::list(a, "regions", nodeListToExprs(a, r.regions)),
495 +
                    sexpr::list(a, "derives", nodeListToExprs(a, r.derives)),
496 +
                ], children);
497 +
            }
465 498
            return sexpr::block(a, "record", &[toExpr(a, r.name)], children);
466 499
        }
467 500
        case super::NodeValue::RecordField { field, type, value } => {
468 501
            return fieldToExpr(a, field, type, value);
469 502
        }
470 503
        case super::NodeValue::UnionDecl(u_) => {
471 504
            let children = variantListToExprs(a, u_.variants);
505 +
            if u_.regions.len > 0 or u_.derives.len > 0 {
506 +
                return sexpr::block(a, "union", &[
507 +
                    toExpr(a, u_.name),
508 +
                    sexpr::list(a, "regions", nodeListToExprs(a, u_.regions)),
509 +
                    sexpr::list(a, "derives", nodeListToExprs(a, u_.derives)),
510 +
                ], children);
511 +
            }
472 512
            return sexpr::block(a, "union", &[toExpr(a, u_.name)], children);
473 513
        }
474 514
        case super::NodeValue::UnionDeclVariant(v) => {
475 515
            return variantToExpr(a, v.name, v.type);
476 516
        }
478 518
        case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => {
479 519
            let children = nodeListToExprs(a, methods);
480 520
            let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, supertraits));
481 521
            return sexpr::block(a, "trait", &[toExpr(a, name), supers], children);
482 522
        }
483 -
        case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => {
523 +
        case super::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } => {
524 +
            let regions = modifiers.regions;
525 +
            let attrs = modifiers.attrs;
484 526
            let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params));
485 527
            let ret = toExprOrNull(a, sig.returnType);
486 528
            let attributes = attributesToExpr(a, attrs);
487 529
            return sexpr::list(
488 530
                a,
489 531
                "methodSig",
490 -
                &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret],
532 +
                &[
533 +
                    attributes,
534 +
                    toExpr(a, receiver),
535 +
                    toExpr(a, name),
536 +
                    sexpr::list(a, "regions", nodeListToExprs(a, regions)),
537 +
                    params,
538 +
                    ret,
539 +
                ],
491 540
            );
492 541
        }
493 -
        case super::NodeValue::InstanceDecl { traitName, targetType, methods } => {
542 +
        case super::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => {
494 543
            let children = nodeListToExprs(a, methods);
495 -
            return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children);
544 +
            return sexpr::block(a, "instance", &[
545 +
                toExpr(a, traitName),
546 +
                toExpr(a, targetType),
547 +
                sexpr::list(a, "regions", nodeListToExprs(a, regions)),
548 +
            ], children);
496 549
        }
497 550
        case super::NodeValue::MethodDecl {
498 -
            name, receiverName, receiverType, sig, body, attrs,
551 +
            name, modifiers, receiverName, receiverType, sig, body,
499 552
        } => {
553 +
            let regions = modifiers.regions;
554 +
            let attrs = modifiers.attrs;
500 555
            let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params));
501 556
            let ret = toExprOrNull(a, sig.returnType);
502 557
            let attributes = attributesToExpr(a, attrs);
503 558
            return sexpr::block(
504 559
                a,
506 561
                &[
507 562
                    attributes,
508 563
                    toExpr(a, receiverType),
509 564
                    toExpr(a, receiverName),
510 565
                    toExpr(a, name),
566 +
                    sexpr::list(a, "regions", nodeListToExprs(a, regions)),
511 567
                    params,
512 568
                    ret,
513 569
                ],
514 570
                &[toExpr(a, body)],
515 571
            );
lib/std/lang/gen/bitset.rad +48 -93
14 14
    return b;
15 15
}
16 16
17 17
/// Calculate the number of 32-bit words needed to store `n` bits.
18 18
export fn wordsFor(n: u32) -> u32 {
19 -
    return (n + 31) / 32;
19 +
    return n / 32 + (1 if n % 32 > 0 else 0);
20 20
}
21 21
22 -
/// A fixed-size bitset backed by an array of 32-bit words.
23 -
export record Bitset: Copy {
24 -
    /// Backing words. The storage must outlive the bitset and its iterators.
25 -
    bits: *unsafe mut [u32],
26 -
    /// Number of bits this bitset can hold.
27 -
    len: u32,
28 -
}
29 -
30 -
/// Create a new bitset backed by the given zero-initialized storage.
31 -
/// The storage must outlive the bitset and its iterators.
32 -
export unsafe fn new(bits: &mut [u32]) -> Bitset {
33 -
    let len = bits.len * 32;
34 -
    return Bitset { bits: bits as *unsafe mut [u32], len };
35 -
}
36 -
37 -
/// Create a new bitset backed by the given storage, zeroing it first.
38 -
/// The storage must outlive the bitset and its iterators.
39 -
export unsafe fn init(bits: &mut [u32]) -> Bitset {
40 -
    for i in 0..bits.len {
41 -
        set bits[i] = 0;
42 -
    }
43 -
    return new(bits);
44 -
}
45 -
46 -
/// Create a bitset from arena allocation.
47 -
export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) {
48 -
    let numWords = wordsFor(len);
49 -
    let bits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *unsafe mut [u32];
50 -
51 -
    return init(bits);
22 +
/// Allocate zeroed words for at least `len` bits in a session.
23 +
export fn allocate 'bits (storage: &Session 'bits, len: u32) -> &'bits mut [u32] throws (alloc::AllocError) {
24 +
    return try storage.fill(0 as u32, wordsFor(len));
52 25
}
53 26
54 27
/// Set bit `n` in the bitset.
55 -
export unsafe fn put(bs: &mut Bitset, n: u32) {
56 -
    if n >= bs.len {
28 +
export fn put(bs: &mut [u32], n: u32) {
29 +
    let word = n / 32;
30 +
    if word >= bs.len {
57 31
        return;
58 32
    }
59 -
    let word = n / 32;
60 33
    let b = n % 32;
61 34
62 -
    set bs.bits[word] |= (1 << b);
35 +
    set bs[word] |= (1 << b);
63 36
}
64 37
65 38
/// Clear bit `n` in the bitset.
66 -
export unsafe fn clear(bs: &mut Bitset, n: u32) {
67 -
    if n >= bs.len {
39 +
export fn clear(bs: &mut [u32], n: u32) {
40 +
    let word = n / 32;
41 +
    if word >= bs.len {
68 42
        return;
69 43
    }
70 -
    let word = n / 32;
71 44
    let b = n % 32;
72 45
73 -
    set bs.bits[word] &= ~(1 << b);
46 +
    set bs[word] &= ~(1 << b);
74 47
}
75 48
76 49
/// Check if bit `n` is set.
77 -
export unsafe fn contains(bs: &Bitset, n: u32) -> bool {
78 -
    if n >= bs.len {
50 +
export fn contains(bs: &[u32], n: u32) -> bool {
51 +
    let word = n / 32;
52 +
    if word >= bs.len {
79 53
        return false;
80 54
    }
81 -
    let word = n / 32;
82 55
    let b = n % 32;
83 56
84 -
    return (bs.bits[word] & (1 << b)) <> 0;
57 +
    return (bs[word] & (1 << b)) <> 0;
85 58
}
86 59
87 60
/// Count the number of set bits.
88 -
export unsafe fn count(bs: &Bitset) -> u32 {
61 +
export fn count(bs: &[u32]) -> u32 {
89 62
    let mut total: u32 = 0;
90 -
    let numWords = bs.bits.len;
91 -
    for i in 0..numWords {
92 -
        let word = bs.bits[i];
63 +
    for word in bs {
93 64
        if word <> 0 {
94 65
            set total += popCount(word);
95 66
        }
96 67
    }
97 68
    return total;
108 79
109 80
    return n & 0x3F;
110 81
}
111 82
112 83
/// Union: `dst = dst | src`.
113 -
export unsafe fn union_(dst: &mut Bitset, src: &Bitset) {
114 -
    let numWords = dst.bits.len;
115 -
    let srcWords = src.bits.len;
116 -
    let minWords = min(numWords, srcWords);
117 -
    for i in 0..minWords {
118 -
        set dst.bits[i] |= src.bits[i];
84 +
export fn union_(dst: &mut [u32], src: &[u32]) {
85 +
    for i in 0..min(dst.len, src.len) {
86 +
        set dst[i] |= src[i];
119 87
    }
120 88
}
121 89
122 90
/// Subtract: `dst = dst - src`.
123 -
export unsafe fn subtract(dst: &mut Bitset, src: &Bitset) {
124 -
    let numWords = dst.bits.len;
125 -
    let srcWords = src.bits.len;
126 -
    let minWords = min(numWords, srcWords);
127 -
    for i in 0..minWords {
128 -
        set dst.bits[i] &= ~src.bits[i];
91 +
export fn subtract(dst: &mut [u32], src: &[u32]) {
92 +
    for i in 0..min(dst.len, src.len) {
93 +
        set dst[i] &= ~src[i];
129 94
    }
130 95
}
131 96
132 97
/// Check if two bitsets are equal.
133 -
export unsafe fn eq(a: &Bitset, b: &Bitset) -> bool {
134 -
    let numWordsA = a.bits.len;
135 -
    let numWordsB = b.bits.len;
136 -
    let minWords = min(numWordsA, numWordsB);
98 +
export fn eq(a: &[u32], b: &[u32]) -> bool {
99 +
    let minWords = min(a.len, b.len);
137 100
138 101
    for i in 0..minWords {
139 -
        if a.bits[i] <> b.bits[i] {
102 +
        if a[i] <> b[i] {
140 103
            return false;
141 104
        }
142 105
    }
143 -
    for i in minWords..numWordsA {
144 -
        if a.bits[i] <> 0 {
106 +
    for i in minWords..a.len {
107 +
        if a[i] <> 0 {
145 108
            return false;
146 109
        }
147 110
    }
148 -
    for i in minWords..numWordsB {
149 -
        if b.bits[i] <> 0 {
111 +
    for i in minWords..b.len {
112 +
        if b[i] <> 0 {
150 113
            return false;
151 114
        }
152 115
    }
153 116
    return true;
154 117
}
155 118
156 119
/// Copy bits from source to destination.
157 -
export unsafe fn copy(dst: &mut Bitset, src: &Bitset) {
158 -
    let numWords = dst.bits.len;
159 -
    let srcWords = src.bits.len;
160 -
    let minWords = min(numWords, srcWords);
120 +
export fn copy(dst: &mut [u32], src: &[u32]) {
121 +
    let minWords = min(dst.len, src.len);
161 122
162 123
    for i in 0..minWords {
163 -
        set dst.bits[i] = src.bits[i];
124 +
        set dst[i] = src[i];
164 125
    }
165 126
    // Clear remaining words if destination is larger.
166 -
    for i in minWords..numWords {
167 -
        set dst.bits[i] = 0;
127 +
    for i in minWords..dst.len {
128 +
        set dst[i] = 0;
168 129
    }
169 130
}
170 131
171 132
/// Clear all bits.
172 -
export unsafe fn clearAll(bs: &mut Bitset) {
173 -
    let numWords = bs.bits.len;
174 -
    for i in 0..numWords {
175 -
        set bs.bits[i] = 0;
133 +
export fn clearAll(bs: &mut [u32]) {
134 +
    for i in 0..bs.len {
135 +
        set bs[i] = 0;
176 136
    }
177 137
}
178 138
179 139
/// Iterator state for iterating set bits.
180 140
export record BitIter: Copy {
181 -
    /// Bitset being iterated. It must outlive this iterator.
182 -
    bs: *unsafe Bitset,
183 141
    /// Current word index.
184 142
    wordIdx: u32,
185 143
    /// Remaining bits in the current word (visited bits cleared).
186 144
    remaining: u32,
187 145
}
188 146
189 147
/// Create an iterator over set bits.
190 -
/// The bitset and its backing words must outlive the iterator.
191 -
export unsafe fn iter(bs: &Bitset) -> BitIter {
192 -
    let remaining = bs.bits[0] if bs.len > 0 else 0;
193 -
    return BitIter { bs: bs as *unsafe Bitset, wordIdx: 0, remaining };
148 +
/// The cursor retains the first word mask and its position.
149 +
export fn iter(bs: &[u32]) -> BitIter {
150 +
    let remaining = bs[0] if bs.len > 0 else 0;
151 +
    return BitIter { wordIdx: 0, remaining };
194 152
}
195 153
196 -
/// Get the next set bit, or nil if none remain.
197 -
export unsafe fn iterNext(it: &mut BitIter) -> ?u32 {
198 -
    let numWords = it.bs.bits.len;
154 +
/// Get the next set bit from the supplied bitset, or nil if none remain.
155 +
/// The current word mask is a value snapshot; later words are read on demand.
156 +
export fn iterNext(it: &mut BitIter, bs: &[u32]) -> ?u32 {
199 157
    // Skip to next non-zero word.
200 158
    while it.remaining == 0 {
201 159
        set it.wordIdx += 1;
202 -
        if it.wordIdx >= numWords {
160 +
        if it.wordIdx >= bs.len {
203 161
            return nil;
204 162
        }
205 -
        set it.remaining = it.bs.bits[it.wordIdx];
163 +
        set it.remaining = bs[it.wordIdx];
206 164
    }
207 165
    // Find lowest set bit position using de Bruijn sequence.
208 166
    let b = ctz(it.remaining);
209 167
    let n = it.wordIdx * 32 + b;
210 -
    if n >= it.bs.len {
211 -
        return nil;
212 -
    }
213 168
    // Clear the lowest set bit.
214 169
    set it.remaining &= it.remaining - 1;
215 170
216 171
    return n;
217 172
}
lib/std/lang/gen/bitset/tests.rad +132 -112
1 1
//! Tests for the bitset module.
2 2
3 3
use std::testing;
4 +
use std::lang::alloc;
4 5
5 -
/// Test [`super::init`] and basic set/contains operations.
6 +
7 +
/// Test word initialization and basic set/contains operations.
6 8
@test unsafe fn testInit() throws (testing::TestError) {
7 9
    let mut bits: [u32; 4] = undefined;
8 -
    let mut bs = super::init(&mut bits[..]);
10 +
    let bs = &mut bits[..];
11 +
    super::clearAll(bs);
9 12
10 -
    // `init` should zero-initialize, so all bits start unset.
11 -
    try testing::expect(not super::contains(&bs, 0));
12 -
    try testing::expect(not super::contains(&bs, 31));
13 -
    try testing::expect(not super::contains(&bs, 32));
14 -
    try testing::expect(not super::contains(&bs, 127));
13 +
    // All bits start unset after clearing the words.
14 +
    try testing::expect(not super::contains(bs, 0));
15 +
    try testing::expect(not super::contains(bs, 31));
16 +
    try testing::expect(not super::contains(bs, 32));
17 +
    try testing::expect(not super::contains(bs, 127));
15 18
16 19
    // Set various bits including word boundaries.
17 -
    super::put(&mut bs, 0);
18 -
    super::put(&mut bs, 31);
19 -
    super::put(&mut bs, 32);
20 -
    super::put(&mut bs, 127);
20 +
    super::put(bs, 0);
21 +
    super::put(bs, 31);
22 +
    super::put(bs, 32);
23 +
    super::put(bs, 127);
21 24
22 -
    try testing::expect(super::contains(&bs, 0));
23 -
    try testing::expect(super::contains(&bs, 31));
24 -
    try testing::expect(super::contains(&bs, 32));
25 -
    try testing::expect(super::contains(&bs, 127));
25 +
    try testing::expect(super::contains(bs, 0));
26 +
    try testing::expect(super::contains(bs, 31));
27 +
    try testing::expect(super::contains(bs, 32));
28 +
    try testing::expect(super::contains(bs, 127));
26 29
27 30
    // Other bits still unset.
28 -
    try testing::expect(not super::contains(&bs, 1));
29 -
    try testing::expect(not super::contains(&bs, 30));
30 -
    try testing::expect(not super::contains(&bs, 33));
31 -
    try testing::expect(not super::contains(&bs, 126));
31 +
    try testing::expect(not super::contains(bs, 1));
32 +
    try testing::expect(not super::contains(bs, 30));
33 +
    try testing::expect(not super::contains(bs, 33));
34 +
    try testing::expect(not super::contains(bs, 126));
32 35
}
33 36
34 37
/// Test clear operation.
35 38
@test unsafe fn testClear() throws (testing::TestError) {
36 39
    let mut bits: [u32; 2] = [0; 2];
37 -
    let mut bs = super::new(&mut bits[..]);
40 +
    let bs = &mut bits[..];
38 41
39 -
    super::put(&mut bs, 0);
40 -
    super::put(&mut bs, 31);
41 -
    super::put(&mut bs, 32);
42 +
    super::put(bs, 0);
43 +
    super::put(bs, 31);
44 +
    super::put(bs, 32);
42 45
43 -
    try testing::expect(super::contains(&bs, 0));
44 -
    try testing::expect(super::contains(&bs, 31));
45 -
    try testing::expect(super::contains(&bs, 32));
46 +
    try testing::expect(super::contains(bs, 0));
47 +
    try testing::expect(super::contains(bs, 31));
48 +
    try testing::expect(super::contains(bs, 32));
46 49
47 -
    super::clear(&mut bs, 31);
50 +
    super::clear(bs, 31);
48 51
49 -
    try testing::expect(super::contains(&bs, 0));
50 -
    try testing::expect(not super::contains(&bs, 31));
51 -
    try testing::expect(super::contains(&bs, 32));
52 +
    try testing::expect(super::contains(bs, 0));
53 +
    try testing::expect(not super::contains(bs, 31));
54 +
    try testing::expect(super::contains(bs, 32));
52 55
}
53 56
54 57
/// Test population count.
55 58
@test unsafe fn testCount() throws (testing::TestError) {
56 59
    let mut bits: [u32; 2] = [0; 2];
57 -
    let mut bs = super::new(&mut bits[..]);
60 +
    let bs = &mut bits[..];
58 61
59 -
    try testing::expect(super::count(&bs) == 0);
62 +
    try testing::expect(super::count(bs) == 0);
60 63
61 -
    super::put(&mut bs, 0);
62 -
    try testing::expect(super::count(&bs) == 1);
64 +
    super::put(bs, 0);
65 +
    try testing::expect(super::count(bs) == 1);
63 66
64 -
    super::put(&mut bs, 31);
65 -
    super::put(&mut bs, 32);
66 -
    super::put(&mut bs, 63);
67 -
    try testing::expect(super::count(&bs) == 4);
67 +
    super::put(bs, 31);
68 +
    super::put(bs, 32);
69 +
    super::put(bs, 63);
70 +
    try testing::expect(super::count(bs) == 4);
68 71
69 -
    super::clear(&mut bs, 31);
70 -
    try testing::expect(super::count(&bs) == 3);
72 +
    super::clear(bs, 31);
73 +
    try testing::expect(super::count(bs) == 3);
71 74
}
72 75
73 76
/// Test union operation.
74 77
@test unsafe fn testUnion() throws (testing::TestError) {
75 78
    let mut bits_a: [u32; 2] = [0; 2];
76 79
    let mut bits_b: [u32; 2] = [0; 2];
77 -
    let mut a = super::new(&mut bits_a[..]);
78 -
    let mut b = super::new(&mut bits_b[..]);
80 +
    let a = &mut bits_a[..];
81 +
    let b = &mut bits_b[..];
79 82
80 -
    super::put(&mut a, 0);
81 -
    super::put(&mut a, 10);
83 +
    super::put(a, 0);
84 +
    super::put(a, 10);
82 85
83 -
    super::put(&mut b, 10);
84 -
    super::put(&mut b, 20);
86 +
    super::put(b, 10);
87 +
    super::put(b, 20);
85 88
86 -
    super::union_(&mut a, &b);
89 +
    super::union_(a, b);
87 90
88 -
    try testing::expect(super::contains(&a, 0));
89 -
    try testing::expect(super::contains(&a, 10));
90 -
    try testing::expect(super::contains(&a, 20));
91 -
    try testing::expect(super::count(&a) == 3);
91 +
    try testing::expect(super::contains(a, 0));
92 +
    try testing::expect(super::contains(a, 10));
93 +
    try testing::expect(super::contains(a, 20));
94 +
    try testing::expect(super::count(a) == 3);
92 95
}
93 96
94 97
/// Test subtract operation.
95 98
@test unsafe fn testSubtract() throws (testing::TestError) {
96 99
    let mut bits_a: [u32; 2] = [0; 2];
97 100
    let mut bits_b: [u32; 2] = [0; 2];
98 -
    let mut a = super::new(&mut bits_a[..]);
99 -
    let mut b = super::new(&mut bits_b[..]);
101 +
    let a = &mut bits_a[..];
102 +
    let b = &mut bits_b[..];
100 103
101 -
    super::put(&mut a, 0);
102 -
    super::put(&mut a, 10);
103 -
    super::put(&mut a, 20);
104 +
    super::put(a, 0);
105 +
    super::put(a, 10);
106 +
    super::put(a, 20);
104 107
105 -
    super::put(&mut b, 10);
106 -
    super::put(&mut b, 30);
108 +
    super::put(b, 10);
109 +
    super::put(b, 30);
107 110
108 -
    super::subtract(&mut a, &b);
111 +
    super::subtract(a, b);
109 112
110 -
    try testing::expect(super::contains(&a, 0));
111 -
    try testing::expect(not super::contains(&a, 10));
112 -
    try testing::expect(super::contains(&a, 20));
113 -
    try testing::expect(super::count(&a) == 2);
113 +
    try testing::expect(super::contains(a, 0));
114 +
    try testing::expect(not super::contains(a, 10));
115 +
    try testing::expect(super::contains(a, 20));
116 +
    try testing::expect(super::count(a) == 2);
114 117
}
115 118
116 119
/// Test equality check.
117 120
@test unsafe fn testEq() throws (testing::TestError) {
118 121
    let mut bits_a: [u32; 2] = [0; 2];
119 122
    let mut bits_b: [u32; 2] = [0; 2];
120 -
    let mut a = super::new(&mut bits_a[..]);
121 -
    let mut b = super::new(&mut bits_b[..]);
123 +
    let a = &mut bits_a[..];
124 +
    let b = &mut bits_b[..];
122 125
123 126
    // Both empty.
124 -
    try testing::expect(super::eq(&a, &b));
127 +
    try testing::expect(super::eq(a, b));
125 128
126 -
    super::put(&mut a, 5);
127 -
    try testing::expect(not super::eq(&a, &b));
129 +
    super::put(a, 5);
130 +
    try testing::expect(not super::eq(a, b));
128 131
129 -
    super::put(&mut b, 5);
130 -
    try testing::expect(super::eq(&a, &b));
132 +
    super::put(b, 5);
133 +
    try testing::expect(super::eq(a, b));
131 134
132 -
    super::put(&mut a, 32);
133 -
    super::put(&mut b, 32);
134 -
    try testing::expect(super::eq(&a, &b));
135 +
    super::put(a, 32);
136 +
    super::put(b, 32);
137 +
    try testing::expect(super::eq(a, b));
135 138
136 -
    super::put(&mut b, 33);
137 -
    try testing::expect(not super::eq(&a, &b));
139 +
    super::put(b, 33);
140 +
    try testing::expect(not super::eq(a, b));
138 141
}
139 142
140 143
/// Test copy operation.
141 144
@test unsafe fn testCopy() throws (testing::TestError) {
142 145
    let mut bits_a: [u32; 2] = [0; 2];
143 146
    let mut bits_b: [u32; 2] = [0; 2];
144 -
    let mut a = super::new(&mut bits_a[..]);
145 -
    let mut b = super::new(&mut bits_b[..]);
147 +
    let a = &mut bits_a[..];
148 +
    let b = &mut bits_b[..];
146 149
147 -
    super::put(&mut a, 0);
148 -
    super::put(&mut a, 31);
149 -
    super::put(&mut a, 63);
150 +
    super::put(a, 0);
151 +
    super::put(a, 31);
152 +
    super::put(a, 63);
150 153
151 -
    super::copy(&mut b, &a);
154 +
    super::copy(b, a);
152 155
153 -
    try testing::expect(super::eq(&a, &b));
154 -
    try testing::expect(super::contains(&b, 0));
155 -
    try testing::expect(super::contains(&b, 31));
156 -
    try testing::expect(super::contains(&b, 63));
156 +
    try testing::expect(super::eq(a, b));
157 +
    try testing::expect(super::contains(b, 0));
158 +
    try testing::expect(super::contains(b, 31));
159 +
    try testing::expect(super::contains(b, 63));
157 160
}
158 161
159 162
/// Test clearAll operation.
160 163
@test unsafe fn testClearAll() throws (testing::TestError) {
161 164
    let mut bits: [u32; 2] = [0; 2];
162 -
    let mut bs = super::new(&mut bits[..]);
163 -
164 -
    super::put(&mut bs, 0);
165 -
    super::put(&mut bs, 31);
166 -
    super::put(&mut bs, 32);
167 -
    super::put(&mut bs, 63);
168 -
    try testing::expect(super::count(&bs) == 4);
169 -
170 -
    super::clearAll(&mut bs);
171 -
    try testing::expect(super::count(&bs) == 0);
172 -
    try testing::expect(not super::contains(&bs, 0));
173 -
    try testing::expect(not super::contains(&bs, 31));
165 +
    let bs = &mut bits[..];
166 +
167 +
    super::put(bs, 0);
168 +
    super::put(bs, 31);
169 +
    super::put(bs, 32);
170 +
    super::put(bs, 63);
171 +
    try testing::expect(super::count(bs) == 4);
172 +
173 +
    super::clearAll(bs);
174 +
    try testing::expect(super::count(bs) == 0);
175 +
    try testing::expect(not super::contains(bs, 0));
176 +
    try testing::expect(not super::contains(bs, 31));
174 177
}
175 178
176 179
/// Test iteration over set bits.
177 180
@test unsafe fn testIter() throws (testing::TestError) {
178 181
    let mut bits: [u32; 2] = [0; 2];
179 -
    let mut bs = super::new(&mut bits[..]);
182 +
    let bs = &mut bits[..];
180 183
181 -
    super::put(&mut bs, 3);
182 -
    super::put(&mut bs, 31);
183 -
    super::put(&mut bs, 32);
184 -
    super::put(&mut bs, 50);
184 +
    super::put(bs, 3);
185 +
    super::put(bs, 31);
186 +
    super::put(bs, 32);
187 +
    super::put(bs, 50);
185 188
186 -
    let mut it = super::iter(&bs);
189 +
    let mut it = super::iter(bs);
187 190
    let mut count: u32 = 0;
188 191
    let mut sum: u32 = 0;
189 192
190 -
    while let n = super::iterNext(&mut it) {
193 +
    while let n = super::iterNext(&mut it, bs) {
191 194
        set count += 1;
192 195
        set sum += n;
193 196
    }
194 197
195 198
    try testing::expect(count == 4);
197 200
}
198 201
199 202
/// Test iteration on empty bitset.
200 203
@test unsafe fn testIterEmpty() throws (testing::TestError) {
201 204
    let mut bits: [u32; 2] = [0; 2];
202 -
    let mut bs = super::new(&mut bits[..]);
203 -
    let mut it = super::iter(&bs);
204 -
    let result = super::iterNext(&mut it);
205 +
    let bs = &mut bits[..];
206 +
    let mut it = super::iter(bs);
207 +
    let result = super::iterNext(&mut it, bs);
205 208
206 209
    try testing::expect(result == nil);
207 210
}
208 211
209 212
/// Test [`super::wordsFor`] calculation.
213 216
    try testing::expect(super::wordsFor(31) == 1);
214 217
    try testing::expect(super::wordsFor(32) == 1);
215 218
    try testing::expect(super::wordsFor(33) == 2);
216 219
    try testing::expect(super::wordsFor(64) == 2);
217 220
    try testing::expect(super::wordsFor(65) == 3);
221 +
    try testing::expect(super::wordsFor(0xFFFFFFFF) == 134217728);
218 222
}
219 223
220 224
/// Test out-of-bounds access is safe.
221 225
@test unsafe fn testOutOfBounds() throws (testing::TestError) {
222 226
    let mut bits: [u32; 1] = [0; 1];
223 -
    let mut bs = super::new(&mut bits[..]);
227 +
    let bs = &mut bits[..];
224 228
225 229
    // Setting beyond length should be ignored.
226 -
    super::put(&mut bs, 100);
227 -
    try testing::expect(not super::contains(&bs, 100));
230 +
    super::put(bs, 100);
231 +
    try testing::expect(not super::contains(bs, 100));
228 232
229 233
    // Clearing beyond length should be safe.
230 -
    super::clear(&mut bs, 100);
234 +
    super::clear(bs, 100);
231 235
232 236
    // Contains beyond length should return false.
233 -
    try testing::expect(not super::contains(&bs, 100));
237 +
    try testing::expect(not super::contains(bs, 100));
238 +
}
239 +
240 +
/// Allocate initialized words with a retained session lifetime.
241 +
@test unsafe fn testSession() throws (testing::TestError) {
242 +
    static DATA: [u8; 64] = [0; 64];
243 +
    let mut arena = alloc::new(&mut DATA[..]);
244 +
    use arena as bits in {
245 +
        let words = try! super::allocate(&bits, 65);
246 +
        try testing::expect(words.len == 3);
247 +
        try testing::expect(super::count(words) == 0);
248 +
        super::put(words, 64);
249 +
        try testing::expect(super::count(words) == 1);
250 +
        let empty = try! super::allocate(&bits, 0);
251 +
        try testing::expect(empty.len == 0);
252 +
        try testing::expect(super::count(empty) == 0);
253 +
    }
234 254
}
lib/std/lang/gen/data.rad +47 -16
90 90
    throws (Error)
91 91
{
92 92
    if item.alignment == 0 or (item.alignment & (item.alignment - 1)) <> 0 {
93 93
        throw Error::Alignment;
94 94
    }
95 -
    if (base & (item.alignment as u64 - 1)) <> 0 { throw Error::Alignment; }
95 +
    if (base & (item.alignment as u64 - 1)) <> 0 {
96 +
        throw Error::Alignment;
97 +
    }
96 98
    let aligned = (*offset as u64 + item.alignment as u64 - 1) & ~(item.alignment as u64 - 1);
97 99
    let end = aligned + item.size as u64;
98 100
    if end > 0xffffffff or base > 0xffffffffffffffff - end {
99 101
        throw Error::Overflow;
100 102
    }
101 -
    if *count >= syms.len { throw Error::Capacity; }
103 +
    if *count >= syms.len {
104 +
        throw Error::Capacity;
105 +
    }
102 106
    set syms[*count] = DataSym { name: item.name, addr: base + aligned };
103 107
    set *count += 1;
104 108
    set *offset = end as u32;
105 109
}
106 110
127 131
    buf: &mut [u8],
128 132
    readOnly: bool,
129 133
    startOffset: u32
130 134
) -> u32 throws (Error) {
131 135
    let mut offset: u32 = startOffset;
132 -
    if offset > buf.len { throw Error::Capacity; }
136 +
    if offset > buf.len {
137 +
        throw Error::Capacity;
138 +
    }
133 139
134 140
    for i in 0..items.len {
135 141
        let data = items[i];
136 142
        if data.readOnly == readOnly and not data.isZeroInit {
137 143
            let start = offset;
138 -
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { throw Error::Alignment; }
144 +
            if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
145 +
                throw Error::Alignment;
146 +
            }
139 147
            let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1);
140 -
            if aligned > buf.len as u64 { throw Error::Capacity; }
148 +
            if aligned > buf.len as u64 {
149 +
                throw Error::Capacity;
150 +
            }
141 151
            set offset = aligned as u32;
142 -
            if data.size > buf.len - offset { throw Error::Capacity; }
152 +
            if data.size > buf.len - offset {
153 +
                throw Error::Capacity;
154 +
            }
143 155
            let end = offset + data.size;
144 -
            for j in start..end { set buf[j] = 0; }
156 +
            for j in start..end {
157 +
                set buf[j] = 0;
158 +
            }
145 159
            for j in 0..data.values.len {
146 160
                let v = &data.values[j];
147 161
                let mut width: u32 = 1;
148 162
                match v.item {
149 -
                    case il::DataItem::Val { typ, .. } => { set width = il::typeSize(typ); },
150 -
                    case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; },
151 -
                    case il::DataItem::Str(bytes) => { set width = bytes.len; },
152 -
                    else => {},
163 +
                    case il::DataItem::Val { typ, .. } => {
164 +
                        set width = il::typeSize(typ);
165 +
                    },
166 +
                    case il::DataItem::Sym(_), il::DataItem::Fn(_) => {
167 +
                        set width = 8;
168 +
                    },
169 +
                    case il::DataItem::Str(bytes) => {
170 +
                        set width = bytes.len;
171 +
                    },
172 +
                    else => {
173 +
                    },
174 +
                }
175 +
                if width > 0 and v.count > (end - offset) / width {
176 +
                    throw Error::Overflow;
177 +
                }
178 +
                if width == 0 {
179 +
                    continue;
153 180
                }
154 -
                if width > 0 and v.count > (end - offset) / width { throw Error::Overflow; }
155 -
                if width == 0 { continue; }
156 181
                for _ in 0..v.count {
157 182
                    match v.item {
158 183
                        case il::DataItem::Val { typ, val } => {
159 184
                            let size = il::typeSize(typ);
160 185
                            try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size));
169 194
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
170 195
171 196
                            set offset += @sizeOf(u64);
172 197
                        },
173 198
                        case il::DataItem::Fn(name) => {
174 -
                            let fnOffset = dict::get(&fnLabels.funcs, name) else { throw Error::Symbol; };
175 -
                            if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 { throw Error::Overflow; }
199 +
                            let fnOffset = dict::get(&fnLabels.funcs, name) else {
200 +
                                throw Error::Symbol;
201 +
                            };
202 +
                            if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 {
203 +
                                throw Error::Overflow;
204 +
                            }
176 205
                            let addr = codeBase + fnOffset as u64;
177 206
                            let addr64: u64 = addr as u64;
178 207
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
179 208
180 209
                            set offset += @sizeOf(*u8);
202 231
    if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 {
203 232
        throw Error::Capacity;
204 233
    }
205 234
    let mut d = dict::init(entries);
206 235
    for i in 0..syms.len {
207 -
        if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { throw Error::Symbol; }
236 +
        if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil {
237 +
            throw Error::Symbol;
238 +
        }
208 239
        dict::insert(&mut d, syms[i].name, i as i32);
209 240
    }
210 241
    return DataSymMap { dict: d, syms };
211 242
}
212 243
lib/std/lang/gen/regalloc.rad +11 -11
33 33
    /// Size of a spill slot in bytes.
34 34
    slotSize: u32,
35 35
}
36 36
37 37
/// Complete register allocation result.
38 -
export record AllocResult: Copy {
38 +
export record AllocResult: 'scratch + Copy {
39 39
    /// SSA register to physical register mapping.
40 -
    assignments: *unsafe [?super::Reg],
40 +
    assignments: &'scratch [?super::Reg],
41 41
    /// Spill slot information.
42 -
    spill: spill::SpillInfo,
42 +
    spill: spill::SpillInfo 'scratch,
43 43
    /// Bitmask of used callee-saved registers.
44 44
    usedCalleeSaved: u32,
45 45
}
46 46
47 47
/// Run register allocation on a function.
48 48
///
49 49
/// Returns a mapping from SSA registers to physical registers, plus
50 50
/// spill information.
51 -
export unsafe fn allocate(
52 -
    func: *unsafe il::Fn,
51 +
export unsafe fn allocate 'scratch (
52 +
    func: &il::Fn,
53 53
    config: &TargetConfig,
54 -
    arena: &mut alloc::Arena
55 -
) -> AllocResult throws (alloc::AllocError) {
54 +
    storage: &Session 'scratch
55 +
) -> AllocResult 'scratch throws (alloc::AllocError) {
56 56
    // Phase 1: Liveness analysis.
57 -
    let live = try liveness::analyze(func, arena);
57 +
    let live = try liveness::analyze(func, storage);
58 58
    // Phase 2: Spill analysis (determine which values need stack slots).
59 -
    let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, arena);
59 +
    let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, storage);
60 60
    // Phase 3: Register assignment (map SSA registers to physical registers).
61 -
    let assignInfo = try assign::assign(func, &live, &spillInfo, config, arena);
61 +
    let assignInfo = try assign::assign(func, &live, &spillInfo, config, storage);
62 62
63 -
    return AllocResult {
63 +
    return AllocResult 'scratch {
64 64
        assignments: assignInfo.assignments,
65 65
        spill: spillInfo,
66 66
        usedCalleeSaved: assignInfo.usedCalleeSaved,
67 67
    };
68 68
}
lib/std/lang/gen/regalloc/assign.rad +78 -96
16 16
/// Maximum number of active register mappings.
17 17
constant MAX_ACTIVE: u32 = 64;
18 18
19 19
/// Register mapping at a program point.
20 20
/// Maps SSA registers to physical registers.
21 -
export record RegMap {
21 +
export record RegMap: 'scratch {
22 22
    /// SSA (virtual) registers that have mappings.
23 -
    virtRegs: *mut [u32],
23 +
    virtRegs: &'scratch mut [u32],
24 24
    /// Physical register for each virtual register.
25 -
    physRegs: *mut [gen::Reg],
25 +
    physRegs: &'scratch mut [gen::Reg],
26 26
    /// Number of active mappings.
27 27
    n: u32,
28 28
}
29 29
30 30
/// Register assignment result, per function.
31 -
export record AssignInfo: Copy {
31 +
export record AssignInfo: 'scratch + Copy {
32 32
    /// SSA register -> physical register mapping.
33 -
    assignments: *unsafe mut [?gen::Reg],
33 +
    assignments: &'scratch [?gen::Reg],
34 34
    /// Bitmask of used callee-saved registers.
35 35
    usedCalleeSaved: u32,
36 36
}
37 37
38 38
/// Per-instruction context for freeing and allocating register uses.
39 -
record InstrCtx: Copy {
40 -
    current: *unsafe mut RegMap,
41 -
    usedRegs: *unsafe mut bitset::Bitset,
39 +
record InstrCtx: 'scratch + 'step where 'scratch: 'step {
40 +
    current: &'step mut RegMap 'scratch,
41 +
    usedRegs: &'step mut [u32],
42 42
    /// Last operand-use index for each register used in the current block.
43 -
    lastUse: *unsafe [u32],
44 -
    live: *unsafe liveness::LiveInfo,
43 +
    lastUse: &'step [u32],
44 +
    live: &'step liveness::LiveInfo 'scratch,
45 45
    blockIdx: u32,
46 46
    instrIdx: u32,
47 47
    allocatable: *[gen::Reg],
48 48
    calleeSaved: *[gen::Reg],
49 -
    assignments: *unsafe mut [?gen::Reg],
50 -
    spillInfo: *unsafe spill::SpillInfo,
51 -
}
52 -
53 -
/// Context for recording the last operand-use index in a block.
54 -
record LastUseCtx: Copy {
55 -
    /// Per-register indices, shared by all blocks in the function.
56 -
    lastUse: *unsafe mut [u32],
57 -
    /// Index of the instruction whose operands are being recorded.
58 -
    index: u32,
49 +
    assignments: &'step mut [?gen::Reg],
50 +
    spillInfo: &'step spill::SpillInfo 'scratch,
59 51
}
60 52
61 53
/// Compute register assignment.
62 -
export unsafe fn assign(
63 -
    func: *unsafe il::Fn,
64 -
    live: &liveness::LiveInfo,
65 -
    spillInfo: &spill::SpillInfo,
54 +
export unsafe fn assign 'scratch (
55 +
    func: &il::Fn,
56 +
    live: &liveness::LiveInfo 'scratch,
57 +
    spillInfo: &spill::SpillInfo 'scratch,
66 58
    config: &super::TargetConfig,
67 -
    arena: &mut alloc::Arena
68 -
) -> AssignInfo throws (alloc::AllocError) {
59 +
    storage: &Session 'scratch
60 +
) -> AssignInfo 'scratch throws (alloc::AllocError) {
69 61
    let maxReg = live.maxReg;
70 62
    let blockCount = func.blocks.len;
71 63
    let allocatable = config.allocatable;
72 64
73 65
    if maxReg == 0 or blockCount == 0 {
74 -
        return AssignInfo {
75 -
            assignments: &mut [],
66 +
        return AssignInfo 'scratch {
67 +
            assignments: try storage.fill(nil as ?gen::Reg, 0),
76 68
            usedCalleeSaved: 0,
77 69
        };
78 70
    }
79 71
80 72
    // Allocate output structures.
81 -
    let assignments = try alloc::allocRawSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *unsafe mut [?gen::Reg];
82 -
    for i in 0..maxReg {
83 -
        set assignments[i] = nil;
84 -
    }
85 -
    // Reuse one last-use table for all blocks in the function.
86 -
    let lastUse = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *unsafe mut [u32];
73 +
    let assignments = try storage.fill(nil as ?gen::Reg, maxReg);
87 74
    // Pre-assign function parameters to argument registers.
88 75
    // Cross-call params are NOT pre-assigned here; they will be allocated
89 76
    // to callee-saved registers by the normal path, and isel emits moves
90 77
    // from the arg register to the assigned register at function entry.
91 78
    for param, i in func.params {
92 79
        if i < config.argRegs.len {
93 -
            if not bitset::contains(&spillInfo.calleeClass, param.value.n) {
80 +
            if not bitset::contains(spillInfo.calleeClass, param.value.n) {
94 81
                set assignments[param.value.n] = config.argRegs[i];
95 82
            }
96 83
        }
97 84
    }
98 85
    // Allocate used registers bitset (32 physical registers per 32-bit word).
99 -
    let usedRegsBits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *unsafe mut [u32];
100 -
    let mut usedRegs = bitset::init(usedRegsBits);
86 +
    let usedRegs = try bitset::allocate(storage, 32);
101 87
102 88
    // Current register mapping.
103 -
    let mut current = try createRegMap(arena);
89 +
    // Reuse one last-use table for all blocks in the function.
90 +
    let lastUse = try storage.fill(0 as u32, maxReg);
91 +
    let mut current = try createRegMap(storage);
104 92
105 93
    // Phase 2: Linear scan allocation.
106 94
    for b in 0..blockCount {
107 95
        let block = &func.blocks[b];
108 96
109 97
        // Record every operand before allocation. Only current-block operands
110 98
        // query this table, so every read is initialized by this scan.
111 99
        // Entries for other registers need not be cleared between blocks.
112 100
        for instr, i in block.instrs {
113 -
            let mut ctx = LastUseCtx { lastUse, index: i };
114 -
            il::forEachReg(instr, recordLastUseCb, &mut ctx as &mut opaque);
101 +
            let mut registers = il::registers(&instr);
102 +
            while let reg = il::nextReg(&mut registers, &instr) {
103 +
                recordLastUse(reg, &mut lastUse[..], i);
104 +
            }
115 105
        }
116 106
117 107
        // Reset for new block.
118 108
        set current.n = 0;
119 -
        bitset::clearAll(&mut usedRegs);
109 +
        bitset::clearAll(usedRegs);
120 110
121 111
        // Mark all live-in values' registers as used.
122 112
        // This ensures we don't reuse registers for values that flow in
123 113
        // from predecessors, even at merge points with multiple predecessors.
124 114
        // Values that are live-in but have no assignment yet (e.g. callee-saved
125 115
        // function parameters not used before a phi block) are allocated now to
126 116
        // prevent conflicts with block parameters.
127 -
        let mut liveInIter = bitset::iter(&live.liveIn[b]);
128 -
        while let ssaReg = bitset::iterNext(&mut liveInIter) {
117 +
        let mut liveInIter = bitset::iter(liveness::liveInRow(live, b));
118 +
        while let ssaReg = bitset::iterNext(&mut liveInIter, liveness::liveInRow(live, b)) {
129 119
            let reg = il::Reg { n: ssaReg };
130 120
            if not spill::isSpilled(spillInfo, reg) {
131 121
                if let phys = assignments[ssaReg] {
132 -
                    bitset::put(&mut usedRegs, *phys as u32);
122 +
                    bitset::put(usedRegs, *phys as u32);
133 123
                    rmapSet(&mut current, ssaReg, phys);
134 124
                } else {
135 -
                    set assignments[ssaReg] = rallocReg(&mut current, &mut usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo);
125 +
                    set assignments[ssaReg] = rallocReg(&mut current, usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo);
136 126
                }
137 127
            }
138 128
        }
139 129
140 130
        // Allocate block parameters.
141 131
        for p in block.params {
142 132
            if p.value.n < maxReg and not spill::isSpilled(spillInfo, p.value) {
143 -
                set assignments[p.value.n] = rallocReg(&mut current, &mut usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo);
133 +
                set assignments[p.value.n] = rallocReg(&mut current, usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo);
144 134
            }
145 135
        }
146 136
147 137
        // Process each instruction.
148 138
        for instr, i in block.instrs {
149 -
            let mut ctx = InstrCtx {
150 -
                current: &mut current,
151 -
                usedRegs: &mut usedRegs,
152 -
                lastUse,
153 -
                live: live as *unsafe liveness::LiveInfo,
154 -
                blockIdx: b,
155 -
                instrIdx: i,
156 -
                allocatable,
157 -
                calleeSaved: config.calleeSaved,
158 -
                assignments,
159 -
                spillInfo: spillInfo as *unsafe spill::SpillInfo,
160 -
            };
161 -
            il::forEachReg(instr, processInstrRegCb, &mut ctx as &mut opaque);
139 +
            let currentRef: 'step = &mut current, usedRef = &mut usedRegs[..],
140 +
                lastUseRef = &lastUse[..], liveRef = &*live,
141 +
                assignmentsRef = &mut assignments[..], spillRef = &*spillInfo
142 +
            where 'scratch: 'step in {
143 +
                let mut ctx = InstrCtx 'scratch 'step {
144 +
                    current: currentRef,
145 +
                    usedRegs: usedRef,
146 +
                    lastUse: lastUseRef,
147 +
                    live: liveRef,
148 +
                    blockIdx: b,
149 +
                    instrIdx: i,
150 +
                    allocatable,
151 +
                    calleeSaved: config.calleeSaved,
152 +
                    assignments: assignmentsRef,
153 +
                    spillInfo: spillRef,
154 +
                };
155 +
                let mut registers = il::registers(&instr);
156 +
                while let reg = il::nextReg(&mut registers, &instr) {
157 +
                    processInstrReg(reg, &mut ctx);
158 +
                }
159 +
            }
162 160
163 161
            // Allocate destination.
164 162
            if let dst = il::instrDst(instr) {
165 163
                if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) {
166 -
                    set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo);
164 +
                    set assignments[dst.n] = rallocReg(&mut current, usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo);
167 165
                }
168 166
            }
169 167
        }
170 168
    }
171 169
    // Compute bitmask of used callee-saved registers.
178 176
                }
179 177
            }
180 178
        }
181 179
    }
182 180
183 -
    return AssignInfo {
184 -
        assignments,
181 +
    return AssignInfo 'scratch {
182 +
        assignments: &assignments[..],
185 183
        usedCalleeSaved,
186 184
    };
187 185
}
188 186
189 187
/// Create an empty register map.
190 -
unsafe fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
191 -
    let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32];
192 -
    let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg];
193 -
194 -
    return RegMap { virtRegs, physRegs, n: 0 };
188 +
fn createRegMap 'scratch (storage: &Session 'scratch) -> RegMap 'scratch throws (alloc::AllocError) {
189 +
    let virtRegs = try storage.fill(0 as u32, MAX_ACTIVE);
190 +
    let physRegs = try storage.fill(gen::Reg(0), MAX_ACTIVE);
191 +
    return RegMap 'scratch { virtRegs, physRegs, n: 0 };
195 192
}
196 193
197 194
/// Find physical register for a virtual register in RegMap.
198 -
fn rmapFind(rmap: &RegMap, virtReg: u32) -> ?gen::Reg {
195 +
fn rmapFind 'scratch (rmap: &RegMap 'scratch, virtReg: u32) -> ?gen::Reg {
199 196
    for i in 0..rmap.n {
200 197
        if rmap.virtRegs[i] == virtReg {
201 198
            return rmap.physRegs[i];
202 199
        }
203 200
    }
204 201
    return nil;
205 202
}
206 203
207 204
/// Add a mapping to the register map.
208 -
fn rmapSet(rmap: &mut RegMap, virtReg: u32, physReg: gen::Reg) {
205 +
fn rmapSet 'scratch (rmap: &mut RegMap 'scratch, virtReg: u32, physReg: gen::Reg) {
209 206
    assert rmap.n < MAX_ACTIVE, "rmapSet: register map overflow";
210 207
    set rmap.virtRegs[rmap.n] = virtReg;
211 208
    set rmap.physRegs[rmap.n] = physReg;
212 209
    set rmap.n += 1;
213 210
}
214 211
215 212
/// Remove a mapping from the register map and return its physical register.
216 -
fn rmapRemove(rmap: &mut RegMap, virtReg: u32) -> ?gen::Reg {
213 +
fn rmapRemove 'scratch (rmap: &mut RegMap 'scratch, virtReg: u32) -> ?gen::Reg {
217 214
    for i in 0..rmap.n {
218 215
        if rmap.virtRegs[i] == virtReg {
219 216
            let phys = rmap.physRegs[i];
220 217
            // Swap with last and decrement.
221 218
            set rmap.n -= 1;
228 225
    }
229 226
    return nil;
230 227
}
231 228
232 229
/// Find first free register in pool, allocate it, return it.
233 -
unsafe fn findFreeInPool(usedRegs: &mut bitset::Bitset, current: &mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
234 -
    for i in 0..pool.len {
235 -
        let r = pool[i];
230 +
fn findFreeInPool 'scratch (usedRegs: &mut [u32], current: &mut RegMap 'scratch, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
231 +
    for r in pool {
236 232
        if not bitset::contains(usedRegs, *r as u32) {
237 233
            bitset::put(usedRegs, *r as u32);
238 234
            rmapSet(current, ssaReg, r);
239 235
            return r;
240 236
        }
242 238
    return nil;
243 239
}
244 240
245 241
/// Allocate a physical register for an SSA register.
246 242
/// Cross-call values are steered to callee-saved registers.
247 -
unsafe fn rallocReg(
248 -
    current: &mut RegMap,
249 -
    usedRegs: &mut bitset::Bitset,
243 +
fn rallocReg 'scratch (
244 +
    current: &mut RegMap 'scratch,
245 +
    usedRegs: &mut [u32],
250 246
    ssaReg: u32,
251 247
    allocatable: *[gen::Reg],
252 248
    calleeSaved: *[gen::Reg],
253 -
    spillInfo: &spill::SpillInfo
249 +
    spillInfo: &spill::SpillInfo 'scratch
254 250
) -> gen::Reg {
255 251
    // Check if already assigned.
256 252
    if let phys = rmapFind(current, ssaReg) {
257 253
        return phys;
258 254
    }
259 -
    let crossCall = bitset::contains(&spillInfo.calleeClass, ssaReg);
260 255
    // Allocate from appropriate pool. Cross-call values must use callee-saved
261 256
    // registers since they are live across function calls.
262 -
    if crossCall {
257 +
    if bitset::contains(spillInfo.calleeClass, ssaReg) {
263 258
        if let r = findFreeInPool(usedRegs, current, ssaReg, calleeSaved) {
264 259
            return r;
265 260
        }
266 261
        panic "rallocReg: no callee-saved register for cross-call value";
267 262
    }
269 264
        return r;
270 265
    }
271 266
    panic "rallocReg: no free register, spilling fault";
272 267
}
273 268
274 -
/// Record the current index; forward traversal leaves the last operand use.
275 -
unsafe fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) {
276 -
    let index = (ctxPtr as &mut LastUseCtx).index;
277 -
    recordLastUse(reg, &mut (ctxPtr as &mut LastUseCtx).lastUse[..], index);
278 -
}
279 -
280 269
/// Record the last instruction that uses the register.
281 270
fn recordLastUse(reg: il::Reg, lastUse: &mut [u32], index: u32) {
282 271
    set lastUse[reg.n] = index;
283 272
}
284 273
285 -
/// Free operands with no later block use or live-out use, then allocate missing uses.
286 -
unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) {
287 -
    processInstrReg(reg, ctxPtr as &mut InstrCtx);
288 -
}
289 -
290 274
/// Release expired registers and assign a register for this operand.
291 -
unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) {
292 -
    if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) {
275 +
fn processInstrReg 'scratch 'step (reg: il::Reg, ctx: &mut InstrCtx 'scratch 'step) where 'scratch: 'step {
276 +
    if not (bitset::contains(liveness::liveOutRow(ctx.live, ctx.blockIdx), reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) {
293 277
        if let phys = rmapRemove(ctx.current, reg.n) {
294 278
            bitset::clear(ctx.usedRegs, *phys as u32);
295 279
        }
296 280
    }
297 -
    assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds";
281 +
    assert reg.n < ctx.assignments.len, "processInstrReg: register out of bounds";
298 282
    if spill::isSpilled(ctx.spillInfo, reg) {
299 283
        return; // Spilled values don't get physical registers.
300 284
    }
301 285
    if ctx.assignments[reg.n] == nil {
302 -
        let current = ctx.current;
303 -
        let usedRegs = ctx.usedRegs;
304 286
        set ctx.assignments[reg.n] = rallocReg(
305 -
            current, usedRegs, reg.n, ctx.allocatable,
287 +
            ctx.current, ctx.usedRegs, reg.n, ctx.allocatable,
306 288
            ctx.calleeSaved, ctx.spillInfo
307 289
        );
308 290
    }
309 291
}
lib/std/lang/gen/regalloc/liveness.rad +86 -100
22 22
//!   let live = liveness::analyze(func, ...);
23 23
//!   if not liveness::hasLaterUse(&live, func, blockIdx, instrIdx, reg) {
24 24
//!       // `reg` dies at this instruction.
25 25
//!   }
26 26
27 -
use std::mem;
27 +
@test mod tests;
28 +
28 29
use std::lang::il;
29 30
use std::lang::alloc;
30 31
use std::lang::gen::bitset;
31 32
32 33
/// Maximum number of SSA registers supported.
33 34
export constant MAX_SSA_REGS: u32 = 8192;
34 35
35 36
/// Liveness information for a function.
36 -
export record LiveInfo: Copy {
37 -
    /// Per-block live-in sets (indexed by block index).
38 -
    liveIn: *unsafe mut [bitset::Bitset],
39 -
    /// Per-block live-out sets (indexed by block index).
40 -
    liveOut: *unsafe mut [bitset::Bitset],
41 -
    /// Per-block defs sets (registers defined in block).
42 -
    defs: *unsafe mut [bitset::Bitset],
43 -
    /// Per-block uses sets (registers used before defined in block).
44 -
    uses: *unsafe mut [bitset::Bitset],
37 +
export record LiveInfo: 'scratch + Copy {
38 +
    /// Per-block live-in words, stored in consecutive rows.
39 +
    liveIn: &'scratch [u32],
40 +
    /// Per-block live-out words, stored in consecutive rows.
41 +
    liveOut: &'scratch [u32],
42 +
    /// Per-block definition words, stored in consecutive rows.
43 +
    defs: &'scratch [u32],
44 +
    /// Per-block use-before-definition words, stored in consecutive rows.
45 +
    uses: &'scratch [u32],
46 +
    /// Number of words in one block row.
47 +
    words: u32,
45 48
    /// Number of blocks.
46 49
    blockCount: u32,
47 50
    /// Maximum register number used.
48 51
    maxReg: u32,
49 52
}
50 53
51 -
/// Context for collecting defs and uses during block analysis.
52 -
record DefsUses: Copy {
53 -
    defs: *unsafe bitset::Bitset,
54 -
    uses: *unsafe mut bitset::Bitset,
55 -
}
56 -
57 -
/// Context for searching for a specific register in an instruction.
58 -
record FindCtx: Copy {
59 -
    target: u32,
60 -
    found: bool,
61 -
}
62 -
63 54
/// Compute liveness by growing live sets until no live-in set changes.
64 -
export unsafe fn analyze(func: *unsafe il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) {
55 +
export unsafe fn analyze 'scratch (func: &il::Fn, storage: &Session 'scratch) -> LiveInfo 'scratch throws (alloc::AllocError) {
65 56
    let blockCount = func.blocks.len;
66 57
    if blockCount == 0 {
67 -
        return LiveInfo {
68 -
            liveIn: &mut [],
69 -
            liveOut: &mut [],
70 -
            defs: &mut [],
71 -
            uses: &mut [],
72 -
            blockCount: 0,
73 -
            maxReg: 0,
74 -
        };
58 +
        let empty = try storage.fill(0 as u32, 0);
59 +
        let frozen: &'scratch [u32] = &empty[..];
60 +
        return LiveInfo 'scratch { liveIn: frozen, liveOut: frozen, defs: frozen, uses: frozen, words: 0, blockCount: 0, maxReg: 0 };
75 61
    }
76 62
77 63
    // Find max register number.
78 64
    let mut maxReg: u32 = 0;
79 65
    for p in func.params {
82 68
    for b in 0..blockCount {
83 69
        let block = &func.blocks[b];
84 70
        for p in block.params {
85 71
            set maxReg = maxRegNum(p.value.n, maxReg);
86 72
        }
87 -
        for i in 0..block.instrs.len {
88 -
            il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as &mut opaque);
89 -
            if let dst = il::instrDst(block.instrs[i]) {
73 +
        for instr in block.instrs {
74 +
            let mut registers = il::registers(&instr);
75 +
            while let reg = il::nextReg(&mut registers, &instr) {
76 +
                updateMaxReg(reg, &mut maxReg);
77 +
            }
78 +
            if let dst = il::instrDst(instr) {
90 79
                set maxReg = maxRegNum(dst.n, maxReg);
91 80
            }
92 81
        }
93 82
    }
94 -
    if maxReg > MAX_SSA_REGS { throw alloc::AllocError::OutOfMemory; }
95 -
    // Allocate per-block bitsets.
96 -
    let liveIn = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset];
97 -
    let liveOut = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset];
98 -
    let defs = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset];
99 -
    let uses = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset];
100 -
101 -
    for b in 0..blockCount {
102 -
        set liveIn[b] = try bitset::allocate(arena, maxReg);
103 -
        set liveOut[b] = try bitset::allocate(arena, maxReg);
104 -
        set defs[b] = try bitset::allocate(arena, maxReg);
105 -
        set uses[b] = try bitset::allocate(arena, maxReg);
83 +
    if maxReg > MAX_SSA_REGS {
84 +
        throw alloc::AllocError::OutOfMemory;
106 85
    }
86 +
    // Allocate one contiguous word matrix for each set class.
87 +
    let words = bitset::wordsFor(maxReg);
88 +
    let count64 = blockCount as u64 * words as u64;
89 +
    if count64 > 0xFFFFFFFF {
90 +
        throw alloc::AllocError::OutOfMemory;
91 +
    }
92 +
    let count = count64 as u32;
93 +
    let liveIn = try storage.fill(0 as u32, count);
94 +
    let liveOut = try storage.fill(0 as u32, count);
95 +
    let defs = try storage.fill(0 as u32, count);
96 +
    let uses = try storage.fill(0 as u32, count);
107 97
108 98
    // Compute local defs and uses for each block.
109 99
    for b in 0..blockCount {
110 -
        computeLocalDefsUses(&func.blocks[b], &mut defs[b], &mut uses[b]);
100 +
        computeLocalDefsUses(&func.blocks[b], &mut defs[b * words..(b + 1) * words], &mut uses[b * words..(b + 1) * words]);
111 101
    }
112 102
    // Live sets grow monotonically from empty sets; no scratch set is needed.
113 103
    let mut changed = true;
114 104
115 105
    while changed {
120 110
        while b > 0 {
121 111
            set b -= 1;
122 112
            let block = &func.blocks[b];
123 113
124 114
            // Add successor live-in sets directly to this block's live-out set.
125 -
            addSuccessorLiveIn(func, block, liveIn, &mut liveOut[b]);
115 +
            addSuccessorLiveIn(func, block, liveIn, words, &mut liveOut[b * words..(b + 1) * words]);
126 116
127 -
            if computeAndUpdateLiveIn(&mut liveIn[b], &liveOut[b], &defs[b], &uses[b]) {
117 +
            if computeAndUpdateLiveIn(&mut liveIn[b * words..(b + 1) * words], &liveOut[b * words..(b + 1) * words], &defs[b * words..(b + 1) * words], &uses[b * words..(b + 1) * words]) {
128 118
                set changed = true;
129 119
            }
130 120
        }
131 121
    }
132 -
    return LiveInfo { liveIn, liveOut, defs, uses, blockCount, maxReg };
122 +
    return LiveInfo 'scratch { liveIn: &liveIn[..], liveOut: &liveOut[..], defs: &defs[..], uses: &uses[..], words, blockCount, maxReg };
133 123
}
134 124
135 125
/// Compute `liveIn = uses | (liveOut - defs)` and update `dst`.
136 126
/// Returns `true` if `dst` changed. Combined loop avoids multiple passes.
137 -
unsafe fn computeAndUpdateLiveIn(
138 -
    dst: *unsafe mut bitset::Bitset,
139 -
    liveOut: *unsafe bitset::Bitset,
140 -
    defs: *unsafe bitset::Bitset,
141 -
    uses: *unsafe bitset::Bitset
127 +
fn computeAndUpdateLiveIn(
128 +
    dst: &mut [u32],
129 +
    liveOut: &[u32],
130 +
    defs: &[u32],
131 +
    uses: &[u32]
142 132
) -> bool {
143 -
    let numWords = dst.bits.len;
144 133
    let mut changed = false;
145 -
    for i in 0..numWords {
146 -
        let newWord = uses.bits[i] | (liveOut.bits[i] & ~defs.bits[i]);
147 -
        if dst.bits[i] <> newWord {
148 -
            set dst.bits[i] = newWord;
134 +
    for i in 0..dst.len {
135 +
        let newWord = uses[i] | (liveOut[i] & ~defs[i]);
136 +
        if dst[i] <> newWord {
137 +
            set dst[i] = newWord;
149 138
            set changed = true;
150 139
        }
151 140
    }
152 141
    return changed;
153 142
}
154 143
155 144
/// Compute local defs and uses for a single block.
156 -
unsafe fn computeLocalDefsUses(block: *unsafe il::Block, defs: *unsafe mut bitset::Bitset, uses: *unsafe mut bitset::Bitset) {
145 +
unsafe fn computeLocalDefsUses(block: &il::Block, defs: &mut [u32], uses: &mut [u32]) {
157 146
    for p in block.params {
158 147
        bitset::put(defs, p.value.n);
159 148
    }
160 -
    for i in 0..block.instrs.len {
161 -
        let instr = block.instrs[i];
162 -
        let mut ctx = DefsUses { defs, uses };
163 -
        il::forEachReg(instr, addUseCallback, &mut ctx as &mut opaque);
149 +
    for instr in block.instrs {
150 +
        let mut registers = il::registers(&instr);
151 +
        while let reg = il::nextReg(&mut registers, &instr) {
152 +
            addUse(reg, defs, uses);
153 +
        }
164 154
165 155
        if let dst = il::instrDst(instr) {
166 156
            bitset::put(defs, dst.n);
167 157
        }
168 158
    }
169 159
}
170 160
171 -
/// Callback for [`il::forEachReg`]: adds register to uses if not already defined.
172 -
unsafe fn addUseCallback(reg: il::Reg, ctx: &mut opaque) {
173 -
    addUse(reg, ctx as &mut DefsUses);
174 -
}
175 -
176 161
/// Add an undefined register to the use set.
177 -
unsafe fn addUse(reg: il::Reg, c: &mut DefsUses) {
178 -
    if not bitset::contains(c.defs, reg.n) {
179 -
        bitset::put(c.uses, reg.n);
162 +
fn addUse(reg: il::Reg, defs: &[u32], uses: &mut [u32]) {
163 +
    if not bitset::contains(defs, reg.n) {
164 +
        bitset::put(uses, reg.n);
180 165
    }
181 166
}
182 167
183 -
/// Callback for [`il::forEachReg`]: updates max register number.
184 -
unsafe fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) {
185 -
    updateMaxReg(reg, ctx as &mut u32);
186 -
}
187 -
188 168
/// Update the largest register number.
189 169
fn updateMaxReg(reg: il::Reg, max: &mut u32) {
190 170
    set *max = maxRegNum(reg.n, *max);
191 171
}
192 172
193 173
/// Return the larger of n+1 and current.
194 174
fn maxRegNum(n: u32, current: u32) -> u32 {
195 -
    if n >= MAX_SSA_REGS { return MAX_SSA_REGS + 1; }
175 +
    if n >= MAX_SSA_REGS {
176 +
        return MAX_SSA_REGS + 1;
177 +
    }
196 178
    if n + 1 > current {
197 179
        return n + 1;
198 180
    }
199 181
    return current;
200 182
}
201 183
202 184
/// Add successor live-in sets to the block's live-out set.
203 -
unsafe fn addSuccessorLiveIn(func: *unsafe il::Fn, block: *unsafe il::Block, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) {
185 +
unsafe fn addSuccessorLiveIn(func: &il::Fn, block: &il::Block, liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
204 186
    if block.instrs.len == 0 {
205 187
        return;
206 188
    }
207 189
    let term = block.instrs[block.instrs.len - 1];
208 190
209 191
    match term {
210 192
        case il::Instr::Jmp { target, .. } =>
211 -
            unionBlockLiveIn(target, liveIn, liveOut),
193 +
            unionBlockLiveIn(target, liveIn, words, liveOut),
212 194
        case il::Instr::Br { thenTarget, elseTarget, .. } => {
213 -
            unionBlockLiveIn(thenTarget, liveIn, liveOut);
214 -
            unionBlockLiveIn(elseTarget, liveIn, liveOut);
195 +
            unionBlockLiveIn(thenTarget, liveIn, words, liveOut);
196 +
            unionBlockLiveIn(elseTarget, liveIn, words, liveOut);
215 197
        },
216 198
        case il::Instr::Switch { defaultTarget, cases, .. } => {
217 -
            unionBlockLiveIn(defaultTarget, liveIn, liveOut);
199 +
            unionBlockLiveIn(defaultTarget, liveIn, words, liveOut);
218 200
            for c in cases {
219 -
                unionBlockLiveIn(c.target, liveIn, liveOut);
201 +
                unionBlockLiveIn(c.target, liveIn, words, liveOut);
220 202
            }
221 203
        },
222 204
        else => {},
223 205
    }
224 206
}
225 207
226 208
/// Union a target block's live-in set into the block's live-out set.
227 -
unsafe fn unionBlockLiveIn(target: u32, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) {
228 -
    bitset::union_(liveOut, &liveIn[target]);
209 +
fn unionBlockLiveIn(target: u32, liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
210 +
    bitset::union_(liveOut, &liveIn[target * words..(target + 1) * words]);
229 211
}
230 212
231 213
/// Check if a register has any use after this instruction.
232 -
export unsafe fn hasLaterUse(info: *unsafe LiveInfo, func: *unsafe il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
214 +
export unsafe fn hasLaterUse 'scratch (info: &LiveInfo 'scratch, func: &il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
233 215
    let block = &func.blocks[blockIdx];
234 216
235 -
    if bitset::contains(&info.liveOut[blockIdx], reg.n) {
217 +
    if bitset::contains(liveOutRow(info, blockIdx), reg.n) {
236 218
        return true;
237 219
    }
238 220
    for i in (instrIdx + 1)..block.instrs.len {
239 221
        if instrUsesReg(block.instrs[i], reg) {
240 222
            return true;
243 225
    return false;
244 226
}
245 227
246 228
/// Check if an instruction uses a specific register.
247 229
unsafe fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool {
248 -
    let mut ctx = FindCtx { target: reg.n, found: false };
249 -
    il::forEachReg(instr, findRegCallback, &mut ctx as &mut opaque);
250 -
    return ctx.found;
230 +
    let mut registers = il::registers(&instr);
231 +
    while let source = il::nextReg(&mut registers, &instr) {
232 +
        if source.n == reg.n {
233 +
            return true;
234 +
        }
235 +
    }
236 +
    return false;
251 237
}
252 238
253 -
/// Callback for [`il::forEachReg`]: sets found if register matches target.
254 -
unsafe fn findRegCallback(reg: il::Reg, ctx: &mut opaque) {
255 -
    findReg(reg, ctx as &mut FindCtx);
239 +
/// Borrow the live-in words for one block.
240 +
export fn liveInRow 'scratch (info: &LiveInfo 'scratch, block: u32) -> &'scratch [u32] {
241 +
    assert block < info.blockCount;
242 +
    return &info.liveIn[block * info.words..(block + 1) * info.words];
256 243
}
257 244
258 -
/// Set the match flag when the register equals the target.
259 -
fn findReg(reg: il::Reg, c: &mut FindCtx) {
260 -
    if reg.n == c.target {
261 -
        set c.found = true;
262 -
    }
245 +
/// Borrow the live-out words for one block.
246 +
export fn liveOutRow 'scratch (info: &LiveInfo 'scratch, block: u32) -> &'scratch [u32] {
247 +
    assert block < info.blockCount;
248 +
    return &info.liveOut[block * info.words..(block + 1) * info.words];
263 249
}
lib/std/lang/gen/regalloc/liveness/tests.rad added +69 -0
1 +
//! Tests for liveness analysis.
2 +
3 +
use std::testing;
4 +
use std::lang::alloc;
5 +
use std::lang::il;
6 +
use std::lang::gen::bitset;
7 +
8 +
9 +
/// Construct a block whose instruction storage belongs to the caller.
10 +
unsafe fn block(name: *[u8], instructions: *unsafe mut [il::Instr]) -> il::Block {
11 +
    return il::Block { label: name, params: &[], instrs: instructions,
12 +
        locs: &[], preds: &[], loopDepth: 0 };
13 +
}
14 +
15 +
/// Check one bit in each word of a three-word row.
16 +
fn check(words: &[u32], first: bool, second: bool, third: bool) throws (testing::TestError) {
17 +
    try testing::expect(words.len == 3);
18 +
    try testing::expect(bitset::contains(words, 0) == first);
19 +
    try testing::expect(bitset::contains(words, 33) == second);
20 +
    try testing::expect(bitset::contains(words, 66) == third);
21 +
}
22 +
23 +
@test unsafe fn testLoopLiveness() throws (testing::TestError) {
24 +
    let r0 = il::Reg { n: 0 };
25 +
    let r33 = il::Reg { n: 33 };
26 +
    let r66 = il::Reg { n: 66 };
27 +
    let mut entry = [
28 +
        il::Instr::Copy { dst: r0, val: il::Val::Imm(7) },
29 +
        il::Instr::Jmp { target: 1, args: &mut [] },
30 +
    ];
31 +
    let mut head = [
32 +
        il::Instr::Copy { dst: r33, val: il::Val::Reg(r0) },
33 +
        il::Instr::Br { op: il::CmpOp::Eq, typ: il::Type::W64,
34 +
            a: il::Val::Reg(r33), b: il::Val::Imm(0),
35 +
            thenTarget: 2, thenArgs: &mut [], elseTarget: 3, elseArgs: &mut [] },
36 +
    ];
37 +
    let mut body = [
38 +
        il::Instr::BinOp { op: il::BinOp::Add, typ: il::Type::W64,
39 +
            dst: r66, a: il::Val::Reg(r33), b: il::Val::Imm(1) },
40 +
        il::Instr::Jmp { target: 1, args: &mut [] },
41 +
    ];
42 +
    let mut exit = [il::Instr::Ret { val: il::Val::Reg(r33) }];
43 +
    let blocks = [block("entry", &mut entry[..]), block("head", &mut head[..]),
44 +
        block("body", &mut body[..]), block("exit", &mut exit[..])];
45 +
    let function = il::Fn { name: "loop", params: &[], returnType: il::Type::W64,
46 +
        isExtern: false, isLeaf: true, blocks: &blocks[..] };
47 +
    static DATA: [u8; 8192] = [0; 8192];
48 +
    let mut arena = alloc::new(&mut DATA[..]);
49 +
    let mut answer: u32 = 0;
50 +
    use arena as analysis in {
51 +
        let live = try! super::analyze(&function, &analysis);
52 +
        try testing::expect(live.blockCount == 4);
53 +
        try testing::expect(live.maxReg == 67);
54 +
        try testing::expect(live.words == 3);
55 +
        try check(super::liveInRow(&live, 0), false, false, false);
56 +
        try check(super::liveOutRow(&live, 0), true, false, false);
57 +
        try check(super::liveInRow(&live, 1), true, false, false);
58 +
        try check(super::liveOutRow(&live, 1), true, true, false);
59 +
        try check(super::liveInRow(&live, 2), true, true, false);
60 +
        try check(super::liveOutRow(&live, 2), true, false, false);
61 +
        try check(super::liveInRow(&live, 3), false, true, false);
62 +
        try check(super::liveOutRow(&live, 3), false, false, false);
63 +
        try check(&live.defs[6..9], false, false, true);
64 +
        try check(&live.uses[6..9], false, true, false);
65 +
        set answer = 42;
66 +
    }
67 +
    alloc::reset(&mut arena);
68 +
    try testing::expect(answer == 42);
69 +
}
lib/std/lang/gen/regalloc/spill.rad +66 -81
47 47
    /// Number of uses (weighted by loop depth).
48 48
    uses: u32,
49 49
}
50 50
51 51
/// Spill decision for a function.
52 -
export record SpillInfo: Copy {
52 +
export record SpillInfo: 'scratch + Copy {
53 53
    /// SSA register mapped to stack slot offset. `-1` means not spilled.
54 -
    slots: *[i32],
54 +
    slots: &'scratch [i32],
55 55
    /// Total spill frame size needed in bytes.
56 56
    frameSize: i32,
57 57
    /// Values that must be allocated in callee-saved registers.
58 -
    calleeClass: bitset::Bitset,
58 +
    calleeClass: &'scratch [u32],
59 59
    /// Maximum SSA register number.
60 60
    maxReg: u32,
61 61
}
62 62
63 63
/// Candidate buffer for spill decisions.
64 64
record Candidates: Copy {
65 -
    entries: [CostEntry; 256],
65 +
    entries: [CostEntry; MAX_CANDIDATES],
66 66
    n: u32,
67 67
}
68 68
69 69
/// Entry for cost sorting.
70 70
record CostEntry: Copy {
71 71
    reg: u32,
72 72
    cost: u32,
73 73
}
74 74
75 -
/// Context for counting register uses.
76 -
record CountCtx: Copy {
77 -
    costs: *unsafe mut [SpillCost],
78 -
    weight: u32,
79 -
}
80 -
81 75
/// Analyze a function and determine which values need spill slots.
82 -
export unsafe fn analyze(
83 -
    func: *unsafe il::Fn,
84 -
    live: &liveness::LiveInfo,
76 +
export unsafe fn analyze 'scratch (
77 +
    func: &il::Fn,
78 +
    live: &liveness::LiveInfo 'scratch,
85 79
    numRegs: u32,
86 80
    numCalleeSaved: u32,
87 81
    slotSize: u32,
88 -
    arena: &mut alloc::Arena
89 -
) -> SpillInfo throws (alloc::AllocError) {
82 +
    storage: &Session 'scratch
83 +
) -> SpillInfo 'scratch throws (alloc::AllocError) {
90 84
    let maxReg = live.maxReg;
91 85
    if maxReg == 0 {
92 -
        let calleeClass = try bitset::allocate(arena, 0);
93 -
        return SpillInfo {
94 -
            slots: &mut [],
86 +
        let calleeClass = try bitset::allocate(storage, 0);
87 +
        return SpillInfo 'scratch {
88 +
            slots: try storage.fill(-1 as i32, 0),
95 89
            frameSize: 0,
96 -
            calleeClass,
90 +
            calleeClass: &calleeClass[..],
97 91
            maxReg: 0,
98 92
        };
99 93
    }
100 94
    // Allocate spill slots array.
101 -
    let slots = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), maxReg) as *mut [i32];
102 -
    for i in 0..maxReg {
103 -
        set slots[i] = -1;
104 -
    }
95 +
    let slots = try storage.fill(-1 as i32, maxReg);
105 96
    // Allocate cost array.
106 -
    let costs = try alloc::allocRawSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *unsafe mut [SpillCost];
107 -
    for i in 0..maxReg {
108 -
        set costs[i] = SpillCost { defs: 0, uses: 0 };
109 -
    }
97 +
    let costs = try storage.fill(SpillCost { defs: 0, uses: 0 }, maxReg);
110 98
    // Phase 1: Calculate spill costs.
111 99
    fillCosts(func, costs);
112 100
113 101
    // Phase 2: Find values that exceed register pressure.
114 -
    let mut spilled = try bitset::allocate(arena, maxReg);
115 -
    let mut calleeClass = try bitset::allocate(arena, maxReg);
116 -
    let mut scratch = try bitset::allocate(arena, maxReg);
102 +
    let spilled = try bitset::allocate(storage, maxReg);
103 +
    let calleeClass = try bitset::allocate(storage, maxReg);
104 +
    let scratch = try bitset::allocate(storage, maxReg);
117 105
118 106
    for b in 0..func.blocks.len {
119 107
        let block = &func.blocks[b];
120 108
121 109
        // Start with live-out set.
122 -
        bitset::copy(&mut scratch, &live.liveOut[b]);
110 +
        bitset::copy(scratch, liveness::liveOutRow(live, b));
123 111
124 112
        // Walk instructions backwards.
125 113
        let mut i = block.instrs.len;
126 114
        while i > 0 {
127 115
            set i -= 1;
128 116
            let instr = block.instrs[i];
129 117
130 118
            // Limit register pressure before processing this instruction.
131 -
            try limitPressure(&mut scratch, &mut spilled, costs, numRegs);
119 +
            try limitPressure(scratch, spilled, costs, numRegs);
132 120
133 121
            // Enforce cross-call pressure at call sites.
134 122
            if il::isCall(instr) {
135 123
                try limitCrossCallPressure(
136 -
                    &mut scratch, &mut spilled, costs,
137 -
                    &mut calleeClass, numCalleeSaved, il::instrDst(instr)
124 +
                    scratch, spilled, costs,
125 +
                    calleeClass, numCalleeSaved, il::instrDst(instr)
138 126
                );
139 127
            }
140 128
            // Remove definition from live set.
141 129
            if let dst = il::instrDst(instr) {
142 -
                bitset::clear(&mut scratch, dst.n);
130 +
                bitset::clear(scratch, dst.n);
143 131
            }
144 132
            // Add uses to live set.
145 -
            il::forEachReg(instr, addRegToSetCallback, &mut scratch as &mut opaque);
133 +
            let mut registers = il::registers(&instr);
134 +
            while let reg = il::nextReg(&mut registers, &instr) {
135 +
                bitset::put(scratch, reg.n);
136 +
            }
146 137
        }
147 138
        // Also limit pressure at block entry.
148 -
        try limitPressure(&mut scratch, &mut spilled, costs, numRegs);
139 +
        try limitPressure(scratch, spilled, costs, numRegs);
149 140
    }
150 141
151 142
    // Phase 3: Enforce global callee-class limit.
152 143
    // The per-call-site limit may leave the callee-class set larger than
153 144
    // `numCalleeSaved` when different call sites keep different subsets.
154 145
    // Spill excess values to guarantee the assignment phase always finds
155 146
    // a callee-saved register for cross-call values.
156 -
    let calleeCount = bitset::count(&calleeClass);
147 +
    let calleeCount = bitset::count(calleeClass);
157 148
    if calleeCount > numCalleeSaved {
158 -
        let mut it = bitset::iter(&calleeClass);
149 +
        let mut it = bitset::iter(calleeClass);
159 150
        for _ in 0..(calleeCount - numCalleeSaved) {
160 -
            if let reg = bitset::iterNext(&mut it) {
161 -
                bitset::clear(&mut calleeClass, reg);
162 -
                bitset::put(&mut spilled, reg);
151 +
            if let reg = bitset::iterNext(&mut it, calleeClass) {
152 +
                bitset::clear(calleeClass, reg);
153 +
                bitset::put(spilled, reg);
163 154
            } else {
164 155
                panic "spill: count > 0 but no set bits found";
165 156
            }
166 157
        }
167 158
    }
168 159
169 160
    // Phase 4: Assign stack slots to spilled values.
170 161
    let mut frameSize: i32 = 0;
171 -
    let mut it = bitset::iter(&spilled);
162 +
    let mut it = bitset::iter(spilled);
172 163
173 -
    while let n = bitset::iterNext(&mut it) {
164 +
    while let n = bitset::iterNext(&mut it, spilled) {
174 165
        set slots[n] = frameSize;
175 166
        set frameSize += slotSize as i32;
176 167
    }
177 -
    return SpillInfo { slots, frameSize, calleeClass, maxReg };
168 +
    return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg };
178 169
}
179 170
180 171
/// Calculate spill costs for all registers, weighted by loop depth.
181 -
unsafe fn fillCosts(func: *unsafe il::Fn, costs: *unsafe mut [SpillCost]) {
172 +
unsafe fn fillCosts(func: &il::Fn, costs: &mut [SpillCost]) {
182 173
    for b in 0..func.blocks.len {
183 174
        let block = &func.blocks[b];
184 175
185 176
        // Exponential weight for loop depth, capped to avoid overflow.
186 177
        let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth;
191 182
            if p.value.n < costs.len {
192 183
                set costs[p.value.n].defs = costs[p.value.n].defs + weight;
193 184
            }
194 185
        }
195 186
        // Count instruction defs and uses.
196 -
        for i in 0..block.instrs.len {
197 -
            let instr = block.instrs[i];
187 +
        for instr in block.instrs {
198 188
199 189
            // Count definition.
200 190
            if let dst = il::instrDst(instr) {
201 191
                if dst.n < costs.len {
202 192
                    set costs[dst.n].defs = costs[dst.n].defs + weight;
203 193
                }
204 194
            }
205 195
            // Count uses.
206 -
            let mut ctx = CountCtx { costs, weight };
207 -
            il::forEachReg(instr, countRegUseCallback, &mut ctx as &mut opaque);
196 +
            let mut registers = il::registers(&instr);
197 +
            while let reg = il::nextReg(&mut registers, &instr) {
198 +
                countRegUse(reg, &mut costs[..], weight);
199 +
            }
208 200
        }
209 201
    }
210 202
}
211 203
212 204
/// Sort candidates by cost (ascending) using insertion sort, then spill
213 205
/// the cheapest `excess` values: set in `spilled`, clear in `source`.
214 -
unsafe fn spillCheapest(
206 +
fn spillCheapest(
215 207
    c: &mut Candidates,
216 208
    excess: u32,
217 -
    source: &mut bitset::Bitset,
218 -
    spilled: &mut bitset::Bitset
209 +
    source: &mut [u32],
210 +
    spilled: &mut [u32]
219 211
) {
220 212
    // Insertion sort ascending by cost.
221 213
    for i in 1..c.n {
222 214
        let key = c.entries[i];
223 215
        let mut j: u32 = i;
233 225
        bitset::clear(source, c.entries[i].reg);
234 226
    }
235 227
}
236 228
237 229
/// Collect all values from a bitset into a candidates buffer with their costs.
238 -
unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *unsafe [SpillCost]) -> Candidates throws (alloc::AllocError) {
230 +
unsafe fn collectCandidates(bs: &[u32], costs: &[SpillCost]) -> Candidates throws (alloc::AllocError) {
239 231
    let mut c = Candidates { entries: undefined, n: 0 };
240 232
    let mut it = bitset::iter(bs);
241 -
    while let reg = bitset::iterNext(&mut it) {
242 -
        if c.n == MAX_CANDIDATES { throw alloc::AllocError::OutOfMemory; }
233 +
    while let reg = bitset::iterNext(&mut it, bs) {
234 +
        if c.n == MAX_CANDIDATES {
235 +
            throw alloc::AllocError::OutOfMemory;
236 +
        }
243 237
        if reg < costs.len {
244 238
            set c.entries[c.n] = CostEntry { reg, cost: costs[reg].defs + costs[reg].uses };
245 239
            set c.n += 1;
246 240
        }
247 241
    }
248 242
    return c;
249 243
}
250 244
251 245
/// Limit register pressure by marking low-cost values as spilled.
252 246
unsafe fn limitPressure(
253 -
    live: &mut bitset::Bitset,
254 -
    spilled: &mut bitset::Bitset,
255 -
    costs: *unsafe [SpillCost],
247 +
    live: &mut [u32],
248 +
    spilled: &mut [u32],
249 +
    costs: &[SpillCost],
256 250
    numRegs: u32
257 251
) throws (alloc::AllocError) {
258 252
    let liveCount = bitset::count(live);
259 253
    if liveCount <= numRegs {
260 254
        return;
275 269
///
276 270
/// At a call site, every live value must survive the call in a callee-saved
277 271
/// register. If the count exceeds `numCalleeSaved`, spill the cheapest
278 272
/// crossing values.
279 273
unsafe fn limitCrossCallPressure(
280 -
    live: &mut bitset::Bitset,
281 -
    spilled: &mut bitset::Bitset,
282 -
    costs: *unsafe [SpillCost],
283 -
    calleeClass: &mut bitset::Bitset,
274 +
    live: &mut [u32],
275 +
    spilled: &mut [u32],
276 +
    costs: &[SpillCost],
277 +
    calleeClass: &mut [u32],
284 278
    numCalleeSaved: u32,
285 279
    callDst: ?il::Reg
286 280
) throws (alloc::AllocError) {
287 281
    // Collect crossing candidates: live values excluding the call destination.
288 -
    let mut candidates: [CostEntry; 256] = undefined;
282 +
    let mut candidates: [CostEntry; MAX_CANDIDATES] = undefined;
289 283
    let mut numCandidates: u32 = 0;
290 284
    let mut it = bitset::iter(live);
291 -
    while let n = bitset::iterNext(&mut it) {
285 +
    while let n = bitset::iterNext(&mut it, live) {
292 286
        if not isCallDst(callDst, n) and n < costs.len {
293 -
            if numCandidates == MAX_CANDIDATES { throw alloc::AllocError::OutOfMemory; }
287 +
            if numCandidates == MAX_CANDIDATES {
288 +
                throw alloc::AllocError::OutOfMemory;
289 +
            }
294 290
            set candidates[numCandidates] = CostEntry {
295 291
                reg: n,
296 292
                cost: costs[n].defs + costs[n].uses,
297 293
            };
298 294
            set numCandidates += 1;
303 299
        let mut c = Candidates { entries: candidates, n: numCandidates };
304 300
        spillCheapest(&mut c, numCandidates - numCalleeSaved, live, spilled);
305 301
    }
306 302
    // Mark crossing values that remain after spilling as callee-saved class.
307 303
    let mut it2 = bitset::iter(live);
308 -
    while let n = bitset::iterNext(&mut it2) {
304 +
    while let n = bitset::iterNext(&mut it2, live) {
309 305
        if not isCallDst(callDst, n) {
310 306
            bitset::put(calleeClass, n);
311 307
        }
312 308
    }
313 309
}
314 310
315 -
/// Callback for [`il::forEachReg`]: increments use count for register.
316 -
unsafe fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) {
317 -
    let weight = (ctxPtr as &mut CountCtx).weight;
318 -
    countRegUse(reg, &mut (ctxPtr as &mut CountCtx).costs[..], weight);
319 -
}
320 -
321 311
/// Add the block weight to the register use count.
322 312
fn countRegUse(reg: il::Reg, costs: &mut [SpillCost], weight: u32) {
323 -
    assert reg.n < costs.len, "countRegUseCallback: register out of bounds";
313 +
    assert reg.n < costs.len, "countRegUse: register out of bounds";
324 314
    set costs[reg.n].uses += weight;
325 315
}
326 316
327 -
/// Callback for [`il::forEachReg`]: adds register to live set.
328 -
unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) {
329 -
    bitset::put(ctx as &mut bitset::Bitset, reg.n);
330 -
}
331 -
332 317
/// Check if a register is spilled.
333 -
export fn isSpilled(info: &SpillInfo, reg: il::Reg) -> bool {
318 +
export fn isSpilled 'scratch (info: &SpillInfo 'scratch, reg: il::Reg) -> bool {
334 319
    if reg.n >= info.maxReg {
335 320
        return false;
336 321
    }
337 322
    return info.slots[reg.n] >= 0;
338 323
}
339 324
340 325
/// Get spill slot offset for a register, or `nil` if not spilled.
341 -
export fn spillSlot(info: &SpillInfo, reg: il::Reg) -> ?i32 {
326 +
export fn spillSlot 'scratch (info: &SpillInfo 'scratch, reg: il::Reg) -> ?i32 {
342 327
    if isSpilled(info, reg) {
343 328
        return info.slots[reg.n];
344 329
    }
345 330
    return nil;
346 331
}
lib/std/lang/il.rad +170 -69
58 58
// TODO: Labels should have their own type.
59 59
// TODO: Blocks should have an instruction in `Instr`.
60 60
61 61
export mod printer;
62 62
export mod binary;
63 +
@test mod tests;
63 64
64 65
use std::mem;
65 66
use std::lang::alloc;
66 67
67 68
/// Source location for debug info.
296 297
    /// Environment break: `ebreak;`.
297 298
    /// Triggers a breakpoint exception for debugging.
298 299
    Ebreak,
299 300
    /// Full acquire/release memory fence.
300 301
    MemoryFence,
302 +
    /// Read one register through a live Device handle.
303 +
    DeviceRead {
304 +
        /// Register access width.
305 +
        typ: Type,
306 +
        /// Destination for the zero-extended register value.
307 +
        dst: Reg,
308 +
        /// Domain-relative Device capability.
309 +
        handle: Val,
310 +
        /// Byte offset within the device region.
311 +
        offset: Val,
312 +
    },
313 +
    /// Write one register through a live Device handle.
314 +
    DeviceWrite {
315 +
        /// Register access width.
316 +
        typ: Type,
317 +
        /// Domain-relative Device capability.
318 +
        handle: Val,
319 +
        /// Byte offset within the device region.
320 +
        offset: Val,
321 +
        /// Value whose low bits are written at the selected width.
322 +
        value: Val,
323 +
    },
301 324
}
302 325
303 326
//////////////////////////
304 327
// Blocks and Functions //
305 328
//////////////////////////
398 421
399 422
///////////////////////
400 423
// Utility Functions //
401 424
///////////////////////
402 425
426 +
/// Compiler-generated metadata call for one MMIO access.
427 +
/// a0 is the handle, a1 the offset, and a2 the byte width plus bit 8 for a write.
428 +
/// a3 holds a write value and is preserved. Success returns the address in a0.
429 +
/// Any failure terminates the caller before the user-mode register access.
430 +
export constant DEVICE_ACCESS: u32 = 74;
431 +
403 432
/// Get the destination register of an instruction, if any.
404 433
export fn instrDst(instr: Instr) -> ?Reg {
405 434
    match instr {
406 435
        case Instr::Reserve { dst, .. } => return dst,
407 436
        case Instr::Load { dst, .. } => return dst,
411 440
        case Instr::UnOp { dst, .. } => return dst,
412 441
        case Instr::Zext { dst, .. } => return dst,
413 442
        case Instr::Sext { dst, .. } => return dst,
414 443
        case Instr::Call { dst, .. } => return dst,
415 444
        case Instr::Ecall { dst, .. } => return dst,
445 +
        case Instr::DeviceRead { dst, .. } => return dst,
416 446
        else => return nil,
417 447
    }
418 448
}
419 449
420 450
/// Check if an instruction is a function call.
421 451
export fn isCall(instr: Instr) -> bool {
422 452
    match instr {
423 453
        case Instr::Call { .. },
424 -
             Instr::Ecall { .. } => return true,
454 +
             Instr::Ecall { .. }, Instr::DeviceRead { .. }, Instr::DeviceWrite { .. } => return true,
425 455
        else => return false,
426 456
    }
427 457
}
428 458
429 -
/// Call a function for each register used by an instruction.
430 -
/// This is called by the register allocator to analyze register usage.
431 -
export unsafe fn forEachReg(instr: Instr, f: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
432 -
    match instr {
433 -
        case Instr::Reserve { size, .. } =>
434 -
            withReg(size, f, ctx),
435 -
        case Instr::Load { src, .. } => f(src, ctx),
436 -
        case Instr::Sload { src, .. } => f(src, ctx),
459 +
/// Position within an instruction's source operands.
460 +
export record RegCursor: Copy {
461 +
    /// Registers in the instruction's fixed operands, in operand order.
462 +
    fixed: [Reg; 5],
463 +
    /// Number of initialized fixed registers.
464 +
    count: u32,
465 +
    /// Next fixed register.
466 +
    next: u32,
467 +
    /// Next argument in the current argument group.
468 +
    argument: u32,
469 +
    /// Argument group: zero for the first group, then one per switch case.
470 +
    branch: u32,
471 +
    /// Whether the instruction has variable argument groups left to scan.
472 +
    arguments: bool,
473 +
}
474 +
475 +
/// Start a source-register scan in instruction operand order.
476 +
export unsafe fn registers(instr: &Instr) -> RegCursor {
477 +
    let mut cursor = RegCursor { fixed: undefined, count: 0, next: 0, argument: 0, branch: 0, arguments: false };
478 +
    match *instr {
479 +
        case Instr::Reserve { size, .. } => addOperand(&mut cursor, size),
480 +
        case Instr::Load { src, .. } => addOperand(&mut cursor, Val::Reg(src)),
481 +
        case Instr::Sload { src, .. } => addOperand(&mut cursor, Val::Reg(src)),
437 482
        case Instr::Store { src, dst, .. } => {
438 -
            withReg(src, f, ctx);
439 -
            f(dst, ctx);
440 -
        },
483 +
            addOperand(&mut cursor, src);
484 +
            addOperand(&mut cursor, Val::Reg(dst));
485 +
        }
441 486
        case Instr::Blit { dst, src, size } => {
442 -
            f(dst, ctx);
443 -
            f(src, ctx);
444 -
            withReg(size, f, ctx);
445 -
        },
446 -
        case Instr::Copy { val, .. } =>
447 -
            withReg(val, f, ctx),
487 +
            addOperand(&mut cursor, Val::Reg(dst));
488 +
            addOperand(&mut cursor, Val::Reg(src));
489 +
            addOperand(&mut cursor, size);
490 +
        }
491 +
        case Instr::Copy { val, .. } => addOperand(&mut cursor, val),
448 492
        case Instr::BinOp { a, b, .. } => {
449 -
            withReg(a, f, ctx);
450 -
            withReg(b, f, ctx);
451 -
        },
452 -
        case Instr::UnOp { a, .. } =>
453 -
            withReg(a, f, ctx),
454 -
        case Instr::Zext { val, .. } =>
455 -
            withReg(val, f, ctx),
456 -
        case Instr::Sext { val, .. } =>
457 -
            withReg(val, f, ctx),
458 -
        case Instr::Call { func, args, .. } => {
459 -
            withReg(func, f, ctx);
460 -
            for arg in args {
461 -
                withReg(arg, f, ctx);
462 -
            }
463 -
        },
493 +
            addOperand(&mut cursor, a);
494 +
            addOperand(&mut cursor, b);
495 +
        }
496 +
        case Instr::UnOp { a, .. } => addOperand(&mut cursor, a),
497 +
        case Instr::Zext { val, .. } => addOperand(&mut cursor, val),
498 +
        case Instr::Sext { val, .. } => addOperand(&mut cursor, val),
499 +
        case Instr::Call { func, .. } => {
500 +
            addOperand(&mut cursor, func);
501 +
            set cursor.arguments = true;
502 +
        }
464 503
        case Instr::Ret { val } => {
465 -
            if let v = val {
466 -
                withReg(v, f, ctx);
504 +
            if let value = val {
505 +
                addOperand(&mut cursor, value);
506 +
            }
507 +
        }
508 +
        case Instr::Jmp { .. } => set cursor.arguments = true,
509 +
        case Instr::Br { a, b, .. } => {
510 +
            addOperand(&mut cursor, a);
511 +
            addOperand(&mut cursor, b);
512 +
            set cursor.arguments = true;
513 +
        }
514 +
        case Instr::Switch { val, .. } => {
515 +
            addOperand(&mut cursor, val);
516 +
            set cursor.arguments = true;
517 +
        }
518 +
        case Instr::Ecall { num, a0, a1, a2, a3, .. } => {
519 +
            addOperand(&mut cursor, num);
520 +
            addOperand(&mut cursor, a0);
521 +
            addOperand(&mut cursor, a1);
522 +
            addOperand(&mut cursor, a2);
523 +
            addOperand(&mut cursor, a3);
524 +
        }
525 +
        case Instr::DeviceRead { handle, offset, .. } => {
526 +
            addOperand(&mut cursor, handle);
527 +
            addOperand(&mut cursor, offset);
528 +
        }
529 +
        case Instr::DeviceWrite { handle, offset, value, .. } => {
530 +
            addOperand(&mut cursor, handle);
531 +
            addOperand(&mut cursor, offset);
532 +
            addOperand(&mut cursor, value);
533 +
        }
534 +
        case Instr::Unreachable, Instr::Ebreak, Instr::MemoryFence => {
535 +
        }
536 +
    }
537 +
    return cursor;
538 +
}
539 +
540 +
/// Append a fixed register operand to the cursor.
541 +
fn addOperand(cursor: &mut RegCursor, value: Val) {
542 +
    if let case Val::Reg(reg) = value {
543 +
        set cursor.fixed[cursor.count] = reg;
544 +
        set cursor.count += 1;
545 +
    }
546 +
}
547 +
548 +
/// Return the next source register, including repeated uses.
549 +
/// Supply the unchanged instruction for every step of a scan.
550 +
export unsafe fn nextReg(cursor: &mut RegCursor, instr: &Instr) -> ?Reg {
551 +
    if cursor.next < cursor.count {
552 +
        let reg = cursor.fixed[cursor.next];
553 +
        set cursor.next += 1;
554 +
        return reg;
555 +
    }
556 +
    if not cursor.arguments {
557 +
        return nil;
558 +
    }
559 +
    match *instr {
560 +
        case Instr::Call { args, .. } => {
561 +
            if let reg = nextArgument(cursor, args) {
562 +
                return reg;
467 563
            }
468 -
        },
564 +
        }
469 565
        case Instr::Jmp { args, .. } => {
470 -
            for arg in args {
471 -
                withReg(arg, f, ctx);
566 +
            if let reg = nextArgument(cursor, args) {
567 +
                return reg;
472 568
            }
473 -
        },
474 -
        case Instr::Br { a, b, thenArgs, elseArgs, .. } => {
475 -
            withReg(a, f, ctx);
476 -
            withReg(b, f, ctx);
477 -
            for arg in thenArgs {
478 -
                withReg(arg, f, ctx);
569 +
        }
570 +
        case Instr::Br { thenArgs, elseArgs, .. } => {
571 +
            if cursor.branch == 0 {
572 +
                if let reg = nextArgument(cursor, thenArgs) {
573 +
                    return reg;
574 +
                }
575 +
                set cursor.branch = 1;
576 +
                set cursor.argument = 0;
479 577
            }
480 -
            for arg in elseArgs {
481 -
                withReg(arg, f, ctx);
578 +
            if let reg = nextArgument(cursor, elseArgs) {
579 +
                return reg;
482 580
            }
483 -
        },
484 -
        case Instr::Switch { val, defaultArgs, cases, .. } => {
485 -
            withReg(val, f, ctx);
486 -
            for arg in defaultArgs {
487 -
                withReg(arg, f, ctx);
581 +
        }
582 +
        case Instr::Switch { defaultArgs, cases, .. } => {
583 +
            if cursor.branch == 0 {
584 +
                if let reg = nextArgument(cursor, defaultArgs) {
585 +
                    return reg;
586 +
                }
587 +
                set cursor.branch = 1;
588 +
                set cursor.argument = 0;
488 589
            }
489 -
            for c in cases {
490 -
                for arg in c.args {
491 -
                    withReg(arg, f, ctx);
590 +
            while cursor.branch - 1 < cases.len {
591 +
                if let reg = nextArgument(cursor, cases[cursor.branch - 1].args) {
592 +
                    return reg;
492 593
                }
594 +
                set cursor.branch += 1;
595 +
                set cursor.argument = 0;
493 596
            }
494 -
        },
495 -
        case Instr::Ecall { num, a0, a1, a2, a3, .. } => {
496 -
            withReg(num, f, ctx);
497 -
            withReg(a0, f, ctx);
498 -
            withReg(a1, f, ctx);
499 -
            withReg(a2, f, ctx);
500 -
            withReg(a3, f, ctx);
501 -
        },
502 -
        case Instr::Unreachable,
503 -
             Instr::Ebreak,
504 -
             Instr::MemoryFence => {},
597 +
        }
598 +
        else => panic "nextReg: expected instruction argument groups",
505 599
    }
600 +
    set cursor.arguments = false;
601 +
    return nil;
506 602
}
507 603
508 -
/// Call callback if value is a register.
509 -
unsafe fn withReg(val: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
510 -
    if let case Val::Reg(r) = val {
511 -
        callback(r, ctx);
604 +
/// Scan an argument group from the cursor's current position.
605 +
unsafe fn nextArgument(cursor: &mut RegCursor, args: *unsafe [Val]) -> ?Reg {
606 +
    while cursor.argument < args.len {
607 +
        let value = args[cursor.argument];
608 +
        set cursor.argument += 1;
609 +
        if let case Val::Reg(reg) = value {
610 +
            return reg;
611 +
        }
512 612
    }
613 +
    return nil;
513 614
}
lib/std/lang/il/binary.rad +4 -0
77 77
export constant INSTR_ECALL: u8 = 16;
78 78
/// Wire tag for instruction Ebreak.
79 79
export constant INSTR_EBREAK: u8 = 17;
80 80
/// Wire tag for instruction MemoryFence.
81 81
export constant INSTR_MEMORYFENCE: u8 = 18;
82 +
/// Wire tag for a checked device read.
83 +
export constant INSTR_DEVICE_READ: u8 = 19;
84 +
/// Wire tag for a checked device write.
85 +
export constant INSTR_DEVICE_WRITE: u8 = 20;
82 86
83 87
/// Wire tag for data Val.
84 88
export constant DATA_VAL: u8 = 0;
85 89
/// Wire tag for data Sym.
86 90
export constant DATA_SYM: u8 = 1;
lib/std/lang/il/binary/collect.rad +12 -3
72 72
/// Collect names referenced by one IL value.
73 73
unsafe fn value(names: &mut Names, owner: *[u8], item: il::Val) throws (binary::Error) {
74 74
    match item {
75 75
        case il::Val::DataSym(name) => try reference(names, owner, name),
76 76
        case il::Val::FnAddr(name) => try reference(names, owner, name),
77 -
        else => {},
77 +
        else => {
78 +
        },
78 79
    }
79 80
}
80 81
81 82
/// Collect names from an instruction's variable-length operand sequence.
82 83
unsafe fn values(names: &mut Names, owner: *[u8], items: &[il::Val]) throws (binary::Error) {
127 128
            try value(names, owner, a0);
128 129
            try value(names, owner, a1);
129 130
            try value(names, owner, a2);
130 131
            try value(names, owner, a3);
131 132
        },
133 +
        case il::Instr::DeviceRead { handle, offset, .. } => {
134 +
            try value(names, owner, handle); try value(names, owner, offset);
135 +
        },
136 +
        case il::Instr::DeviceWrite { handle, offset, value: source, .. } => {
137 +
            try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source);
138 +
        },
132 139
        case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable,
133 -
             il::Instr::Ebreak, il::Instr::MemoryFence => {},
140 +
             il::Instr::Ebreak, il::Instr::MemoryFence => {
141 +
             },
134 142
    }
135 143
}
136 144
137 145
/// Check that a definition belongs to the selected package.
138 146
unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) {
165 173
        try definition(names, owner, item.name);
166 174
        for data in item.values {
167 175
            match data.item {
168 176
                case il::DataItem::Sym(name) => try reference(names, owner, name),
169 177
                case il::DataItem::Fn(name) => try reference(names, owner, name),
170 -
                else => {},
178 +
                else => {
179 +
                },
171 180
            }
172 181
        }
173 182
    }
174 183
    for func in program.fns {
175 184
        try definition(names, owner, func.name);
lib/std/lang/il/binary/decodeTests.rad +3 -1
347 347
348 348
/// Copy data fixtures into stable storage for the package descriptor.
349 349
unsafe fn retainData(items: &[il::Data]) -> *[il::Data] {
350 350
    unsafe static DATA: [il::Data; 2] = undefined;
351 351
    assert items.len <= DATA.len;
352 -
    for item, i in items { set DATA[i] = item; }
352 +
    for item, i in items {
353 +
        set DATA[i] = item;
354 +
    }
353 355
    return &DATA[..items.len];
354 356
}
lib/std/lang/il/binary/program.rad +12 -4
116 116
        let mut extent: u64 = 0;
117 117
        for j in 0..count {
118 118
            let value = try reader::dataValue(input);
119 119
            let mut width: u32 = 0;
120 120
            match value.item {
121 -
                case il::DataItem::Val { typ, .. } => { set width = il::typeSize(typ); },
122 -
                case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; },
123 -
                case il::DataItem::Str(text) => { set width = text.len; },
124 -
                case il::DataItem::Undef => { set width = 1; },
121 +
                case il::DataItem::Val { typ, .. } => {
122 +
                    set width = il::typeSize(typ);
123 +
                },
124 +
                case il::DataItem::Sym(_), il::DataItem::Fn(_) => {
125 +
                    set width = 8;
126 +
                },
127 +
                case il::DataItem::Str(text) => {
128 +
                    set width = text.len;
129 +
                },
130 +
                case il::DataItem::Undef => {
131 +
                    set width = 1;
132 +
                },
125 133
            }
126 134
            set extent += width as u64 * value.count as u64;
127 135
            if extent > size as u64 {
128 136
                throw binary::Error::Invalid;
129 137
            }
lib/std/lang/il/binary/reader.rad +23 -4
314 314
            let va1 = try val(input);
315 315
            let va2 = try val(input);
316 316
            let va3 = try val(input);
317 317
            return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 };
318 318
        },
319 +
        case super::INSTR_DEVICE_READ => {
320 +
            let t = try typ(input);
321 +
            let dst = try reg(input);
322 +
            let handle = try val(input); let offset = try val(input);
323 +
            return il::Instr::DeviceRead { typ: t, dst, handle, offset };
324 +
        },
325 +
        case super::INSTR_DEVICE_WRITE => {
326 +
            let t = try typ(input);
327 +
            let handle = try val(input); let offset = try val(input); let value = try val(input);
328 +
            return il::Instr::DeviceWrite { typ: t, handle, offset, value };
329 +
        },
319 330
        case super::INSTR_EBREAK => {
320 331
            return il::Instr::Ebreak;
321 332
        },
322 333
        case super::INSTR_MEMORYFENCE => {
323 334
            return il::Instr::MemoryFence;
334 345
        case super::DATA_VAL => {
335 346
            let t = try typ(input);
336 347
            let n = try integer(input, il::typeSize(t));
337 348
            set item = il::DataItem::Val { typ: t, val: n as i64 };
338 349
        },
339 -
        case super::DATA_SYM => { set item = il::DataItem::Sym(try symbol(input)); },
340 -
        case super::DATA_FN => { set item = il::DataItem::Fn(try symbol(input)); },
341 -
        case super::DATA_STR => { set item = il::DataItem::Str(try bytes(input)); },
342 -
        case super::DATA_UNDEF => { set item = il::DataItem::Undef; },
350 +
        case super::DATA_SYM => {
351 +
            set item = il::DataItem::Sym(try symbol(input));
352 +
        },
353 +
        case super::DATA_FN => {
354 +
            set item = il::DataItem::Fn(try symbol(input));
355 +
        },
356 +
        case super::DATA_STR => {
357 +
            set item = il::DataItem::Str(try bytes(input));
358 +
        },
359 +
        case super::DATA_UNDEF => {
360 +
            set item = il::DataItem::Undef;
361 +
        },
343 362
        else => throw binary::Error::Invalid,
344 363
    }
345 364
    let n = try integer(input, 4) as u32;
346 365
    return il::DataValue { item, count: n };
347 366
}
lib/std/lang/il/binary/tests.rad +78 -22
12 12
13 13
/// Check little-endian encoding for every integer width.
14 14
@test unsafe fn integers() throws (testing::TestError) {
15 15
    let mut buffer: [u8; 15] = [0; 15];
16 16
    let mut out = writer::new(&mut buffer[..], &[]);
17 -
    try writer::integer(&mut out, 0x12, 1) catch { throw testing::TestError::Failed; };
18 -
    try writer::integer(&mut out, 0x3456, 2) catch { throw testing::TestError::Failed; };
19 -
    try writer::integer(&mut out, 0x789abcde, 4) catch { throw testing::TestError::Failed; };
20 -
    try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { throw testing::TestError::Failed; };
17 +
    try writer::integer(&mut out, 0x12, 1) catch {
18 +
        throw testing::TestError::Failed;
19 +
    };
20 +
    try writer::integer(&mut out, 0x3456, 2) catch {
21 +
        throw testing::TestError::Failed;
22 +
    };
23 +
    try writer::integer(&mut out, 0x789abcde, 4) catch {
24 +
        throw testing::TestError::Failed;
25 +
    };
26 +
    try writer::integer(&mut out, 0x0123456789abcdef, 8) catch {
27 +
        throw testing::TestError::Failed;
28 +
    };
21 29
    try testing::expectBytesEq(&buffer[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd,
22 30
        0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
23 31
}
24 32
25 33
/// Compare one instruction with its fixed wire representation.
26 34
unsafe fn instruction(item: il::Instr, expected: &[u8]) throws (testing::TestError) {
27 35
    let mut buffer: [u8; 256] = [0; 256];
28 36
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
29 -
    try writer::instr(&mut out, item) catch { throw testing::TestError::Failed; };
37 +
    try writer::instr(&mut out, item) catch {
38 +
        throw testing::TestError::Failed;
39 +
    };
30 40
    try testing::expectBytesEq(&buffer[..out.offset], expected);
31 41
    for capacity in 0..expected.len {
32 42
        let mut short = writer::new(&mut buffer[..capacity], &["data", "fn"]);
33 43
        let mut failed = false;
34 44
        try writer::instr(&mut short, item) catch err {
41 51
    let memory = &mut MEMORY[..512];
42 52
    let mut arena = alloc::new(&mut memory[..]);
43 53
    let mut input = reader::new(expected, &mut arena, &["data", "fn"]);
44 54
    set input.registers = 16;
45 55
    set input.blocks = 4;
46 -
    let decoded = try reader::instr(&mut input) catch { throw testing::TestError::Failed; };
56 +
    let decoded = try reader::instr(&mut input) catch {
57 +
        throw testing::TestError::Failed;
58 +
    };
47 59
    try testing::expect(input.offset == expected.len);
48 60
    set out.offset = 0;
49 -
    try writer::instr(&mut out, decoded) catch { throw testing::TestError::Failed; };
61 +
    try writer::instr(&mut out, decoded) catch {
62 +
        throw testing::TestError::Failed;
63 +
    };
50 64
    try testing::expectBytesEq(&buffer[..out.offset], expected);
51 65
    for length in 0..expected.len {
52 66
        alloc::reset(&mut arena);
53 67
        set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]);
54 68
        set input.registers = 16;
131 145
        dst: il::Reg { n: 1 }, num: il::Val::Undef, a0: il::Val::Undef, a1: il::Val::Undef,
132 146
        a2: il::Val::Undef, a3: il::Val::Undef,
133 147
    }, &[16, 1, 0, 0, 0, 4, 4, 4, 4, 4]);
134 148
    try instruction(il::Instr::Ebreak, &[17]);
135 149
    try instruction(il::Instr::MemoryFence, &[18]);
150 +
    for typ in [il::Type::W8, il::Type::W16, il::Type::W32, il::Type::W64] {
151 +
        let mut read: [u8; 8] = [19, 0, 1, 0, 0, 0, 4, 4];
152 +
        let mut write: [u8; 5] = [20, 0, 4, 4, 4];
153 +
        set read[1] = il::typeSize(typ) as u8;
154 +
        set write[1] = il::typeSize(typ) as u8;
155 +
        try instruction(il::Instr::DeviceRead {
156 +
            typ, dst: il::Reg { n: 1 }, handle: il::Val::Undef, offset: il::Val::Undef,
157 +
        }, &read[..]);
158 +
        try instruction(il::Instr::DeviceWrite {
159 +
            typ, handle: il::Val::Undef, offset: il::Val::Undef, value: il::Val::Undef,
160 +
        }, &write[..]);
161 +
    }
136 162
}
137 163
138 164
/// Check every value tag and empty and nonempty sequences.
139 165
@test unsafe fn values() throws (testing::TestError) {
140 166
    let mut buffer: [u8; 64] = [0; 64];
141 167
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
142 -
    try writer::values(&mut out, &[]) catch { throw testing::TestError::Failed; };
168 +
    try writer::values(&mut out, &[]) catch {
169 +
        throw testing::TestError::Failed;
170 +
    };
143 171
    try writer::values(&mut out, &[
144 172
        il::Val::Reg(il::Reg { n: 0x12345678 }),
145 173
        il::Val::Imm(-2), il::Val::DataSym("data"),
146 174
        il::Val::FnAddr("fn"), il::Val::Undef,
147 -
    ]) catch { throw testing::TestError::Failed; };
175 +
    ]) catch {
176 +
        throw testing::TestError::Failed;
177 +
    };
148 178
    try testing::expectBytesEq(&buffer[..out.offset], &[
149 179
        0, 0, 0, 0, 5, 0, 0, 0,
150 180
        0, 0x78, 0x56, 0x34, 0x12,
151 181
        1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
152 182
        2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4,
153 183
    ]);
154 184
    let memory = &mut MEMORY[..256];
155 185
    let mut arena = alloc::new(&mut memory[..]);
156 186
    let mut input = reader::new(&buffer[..out.offset], &mut arena, &["data", "fn"]);
157 187
    set input.registers = 0x12345679;
158 -
    let empty = try reader::values(&mut input) catch { throw testing::TestError::Failed; };
159 -
    let decoded = try reader::values(&mut input) catch { throw testing::TestError::Failed; };
188 +
    let empty = try reader::values(&mut input) catch {
189 +
        throw testing::TestError::Failed;
190 +
    };
191 +
    let decoded = try reader::values(&mut input) catch {
192 +
        throw testing::TestError::Failed;
193 +
    };
160 194
    try testing::expect(empty.len == 0);
161 195
    try testing::expect(decoded.len == 5);
162 196
    try testing::expect(input.offset == out.offset);
163 197
    let mut repeated: [u8; 64] = [0; 64];
164 198
    let mut copy = writer::new(&mut repeated[..], &["data", "fn"]);
165 -
    try writer::values(&mut copy, empty) catch { throw testing::TestError::Failed; };
166 -
    try writer::values(&mut copy, decoded) catch { throw testing::TestError::Failed; };
199 +
    try writer::values(&mut copy, empty) catch {
200 +
        throw testing::TestError::Failed;
201 +
    };
202 +
    try writer::values(&mut copy, decoded) catch {
203 +
        throw testing::TestError::Failed;
204 +
    };
167 205
    try testing::expectBytesEq(&buffer[..out.offset], &repeated[..copy.offset]);
168 206
169 207
}
170 208
171 209
/// Check initializer bytes and repetition counts.
172 210
@test unsafe fn initializers() throws (testing::TestError) {
173 211
    let mut buffer: [u8; 128] = [0; 128];
174 212
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
175 213
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8,
176 -
        val: -1 }, count: 0 }) catch { throw testing::TestError::Failed; };
214 +
        val: -1 }, count: 0 }) catch {
215 +
            throw testing::TestError::Failed;
216 +
        };
177 217
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16,
178 -
        val: -2 }, count: 1 }) catch { throw testing::TestError::Failed; };
218 +
        val: -2 }, count: 1 }) catch {
219 +
            throw testing::TestError::Failed;
220 +
        };
179 221
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32,
180 -
        val: -3 }, count: 2 }) catch { throw testing::TestError::Failed; };
222 +
        val: -3 }, count: 2 }) catch {
223 +
            throw testing::TestError::Failed;
224 +
        };
181 225
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64,
182 -
        val: -4 }, count: 3 }) catch { throw testing::TestError::Failed; };
226 +
        val: -4 }, count: 3 }) catch {
227 +
            throw testing::TestError::Failed;
228 +
        };
183 229
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 })
184 -
        catch { throw testing::TestError::Failed; };
230 +
        catch {
231 +
            throw testing::TestError::Failed;
232 +
        };
185 233
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch
186 -
        { throw testing::TestError::Failed; };
234 +
        {
235 +
            throw testing::TestError::Failed;
236 +
        };
187 237
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch
188 -
        { throw testing::TestError::Failed; };
238 +
        {
239 +
            throw testing::TestError::Failed;
240 +
        };
189 241
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch {
190 242
        throw testing::TestError::Failed; };
191 243
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch {
192 244
        throw testing::TestError::Failed; };
193 245
    try testing::expectBytesEq(&buffer[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0,
208 260
    try writer::symbol(&mut out, "absent") catch err {
209 261
        try testing::expect(err == binary::Error::Symbol);
210 262
        set failures += 1;
211 263
    };
212 264
    try testing::expect(out.offset == 0);
213 -
    try writer::integer(&mut out, 0, 8) catch { throw testing::TestError::Failed; };
265 +
    try writer::integer(&mut out, 0, 8) catch {
266 +
        throw testing::TestError::Failed;
267 +
    };
214 268
    try writer::integer(&mut out, 1, 1) catch err {
215 269
        try testing::expect(err == binary::Error::Capacity);
216 270
        set failures += 1;
217 271
    };
218 272
    try testing::expect(failures == 3);
264 318
        };
265 319
        try testing::expect(failed);
266 320
        try testing::expect(out.offset <= capacity);
267 321
    }
268 322
    let mut out = writer::new(&mut buffer[..], &[]);
269 -
    try writer::bytes(&mut out, "abc") catch { throw testing::TestError::Failed; };
323 +
    try writer::bytes(&mut out, "abc") catch {
324 +
        throw testing::TestError::Failed;
325 +
    };
270 326
    try testing::expectBytesEq(&buffer[..], &[3, 0, 0, 0, 97, 98, 99]);
271 327
}
lib/std/lang/il/binary/writer.rad +11 -0
265 265
            try val(out, va0);
266 266
            try val(out, va1);
267 267
            try val(out, va2);
268 268
            try val(out, va3);
269 269
        },
270 +
        case il::Instr::DeviceRead { typ: t, dst, handle, offset } => {
271 +
            try integer(out, super::INSTR_DEVICE_READ as u64, 1);
272 +
            try typ(out, t);
273 +
            try integer(out, dst.n as u64, 4);
274 +
            try val(out, handle); try val(out, offset);
275 +
        },
276 +
        case il::Instr::DeviceWrite { typ: t, handle, offset, value } => {
277 +
            try integer(out, super::INSTR_DEVICE_WRITE as u64, 1);
278 +
            try typ(out, t);
279 +
            try val(out, handle); try val(out, offset); try val(out, value);
280 +
        },
270 281
        case il::Instr::Ebreak => {
271 282
            try integer(out, super::INSTR_EBREAK as u64, 1);
272 283
        },
273 284
        case il::Instr::MemoryFence => {
274 285
            try integer(out, super::INSTR_MEMORYFENCE as u64, 1);
lib/std/lang/il/printer.rad +8 -0
336 336
            write(out, " ");
337 337
            writeVal(out, a2);
338 338
            write(out, " ");
339 339
            writeVal(out, a3);
340 340
        }
341 +
        case super::Instr::DeviceRead { typ, dst, handle, offset } => {
342 +
            write(out, "device-read "); write(out, typeStr(typ)); write(out, " ");
343 +
            writeReg(out, dst); write(out, " "); writeVal(out, handle); write(out, " "); writeVal(out, offset);
344 +
        }
345 +
        case super::Instr::DeviceWrite { typ, handle, offset, value } => {
346 +
            write(out, "device-write "); write(out, typeStr(typ)); write(out, " ");
347 +
            writeVal(out, handle); write(out, " "); writeVal(out, offset); write(out, " "); writeVal(out, value);
348 +
        }
341 349
        case super::Instr::Ebreak => {
342 350
            write(out, "ebreak");
343 351
        }
344 352
        case super::Instr::MemoryFence => {
345 353
            write(out, "memory-fence");
lib/std/lang/il/tests.rad added +64 -0
1 +
//! Tests for RIL source register iteration.
2 +
3 +
use std::testing;
4 +
5 +
/// Require exact source-register order and stable exhaustion.
6 +
unsafe fn check(instr: super::Instr, expected: &[u32]) throws (testing::TestError) {
7 +
    let mut cursor = super::registers(&instr);
8 +
    for value in expected {
9 +
        let reg = super::nextReg(&mut cursor, &instr) else panic;
10 +
        try testing::expect(reg.n == value);
11 +
    }
12 +
    try testing::expect(super::nextReg(&mut cursor, &instr) == nil);
13 +
    try testing::expect(super::nextReg(&mut cursor, &instr) == nil);
14 +
}
15 +
16 +
@test unsafe fn testRegisterIteration() throws (testing::TestError) {
17 +
    let dst = super::Reg { n: 99 };
18 +
    let r1 = super::Reg { n: 1 };
19 +
    let r2 = super::Reg { n: 2 };
20 +
    let a = super::Val::Reg(r1);
21 +
    let b = super::Val::Reg(r2);
22 +
    let imm = super::Val::Imm(7);
23 +
    try check(super::Instr::Reserve { dst, size: a, alignment: 8 }, &[1]);
24 +
    try check(super::Instr::Reserve { dst, size: imm, alignment: 8 }, &[]);
25 +
    try check(super::Instr::Load { typ: super::Type::W64, dst, src: r1, offset: 0 }, &[1]);
26 +
    try check(super::Instr::Sload { typ: super::Type::W32, dst, src: r2, offset: 0 }, &[2]);
27 +
    try check(super::Instr::Store { typ: super::Type::W64, src: a, dst: r2, offset: 0 }, &[1, 2]);
28 +
    try check(super::Instr::Store { typ: super::Type::W64, src: imm, dst: r2, offset: 0 }, &[2]);
29 +
    try check(super::Instr::Blit { dst: r1, src: r2, size: a }, &[1, 2, 1]);
30 +
    try check(super::Instr::Blit { dst: r1, src: r2, size: imm }, &[1, 2]);
31 +
    try check(super::Instr::Copy { dst, val: a }, &[1]);
32 +
    try check(super::Instr::Copy { dst, val: imm }, &[]);
33 +
    try check(super::Instr::Copy { dst, val: super::Val::DataSym("data") }, &[]);
34 +
    try check(super::Instr::Copy { dst, val: super::Val::Undef }, &[]);
35 +
    try check(super::Instr::BinOp { op: super::BinOp::Add, typ: super::Type::W64, dst, a: imm, b }, &[2]);
36 +
    try check(super::Instr::BinOp { op: super::BinOp::Add, typ: super::Type::W64, dst, a, b: a }, &[1, 1]);
37 +
    try check(super::Instr::UnOp { op: super::UnOp::Neg, typ: super::Type::W64, dst, a }, &[1]);
38 +
    try check(super::Instr::Zext { typ: super::Type::W8, dst, val: a }, &[1]);
39 +
    try check(super::Instr::Sext { typ: super::Type::W8, dst, val: b }, &[2]);
40 +
    let mut args = [imm, a, b, a];
41 +
    try check(super::Instr::Call { retTy: super::Type::W64, dst, func: b, args: &args[..] }, &[2, 1, 2, 1]);
42 +
    try check(super::Instr::Call { retTy: super::Type::W64, dst: nil, func: super::Val::FnAddr("callee"), args: &[] }, &[]);
43 +
    try check(super::Instr::Ret { val: a }, &[1]);
44 +
    try check(super::Instr::Ret { val: nil }, &[]);
45 +
    try check(super::Instr::Jmp { target: 0, args: &mut args[..] }, &[1, 2, 1]);
46 +
    let mut other = [b, imm];
47 +
    try check(super::Instr::Br { op: super::CmpOp::Eq, typ: super::Type::W64, a, b,
48 +
        thenTarget: 0, thenArgs: &mut args[..], elseTarget: 1, elseArgs: &mut other[..] }, &[1, 2, 1, 2, 1, 2]);
49 +
    let mut cases = [
50 +
        super::SwitchCase { value: 0, target: 0, args: &mut [] },
51 +
        super::SwitchCase { value: 1, target: 1, args: &mut args[..] },
52 +
        super::SwitchCase { value: 2, target: 2, args: &mut [] },
53 +
        super::SwitchCase { value: 3, target: 3, args: &mut other[..] },
54 +
    ];
55 +
    try check(super::Instr::Switch { val: b, defaultTarget: 0, defaultArgs: &mut [], cases: &mut cases[..] }, &[2, 1, 2, 1, 2]);
56 +
    try check(super::Instr::Switch { val: imm, defaultTarget: 0, defaultArgs: &mut args[..], cases: &mut [] }, &[1, 2, 1]);
57 +
    try check(super::Instr::Switch { val: imm, defaultTarget: 0, defaultArgs: &mut [], cases: &mut [] }, &[]);
58 +
    try check(super::Instr::Ecall { dst, num: a, a0: imm, a1: b, a2: a, a3: imm }, &[1, 2, 1]);
59 +
    try check(super::Instr::Ecall { dst, num: a, a0: b, a1: a, a2: b, a3: a }, &[1, 2, 1, 2, 1]);
60 +
    try check(super::Instr::Unreachable, &[]);
61 +
    try check(super::Instr::Ebreak, &[]);
62 +
    try check(super::Instr::MemoryFence, &[]);
63 +
64 +
}
lib/std/lang/lower.rad +922 -628
262 262
    Normal,
263 263
    /// Program entry function marked with `@default`.
264 264
    Default,
265 265
}
266 266
267 -
/// Function sink used by lowerers that consume functions as they are produced.
268 -
export record FnSink: Copy {
269 -
    /// Opaque context. It must remain valid for every sink callback.
270 -
    ctx: *unsafe mut opaque,
271 -
    /// Callback invoked for each lowered function.
272 -
    emitFn: unsafe fn(*unsafe mut opaque, *unsafe il::Fn, FnRole),
273 -
}
274 -
275 -
/// Destination for functions produced by the lowerer.
276 -
export union FnOutput: Copy {
277 -
    /// Store lowered functions in the provided slice.
278 -
    Accumulate(*unsafe mut [*unsafe il::Fn]),
279 -
    /// Send lowered functions to an external consumer immediately.
280 -
    Stream(FnSink),
267 +
/// One function ready for a caller to consume before lowering continues.
268 +
export record LoweredFunction: Copy {
269 +
    /// Function allocated in the current function arena.
270 +
    function: *unsafe il::Fn,
271 +
    /// Entry-point role of the source declaration.
272 +
    role: FnRole,
273 +
}
274 +
275 +
/// Position within one module's declarations and instance methods.
276 +
export record ModuleCursor {
277 +
    /// Top-level declaration nodes in source order.
278 +
    declarations: *[*ast::Node],
279 +
    /// Next declaration to process.
280 +
    next: u32,
281 +
    /// Whether default functions in this module are program entry points.
282 +
    isRoot: bool,
283 +
    /// Instance whose methods are currently being lowered.
284 +
    instanceState: InstanceCursor,
285 +
    /// Whether the instance cursor contains an active declaration.
286 +
    instanceActive: bool,
287 +
}
288 +
289 +
/// Persistent v-table names and position within an instance declaration.
290 +
export record InstanceCursor: Copy {
291 +
    /// Trait whose method slots define the v-table.
292 +
    traitInfo: *unsafe resolver::TraitType,
293 +
    /// Trait source name.
294 +
    traitName: *[u8],
295 +
    /// Concrete type source name.
296 +
    typeName: *[u8],
297 +
    /// Method declarations in source order.
298 +
    methods: *[*ast::Node],
299 +
    /// Next method to process.
300 +
    next: u32,
301 +
    /// Persistent qualified function names, indexed by trait method slot.
302 +
    methodNames: [*[u8]; ast::MAX_TRAIT_METHODS],
303 +
    /// Slots initialized by concrete methods.
304 +
    methodNameSet: [bool; ast::MAX_TRAIT_METHODS],
281 305
}
282 306
283 307
/// Module-level lowering context. Shared across all function lowerings.
284 308
/// Holds global state like the data section (strings, constants) and provides
285 309
/// access to the resolver for type queries.
286 -
export record Lowerer {
310 +
export record Lowerer: 'arena + 'phase where 'arena: 'phase {
287 311
    /// Arena for persistent lowering state, including data and symbol names.
288 312
    arena: *unsafe mut alloc::Arena,
289 -
    /// Arena for allocations owned by the function currently being lowered.
290 -
    fnArena: *unsafe mut alloc::Arena,
291 -
    /// Allocator backed by the arena.
292 -
    allocator: alloc::Allocator,
293 313
    /// Resolver for type information. Used to query types, symbols, and
294 314
    /// compile-time constant values during lowering.
295 -
    resolver: *unsafe resolver::Resolver,
315 +
    resolver: &'phase resolver::Resolver 'arena,
296 316
    /// Module graph for cross-module symbol resolution.
297 -
    moduleGraph: ?*unsafe module::ModuleGraph,
317 +
    moduleGraph: ?&'phase module::ModuleGraph,
298 318
    /// Package name for qualified symbol names.
299 319
    pkgName: *[u8],
300 320
    /// Current module being lowered.
301 321
    currentMod: ?u16,
302 322
    /// Global data items (string literals, constants, static arrays).
303 323
    /// These become the data sections in the final binary.
304 324
    data: *mut [il::Data],
305 -
    /// Destination for lowered functions.
306 -
    output: FnOutput,
307 -
    /// Map of function symbols to qualified names.
308 -
    fnSyms: *mut [FnSymEntry],
325 +
    /// First data entry owned by the package currently being lowered.
326 +
    packageDataStart: u32,
327 +
    /// Functions retained for accumulated output.
328 +
    functions: *unsafe mut [*unsafe il::Fn],
329 +
    /// Function and data symbols with their emitted names.
330 +
    symbolNames: *mut [SymbolNameEntry],
309 331
    /// Global error type tag table. Maps nominal types to unique tags.
310 332
    errTags: *mut [ErrTagEntry],
311 333
    /// Next error tag to assign (starts at 1; 0 = success).
312 334
    errTagCounter: u32,
313 335
    /// Lowering options.
314 336
    options: LowerOptions,
315 337
}
316 338
317 -
/// Entry mapping a function symbol to its qualified name.
318 -
record FnSymEntry: Copy {
339 +
/// Entry mapping a function or data symbol to its emitted name.
340 +
record SymbolNameEntry: Copy {
341 +
    /// Stable resolver symbol identity.
319 342
    sym: *unsafe resolver::Symbol,
343 +
    /// Qualified name in the emitted program.
320 344
    qualName: *[u8],
321 345
}
322 346
323 347
/// Entry in the global error tag table.
324 348
record ErrTagEntry: Copy {
340 364
    return maxSize;
341 365
}
342 366
343 367
/// Get or assign a globally unique error tag for the given error type.
344 368
/// Tag `0` is reserved for success; error tags start at `1`.
345 -
fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 {
369 +
unsafe fn getOrAssignErrorTag 'arena 'phase (self: &mut Lowerer 'arena 'phase, errType: resolver::Type) -> u32 where 'arena: 'phase {
346 370
    for entry in &self.errTags[..] {
347 -
        if entry.ty == errType {
371 +
        if resolver::erasedTypesEqual(entry.ty, errType) {
348 372
            return entry.tag;
349 373
        }
350 374
    }
351 375
    let tag = self.errTagCounter;
352 376
353 377
    set self.errTagCounter += 1;
354 -
    self.errTags.append(ErrTagEntry { ty: errType, tag }, self.allocator);
378 +
    self.errTags.append(ErrTagEntry { ty: errType, tag }, alloc::arenaAllocator(self.arena));
355 379
356 380
    return tag;
357 381
}
358 382
359 -
/// Emit one function to the active output.
360 -
unsafe fn emitFunction(self: &mut Lowerer, func: *unsafe il::Fn, role: FnRole) {
361 -
    match self.output {
362 -
        case FnOutput::Accumulate(accumulated) => {
363 -
            let mut fns = accumulated;
364 -
            fns.append(func, self.allocator);
365 -
            set self.output = FnOutput::Accumulate(fns);
366 -
        }
367 -
        case FnOutput::Stream(sink) => {
368 -
            sink.emitFn(sink.ctx, func, role);
369 -
        }
370 -
    }
383 +
/// Retain a lowered function for accumulated IL output.
384 +
unsafe fn emitFunction 'arena 'phase (self: &mut Lowerer 'arena 'phase, func: *unsafe il::Fn) where 'arena: 'phase {
385 +
    self.functions.append(func, alloc::arenaAllocator(self.arena));
371 386
}
372 387
373 388
/// Get the function role for a top-level function declaration.
374 389
fn lowerFnRole(isRoot: bool, attrs: ?ast::Attributes) -> FnRole {
375 390
    if isRoot and checkAttr(attrs, ast::Attribute::Default) {
673 688
// Function Lowering State //
674 689
/////////////////////////////
675 690
676 691
/// Per-function lowering state. Created fresh for each function and contains
677 692
/// all the mutable state needed during function body lowering.
678 -
record FnLowerer: Copy {
693 +
record FnLowerer: 'arena + 'phase + 'function where 'arena: 'phase, 'phase: 'function {
679 694
    /// Reference to the module-level lowerer.
680 -
    low: *unsafe mut Lowerer,
681 -
    /// Allocator for IL allocations.
682 -
    allocator: alloc::Allocator,
695 +
    low: &'function mut Lowerer 'arena 'phase,
696 +
    /// Arena for allocations owned by this function.
697 +
    arena: &'function mut alloc::Arena,
683 698
    /// Type signature of the function being lowered.
684 699
    fnType: *resolver::FnType,
685 700
    /// Number of SSA variable slots required for each block.
686 701
    localCount: u32,
687 702
    /// Function name, used as prefix for generated data symbols.
692 707
    /// Metadata (name, type, mutability) for each variable. Indexed by variable
693 708
    /// id. Doesn't change after declaration. For the SSA value of a variable in
694 709
    /// a specific block, see [`BlockData::vars`].
695 710
    vars: *unsafe mut [VarData],
696 711
    /// Parameter-to-variable bindings, initialized in the entry block.
697 -
    params: *unsafe mut [FnParamBinding],
712 +
    params: [FnParamBinding; resolver::MAX_FN_PARAMS],
713 +
    /// Number of initialized parameter bindings.
714 +
    paramsLen: u32,
698 715
699 716
    // ~ Basic block management ~ //
700 717
701 718
    /// Block storage array, indexed by block id.
702 719
    blockData: *unsafe mut [BlockData],
705 722
    /// The block currently receiving new instructions.
706 723
    currentBlock: ?BlockId,
707 724
708 725
    // ~ Loop management ~ //
709 726
710 -
    /// Stack of loop contexts for break/continue resolution.
711 -
    loopStack: *unsafe mut [LoopCtx],
727 +
    /// Loop contexts indexed by nesting depth.
728 +
    /// The active prefix contains `loopDepth` entries.
729 +
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
712 730
    /// Current nesting depth (index into loopStack).
713 731
    loopDepth: u32,
714 732
715 733
    // ~ Counters ~ //
716 734
745 763
/// 2. Iterates over top-level declarations, lowering each.
746 764
/// 3. Returns the complete IL program with functions and data section.
747 765
///
748 766
/// The resolver must have already processed the AST -- we rely on its type
749 767
/// annotations, symbol table, and constant evaluations.
750 -
export unsafe fn lower(
751 -
    res: &resolver::Resolver,
768 +
export unsafe fn lower 'arena (
769 +
    res: &resolver::Resolver 'arena,
752 770
    root: *ast::Node,
753 771
    pkgName: *[u8],
754 772
    arena: &mut alloc::Arena
755 773
) -> il::Program throws (LowerError) {
756 -
    let mut low = Lowerer {
757 -
        arena: (&mut *arena) as *unsafe mut alloc::Arena,
758 -
        fnArena: (&mut *arena) as *unsafe mut alloc::Arena,
759 -
        allocator: alloc::arenaAllocator(arena),
760 -
        resolver: res as *unsafe resolver::Resolver,
761 -
        moduleGraph: nil,
762 -
        pkgName,
763 -
        currentMod: nil,
764 -
        data: &mut [],
765 -
        output: FnOutput::Accumulate(&mut []),
766 -
        fnSyms: &mut [],
767 -
        errTags: &mut [],
768 -
        errTagCounter: 1,
769 -
        options: LowerOptions { debug: false, buildTest: false },
770 -
    };
771 -
    try lowerDecls(&mut low, root, true);
774 +
    let functionArena = (&mut *arena) as *unsafe mut alloc::Arena;
775 +
    let resolved: 'phase = &*res where 'arena: 'phase in {
776 +
        let mut low = Lowerer 'arena 'phase {
777 +
            arena: (&mut *arena) as *unsafe mut alloc::Arena,
778 +
            resolver: resolved,
779 +
            moduleGraph: nil,
780 +
            pkgName,
781 +
            currentMod: nil,
782 +
            data: &mut [],
783 +
            packageDataStart: 0,
784 +
            functions: &mut [],
785 +
            symbolNames: &mut [],
786 +
            errTags: &mut [],
787 +
            errTagCounter: 1,
788 +
            options: LowerOptions { debug: false, buildTest: false },
789 +
        };
790 +
        try lowerDecls(&mut low, root, true, functionArena);
772 791
773 -
    return finalize(low);
792 +
        return finalize(low);
793 +
    }
774 794
}
775 795
776 796
/////////////////////////////////
777 797
// Multi-Module Lowering API   //
778 798
/////////////////////////////////
779 799
780 800
/// Create a lowerer for multi-module compilation.
781 -
/// The resolver, graph, and both arenas must outlive the returned lowerer.
782 -
export unsafe fn lowerer(
783 -
    res: *unsafe resolver::Resolver,
784 -
    graph: &module::ModuleGraph,
801 +
/// The resolver, graph, and persistent arena must outlive the returned lowerer.
802 +
export unsafe fn lowerer 'arena 'phase (
803 +
    res: &'phase resolver::Resolver 'arena,
804 +
    graph: &'phase module::ModuleGraph,
785 805
    pkgName: *[u8],
786 806
    arena: *unsafe mut alloc::Arena,
787 -
    fnArena: *unsafe mut alloc::Arena,
788 807
    options: LowerOptions
789 -
) -> Lowerer {
790 -
    return Lowerer {
808 +
) -> Lowerer 'arena 'phase where 'arena: 'phase {
809 +
    return Lowerer 'arena 'phase {
791 810
        arena,
792 -
        fnArena,
793 -
        allocator: alloc::arenaAllocator(arena),
794 811
        resolver: res,
795 -
        moduleGraph: graph as *unsafe module::ModuleGraph,
812 +
        moduleGraph: graph,
796 813
        pkgName,
797 814
        currentMod: nil,
798 815
        data: &mut [],
799 -
        output: FnOutput::Accumulate(&mut []),
800 -
        fnSyms: &mut [],
816 +
        packageDataStart: 0,
817 +
        functions: &mut [],
818 +
        symbolNames: &mut [],
801 819
        errTags: &mut [],
802 820
        errTagCounter: 1,
803 821
        options,
804 822
    };
805 823
}
806 824
807 825
/// Lower a module's AST into the lowerer.
808 826
/// Call this for each module in the package, then use `finalize` to get the program.
809 -
export unsafe fn lowerModule(
810 -
    low: &mut Lowerer,
827 +
export unsafe fn lowerModule 'arena 'phase (
828 +
    low: &mut Lowerer 'arena 'phase,
811 829
    moduleId: u16,
812 830
    root: *ast::Node,
813 -
    isRoot: bool
814 -
) throws (LowerError) {
831 +
    isRoot: bool,
832 +
    functionArena: &mut alloc::Arena
833 +
) throws (LowerError) where 'arena: 'phase {
815 834
    set low.currentMod = moduleId;
816 -
    try lowerDecls(low, root, isRoot);
835 +
    try lowerDecls(low, root, isRoot, functionArena);
817 836
}
818 837
819 -
/// Lower all top-level declarations in a block.
820 -
unsafe fn lowerDecls(low: &mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) {
838 +
/// Start lowering the declarations of one module.
839 +
export unsafe fn moduleCursor(root: *ast::Node, isRoot: bool) -> ModuleCursor throws (LowerError) {
821 840
    let case ast::NodeValue::Block(block) = root.value else {
822 841
        throw LowerError::ExpectedBlock(root);
823 842
    };
824 -
    let stmtsList = block.statements;
825 -
826 -
    for node in stmtsList {
843 +
    return ModuleCursor { declarations: block.statements, next: 0, isRoot, instanceState: undefined, instanceActive: false };
844 +
}
845 +
846 +
/// Lower declarations until one function is ready or the module is complete.
847 +
/// The caller must finish using a function before reclaiming its arena storage.
848 +
export unsafe fn lowerNext 'arena 'phase (
849 +
    low: &mut Lowerer 'arena 'phase,
850 +
    cursor: &mut ModuleCursor,
851 +
    functionArena: &mut alloc::Arena
852 +
) -> ?LoweredFunction throws (LowerError) where 'arena: 'phase {
853 +
    loop {
854 +
        if cursor.instanceActive {
855 +
            if let function = try lowerNextInstance(low, &mut cursor.instanceState, functionArena) {
856 +
                return LoweredFunction { function, role: FnRole::Normal };
857 +
            }
858 +
            set cursor.instanceActive = false;
859 +
        }
860 +
        if cursor.next >= cursor.declarations.len {
861 +
            return nil;
862 +
        }
863 +
        let node = cursor.declarations[cursor.next];
864 +
        set cursor.next += 1;
827 865
        match node.value {
828 866
            case ast::NodeValue::FnDecl(decl) => {
829 -
                if let f = try lowerFnDecl(low, node, decl) {
830 -
                    let role = lowerFnRole(isRoot, decl.attrs);
831 -
                    emitFunction(low, f, role);
867 +
                if let function = try lowerFnDecl(low, node, decl, functionArena) {
868 +
                    return LoweredFunction { function, role: lowerFnRole(cursor.isRoot, decl.attrs) };
832 869
                }
833 870
            }
834 -
            case ast::NodeValue::ConstDecl(decl) => {
835 -
                try lowerDataDecl(low, node, decl.value, true);
836 -
            }
837 -
            case ast::NodeValue::StaticDecl(decl) => {
838 -
                try lowerDataDecl(low, node, decl.value, false);
839 -
            }
840 -
            case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => {
841 -
                try lowerInstanceDecl(low, node, traitName, targetType, methods);
871 +
            case ast::NodeValue::ConstDecl(decl) =>
872 +
                try lowerDataDecl(low, node, decl.value, true),
873 +
            case ast::NodeValue::StaticDecl(decl) =>
874 +
                try lowerDataDecl(low, node, decl.value, false),
875 +
            case ast::NodeValue::InstanceDecl { traitName, targetType, methods, .. } => {
876 +
                set cursor.instanceState = try instanceCursor(low, traitName, targetType, methods);
877 +
                set cursor.instanceActive = true;
842 878
            }
843 -
            case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, .. } => {
844 -
                if let f = try lowerMethodDecl(low, node, name, receiverName, sig, body) {
845 -
                    emitFunction(low, f, FnRole::Normal);
879 +
            case ast::NodeValue::MethodDecl { name, receiverName, sig, body, .. } => {
880 +
                if let function = try lowerMethodDecl(
881 +
                    low, node, name, receiverName, sig, body, functionArena
882 +
                ) {
883 +
                    return LoweredFunction { function, role: FnRole::Normal };
846 884
                }
847 885
            }
848 886
            else => {},
849 887
        }
850 888
    }
851 889
}
852 890
891 +
/// Lower all top-level declarations in a block.
892 +
unsafe fn lowerDecls 'arena 'phase (
893 +
    low: &mut Lowerer 'arena 'phase,
894 +
    root: *ast::Node,
895 +
    isRoot: bool,
896 +
    functionArena: &mut alloc::Arena
897 +
) throws (LowerError) where 'arena: 'phase {
898 +
    let mut cursor = try moduleCursor(root, isRoot);
899 +
    while let result = try lowerNext(low, &mut cursor, functionArena) {
900 +
        emitFunction(low, result.function);
901 +
    }
902 +
}
903 +
853 904
/// Consume the lowerer and publish its global data as an immutable array.
854 -
export fn finalize(low: Lowerer) -> il::Program {
855 -
    let case Lowerer { data, output, .. } = low
905 +
export fn finalize 'arena 'phase (low: Lowerer 'arena 'phase) -> il::Program where 'arena: 'phase {
906 +
    let case Lowerer 'arena 'phase { data, functions, .. } = low
856 907
        else panic "expected lowerer";
857 -
    match output {
858 -
        case FnOutput::Accumulate(accumulated) => {
859 -
            return il::Program {
860 -
                data,
861 -
                fns: accumulated,
862 -
            };
863 -
        }
864 -
        case FnOutput::Stream(_) => {
865 -
            panic "finalize: cannot finalize streaming lowerer";
866 -
        }
867 -
    }
908 +
    return il::Program { data, fns: functions };
868 909
}
869 910
870 911
/////////////////////////////////
871 912
// Qualified Name Construction //
872 913
/////////////////////////////////
873 914
874 915
/// Get module path segments for the current or specified module.
875 916
/// Returns empty slice if no module graph or module not found.
876 -
unsafe fn getModulePath(self: &mut Lowerer, modId: ?u16) -> *[*[u8]] {
917 +
unsafe fn getModulePath 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16) -> *[*[u8]] where 'arena: 'phase {
877 918
    let graph = self.moduleGraph else {
878 919
        return &[];
879 920
    };
880 921
    let mut id = modId;
881 922
    if id == nil {
890 931
    return module::moduleQualifiedPath(entry);
891 932
}
892 933
893 934
/// Build a qualified name string for a symbol.
894 935
/// If `modId` is nil, uses current module.
895 -
unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] {
936 +
unsafe fn qualifyName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, name: *[u8]) -> *[u8] where 'arena: 'phase {
896 937
    let path = getModulePath(self, modId);
897 938
    if path.len == 0 {
898 939
        return name;
899 940
    }
900 941
    return il::formatQualifiedName(self.arena, path, name);
901 942
}
902 943
903 -
/// Register a function symbol with its qualified name.
904 -
/// Called when lowering function declarations, so cross-package calls can find
905 -
/// the function by name.
906 -
fn registerFnSym(self: &mut Lowerer, sym: *unsafe resolver::Symbol, qualName: *[u8]) {
907 -
    self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
944 +
/// Register the emitted name for a function or data symbol.
945 +
/// Calls and address expressions use this name across package boundaries.
946 +
unsafe fn registerSymbolName 'arena 'phase (self: &mut Lowerer 'arena 'phase, sym: *unsafe resolver::Symbol, qualName: *[u8]) where 'arena: 'phase {
947 +
    self.symbolNames.append(SymbolNameEntry { sym, qualName }, alloc::arenaAllocator(self.arena));
908 948
}
909 949
910 -
/// Look up a function's qualified name by its symbol.
911 -
/// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered).
950 +
/// Look up the emitted name for a function or data symbol.
951 +
/// Return `nil` if its declaration has not been lowered.
912 952
// TODO: This is kind of dubious as an optimization, if it depends on the order
913 953
// in which modules are lowered.
914 954
// TODO: Use a hash table here?
915 -
unsafe fn lookupFnSym(self: &Lowerer, sym: *unsafe resolver::Symbol) -> ?*[u8] {
916 -
    for entry in &self.fnSyms[..] {
955 +
unsafe fn lookupSymbolName 'arena 'phase (self: &Lowerer 'arena 'phase, sym: *unsafe resolver::Symbol) -> ?*[u8] where 'arena: 'phase {
956 +
    for entry in &self.symbolNames[..] {
917 957
        if entry.sym == sym {
918 958
            return entry.qualName;
919 959
        }
920 960
    }
921 961
    return nil;
922 962
}
923 963
924 964
/// Set the package context for lowering.
925 965
/// Called before lowering each package.
926 -
/// The graph must outlive later uses of the lowerer.
927 -
export unsafe fn setPackage(self: &mut Lowerer, graph: &module::ModuleGraph, pkgName: *[u8]) {
928 -
    set self.moduleGraph = graph as *unsafe module::ModuleGraph;
966 +
export unsafe fn setPackage 'arena 'phase (self: &mut Lowerer 'arena 'phase, pkgName: *[u8]) where 'arena: 'phase {
929 967
    set self.pkgName = pkgName;
930 968
    set self.currentMod = nil;
969 +
    set self.packageDataStart = self.data.len;
931 970
}
932 971
933 972
/// Create a new function lowerer for a given function type and name.
934 -
unsafe fn fnLowerer(
935 -
    self: &mut Lowerer,
973 +
unsafe fn fnLowerer 'arena 'phase 'function (
974 +
    self: &'function mut Lowerer 'arena 'phase,
936 975
    node: *ast::Node,
937 976
    fnType: *resolver::FnType,
938 -
    qualName: *[u8]
939 -
) -> FnLowerer {
940 -
    let loopStack = try! alloc::allocRawSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *unsafe mut [LoopCtx];
941 -
942 -
    let mut fnLow = FnLowerer {
943 -
        low: (&mut *self) as *unsafe mut Lowerer,
944 -
        allocator: alloc::arenaAllocator(self.fnArena),
945 -
        fnType: fnType,
946 -
        localCount: resolver::nodeData(self.resolver, node).localCount,
977 +
    qualName: *[u8],
978 +
    functionArena: &'function mut alloc::Arena
979 +
) -> FnLowerer 'arena 'phase 'function where 'arena: 'phase, 'phase: 'function {
980 +
    let localCount = resolver::nodeData(self.resolver, node).localCount;
981 +
    let mut fnLow = FnLowerer 'arena 'phase 'function {
982 +
        low: self,
983 +
        arena: functionArena,
984 +
        fnType,
985 +
        localCount,
947 986
        fnName: qualName,
948 987
        vars: &mut [],
949 -
        params: &mut [],
988 +
        params: [FnParamBinding { var: Var(0), reg: il::Reg { n: 0 } }; resolver::MAX_FN_PARAMS],
989 +
        paramsLen: 0,
950 990
        blockData: &mut [],
951 991
        entryBlock: nil,
952 992
        currentBlock: nil,
953 -
        loopStack,
993 +
        loopStack: [LoopCtx { breakTarget: BlockId(0), continueTarget: nil }; MAX_LOOP_DEPTH],
954 994
        loopDepth: 0,
955 995
        labelCounter: 0,
956 996
        dataCounter: 0,
957 997
        regCounter: 0,
958 998
        returnReg: nil,
959 999
        isLeaf: true,
960 1000
        srcLoc: undefined,
961 1001
    };
962 -
    if self.options.debug {
963 -
        let modId = self.currentMod else {
1002 +
    if fnLow.low.options.debug {
1003 +
        let modId = fnLow.low.currentMod else {
964 1004
            panic "fnLowerer: debug enabled but no current module";
965 1005
        };
966 1006
        set fnLow.srcLoc = il::SrcLoc {
967 1007
            moduleId: modId,
968 1008
            offset: node.span.offset,
976 1016
/// This sets up the per-function lowering state, processes parameters,
977 1017
/// then lowers the function body into a CFG of basic blocks.
978 1018
///
979 1019
/// For throwing functions, the return type is a result aggregate
980 1020
/// rather than the declared return type.
981 -
unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*unsafe il::Fn throws (LowerError) {
1021 +
unsafe fn lowerFnDecl 'arena 'phase (
1022 +
    self: &mut Lowerer 'arena 'phase,
1023 +
    node: *ast::Node,
1024 +
    decl: ast::FnDecl,
1025 +
    functionArena: &mut alloc::Arena
1026 +
) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase {
982 1027
    if not shouldLowerFn(&decl, self.options.buildTest) {
983 1028
        return nil;
984 1029
    }
985 1030
    let case ast::NodeValue::Ident(name) = decl.name.value else {
986 1031
        throw LowerError::ExpectedIdentifier;
994 1039
    // Build qualified function name for multi-module compilation.
995 1040
    let qualName = qualifyName(self, nil, name);
996 1041
997 1042
    // Register function symbol for cross-package call resolution.
998 1043
    if let sym = data.sym {
999 -
        registerFnSym(self, sym, qualName);
1000 -
    }
1001 -
    let mut fnLow = fnLowerer(self, node, fnType, qualName);
1002 -
1003 -
    // If the function returns an aggregate or is throwing, prepend a hidden
1004 -
    // return parameter. The caller allocates the buffer and passes it
1005 -
    // as the first argument; the callee writes the return value into it.
1006 -
    if requiresReturnParam(fnType) and not isExtern {
1007 -
        set fnLow.returnReg = nextReg(&mut fnLow);
1008 -
    }
1009 -
    let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil);
1010 -
    let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1044 +
        registerSymbolName(self, sym, qualName);
1045 +
    }
1046 +
    let parent: 'function = &mut *self, arena = &mut *functionArena where 'phase: 'function in {
1047 +
        let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena);
1048 +
1049 +
        // If the function returns an aggregate or is throwing, prepend a hidden
1050 +
        // return parameter. The caller allocates the buffer and passes it
1051 +
        // as the first argument; the callee writes the return value into it.
1052 +
        if requiresReturnParam(fnType) and not isExtern {
1053 +
            set fnLow.returnReg = nextReg(&mut fnLow);
1054 +
        }
1055 +
        let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil);
1056 +
        let func = try! alloc::allocRaw(fnLow.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1057 +
1058 +
        // Throwing functions return a result aggregate (word-sized pointer).
1059 +
        // TODO: The resolver should set an appropriate type that takes into account
1060 +
        //       the throws list. It shouldn't set the return type to the "success"
1061 +
        //       value only.
1062 +
        let returnType = il::Type::W64 if fnType.throwList.len > 0
1063 +
            else ilType(fnLow.low, *fnType.returnType);
1064 +
        set *func = il::Fn {
1065 +
            name: qualName,
1066 +
            params: lowParams,
1067 +
            returnType,
1068 +
            isExtern,
1069 +
            isLeaf: true,
1070 +
            blocks: &[],
1071 +
        };
1072 +
        let body = decl.body else {
1073 +
            // Extern functions have no body.
1074 +
            assert isExtern;
1075 +
            return func;
1076 +
        };
1077 +
        set func.blocks = try lowerFnBody(&mut fnLow, body);
1078 +
        set func.isLeaf = fnLow.isLeaf;
1011 1079
1012 -
    set *func = il::Fn {
1013 -
        name: qualName,
1014 -
        params: lowParams,
1015 -
        returnType: undefined,
1016 -
        isExtern,
1017 -
        isLeaf: true,
1018 -
        blocks: &[],
1019 -
    };
1020 -
    // Throwing functions return a result aggregate (word-sized pointer).
1021 -
    // TODO: The resolver should set an appropriate type that takes into account
1022 -
    //       the throws list. It shouldn't set the return type to the "success"
1023 -
    //       value only.
1024 -
    if fnType.throwList.len > 0 {
1025 -
        set func.returnType = il::Type::W64;
1026 -
    } else {
1027 -
        set func.returnType = ilType(self, *fnType.returnType);
1028 -
    }
1029 -
    let body = decl.body else {
1030 -
        // Extern functions have no body.
1031 -
        assert isExtern;
1032 1080
        return func;
1033 -
    };
1034 -
    set func.blocks = try lowerFnBody(&mut fnLow, body);
1035 -
    set func.isLeaf = fnLow.isLeaf;
1036 -
1037 -
    return func;
1081 +
    }
1038 1082
}
1039 1083
1040 1084
/// Build a qualified name of the form "Type::method".
1041 -
unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] {
1085 +
unsafe fn instanceMethodName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] where 'arena: 'phase {
1042 1086
    let sepLen: u32 = 2; // "::"
1043 1087
    let totalLen = typeName.len + sepLen + methodName.len;
1044 1088
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1045 1089
    let mut pos: u32 = 0;
1046 1090
1051 1095
1052 1096
    return qualifyName(self, modId, &buf[..totalLen]);
1053 1097
}
1054 1098
1055 1099
/// Build a v-table data name of the form "vtable::Type::Trait".
1056 -
unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] {
1100 +
unsafe fn vtableName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] where 'arena: 'phase {
1057 1101
    let prefix = "vtable::";
1058 1102
    let sepLen: u32 = 2; // "::"
1059 1103
    let totalLen = prefix.len + typeName.len + sepLen + traitName.len;
1060 1104
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1061 1105
    let mut pos: u32 = 0;
1074 1118
/// Each method in the instance block is lowered as a standalone function
1075 1119
/// with a qualified name of the form `Type::method`. A read-only v-table
1076 1120
/// data record is emitted containing pointers to these functions, ordered
1077 1121
/// by the trait's method indices. The v-table is later referenced when
1078 1122
/// constructing trait objects for dynamic dispatch.
1079 -
unsafe fn lowerInstanceDecl(
1080 -
    self: &mut Lowerer,
1081 -
    node: *ast::Node,
1123 +
unsafe fn instanceCursor 'arena 'phase (
1124 +
    self: &mut Lowerer 'arena 'phase,
1082 1125
    traitNameNode: *ast::Node,
1083 1126
    targetTypeNode: *ast::Node,
1084 1127
    methods: *[*ast::Node]
1085 -
) throws (LowerError) {
1128 +
) -> InstanceCursor throws (LowerError) where 'arena: 'phase {
1086 1129
    // Look up the trait and type from the resolver.
1087 1130
    let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym
1088 1131
        else throw LowerError::MissingSymbol(traitNameNode);
1089 1132
    let case resolver::SymbolData::Trait(traitInfo) = traitSym.data
1090 1133
        else throw LowerError::MissingMetadata;
1091 1134
    let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym
1092 1135
        else throw LowerError::MissingSymbol(targetTypeNode);
1093 1136
1094 -
    let tName = traitSym.name;
1095 -
    let typeName = typeSym.name;
1096 -
1097 1137
    // Lower each instance method as a regular function.
1098 1138
    // Collect qualified names for the v-table. Empty entries are filled
1099 1139
    // later from inherited supertrait methods.
1100 -
    let mut methodNames: [*[u8]; ast::MAX_TRAIT_METHODS] = undefined;
1101 -
    let mut methodNameSet: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
1140 +
    return InstanceCursor {
1141 +
        traitInfo, traitName: traitSym.name, typeName: typeSym.name, methods, next: 0,
1142 +
        methodNames: undefined, methodNameSet: [false; ast::MAX_TRAIT_METHODS],
1143 +
    };
1144 +
}
1102 1145
1103 -
    for methodNode in methods {
1146 +
/// Lower the next concrete method, or finish the instance's v-table.
1147 +
unsafe fn lowerNextInstance 'arena 'phase (
1148 +
    self: &mut Lowerer 'arena 'phase,
1149 +
    state: &mut InstanceCursor,
1150 +
    functionArena: &mut alloc::Arena
1151 +
) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase {
1152 +
    let traitInfo = state.traitInfo;
1153 +
    let typeName = state.typeName;
1154 +
    let tName = state.traitName;
1155 +
    while state.next < state.methods.len {
1156 +
        let methodNode = state.methods[state.next];
1157 +
        set state.next += 1;
1104 1158
        let case ast::NodeValue::MethodDecl {
1105 -
            name, receiverName, receiverType, sig, body, ..
1159 +
            name, receiverName, sig, body, ..
1106 1160
        } = methodNode.value else continue;
1107 1161
1108 1162
        let case ast::NodeValue::Ident(mName) = name.value else {
1109 1163
            throw LowerError::ExpectedIdentifier;
1110 1164
        };
1111 1165
        let qualName = instanceMethodName(self, nil, typeName, mName);
1112 -
        let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body)
1166 +
        let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body, functionArena)
1113 1167
            else continue;
1114 -
        emitFunction(self, func, FnRole::Normal);
1115 1168
1116 1169
        let method = resolver::findTraitMethod(traitInfo, mName)
1117 1170
            else panic "lowerInstanceDecl: method not found in trait";
1118 1171
1119 -
        set methodNames[method.index] = qualName;
1120 -
        set methodNameSet[method.index] = true;
1172 +
        set state.methodNames[method.index] = qualName;
1173 +
        set state.methodNameSet[method.index] = true;
1174 +
        return func;
1121 1175
    }
1122 1176
1123 1177
    // Fill inherited method slots from supertraits.
1124 1178
    // These methods were already lowered as part of the supertrait instance
1125 1179
    // declarations and use the same `Type::method` qualified name.
1126 1180
    for method, i in traitInfo.methods {
1127 -
        if not methodNameSet[i] {
1128 -
            set methodNames[i] = instanceMethodName(self, nil, typeName, method.name);
1181 +
        if not state.methodNameSet[i] {
1182 +
            set state.methodNames[i] = instanceMethodName(self, nil, typeName, method.name);
1129 1183
        }
1130 1184
    }
1131 1185
1132 1186
    // Create v-table in data section, used for dynamic dispatch.
1133 1187
    let vName = vtableName(self, nil, typeName, tName);
1135 1189
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32
1136 1190
    ) as *mut [il::DataValue];
1137 1191
1138 1192
    for i in 0..traitInfo.methods.len {
1139 1193
        set values[i] = il::DataValue {
1140 -
            item: il::DataItem::Fn(methodNames[i]),
1194 +
            item: il::DataItem::Fn(state.methodNames[i]),
1141 1195
            count: 1,
1142 1196
        };
1143 1197
    }
1144 1198
    self.data.append(il::Data {
1145 1199
        name: vName,
1146 1200
        size: traitInfo.methods.len as u32 * resolver::PTR_SIZE,
1147 1201
        alignment: resolver::PTR_SIZE,
1148 1202
        readOnly: true,
1149 1203
        isZeroInit: false,
1150 1204
        values: &values[..traitInfo.methods.len as u32],
1151 -
    }, self.allocator);
1205 +
    }, alloc::arenaAllocator(self.arena));
1206 +
    return nil;
1152 1207
}
1153 1208
1154 1209
/// Lower a method node into an IL function with the given qualified name.
1155 1210
/// Shared by both instance methods and standalone methods.
1156 -
unsafe fn lowerMethod(
1157 -
    self: &mut Lowerer,
1211 +
unsafe fn lowerMethod 'arena 'phase (
1212 +
    self: &mut Lowerer 'arena 'phase,
1158 1213
    node: *ast::Node,
1159 1214
    qualName: *[u8],
1160 1215
    receiverName: *ast::Node,
1161 1216
    sig: ast::FnSig,
1162 1217
    body: *ast::Node,
1163 -
) -> ?*unsafe il::Fn throws (LowerError) {
1218 +
    functionArena: &mut alloc::Arena,
1219 +
) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase {
1164 1220
    let data = resolver::nodeData(self.resolver, node);
1165 1221
    let case resolver::Type::Fn(fnType) = data.ty else {
1166 1222
        throw LowerError::ExpectedFunction;
1167 1223
    };
1168 1224
    let sym = data.sym else throw LowerError::MissingSymbol(node);
1169 -
    registerFnSym(self, sym, qualName);
1170 -
1171 -
    let mut fnLow = fnLowerer(self, node, fnType, qualName);
1172 -
    if requiresReturnParam(fnType) {
1173 -
        set fnLow.returnReg = nextReg(&mut fnLow);
1174 -
    }
1175 -
    let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1176 -
    let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1225 +
    registerSymbolName(self, sym, qualName);
1226 +
1227 +
    let parent: 'function = &mut *self, arena = &mut *functionArena where 'phase: 'function in {
1228 +
        let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena);
1229 +
        if requiresReturnParam(fnType) {
1230 +
            set fnLow.returnReg = nextReg(&mut fnLow);
1231 +
        }
1232 +
        let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1233 +
        let func = try! alloc::allocRaw(fnLow.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1234 +
1235 +
        let returnType = il::Type::W64 if fnType.throwList.len > 0
1236 +
            else ilType(fnLow.low, *fnType.returnType);
1237 +
        set *func = il::Fn {
1238 +
            name: qualName,
1239 +
            params: lowParams,
1240 +
            returnType,
1241 +
            isExtern: false,
1242 +
            isLeaf: true,
1243 +
            blocks: &[],
1244 +
        };
1245 +
        set func.blocks = try lowerFnBody(&mut fnLow, body);
1246 +
        set func.isLeaf = fnLow.isLeaf;
1177 1247
1178 -
    set *func = il::Fn {
1179 -
        name: qualName,
1180 -
        params: lowParams,
1181 -
        returnType: ilType(self, *fnType.returnType),
1182 -
        isExtern: false,
1183 -
        isLeaf: true,
1184 -
        blocks: &[],
1185 -
    };
1186 -
    if fnType.throwList.len > 0 {
1187 -
        set func.returnType = il::Type::W64;
1248 +
        return func;
1188 1249
    }
1189 -
    set func.blocks = try lowerFnBody(&mut fnLow, body);
1190 -
    set func.isLeaf = fnLow.isLeaf;
1191 -
1192 -
    return func;
1193 1250
}
1194 1251
1195 1252
/// Lower a standalone method declaration.
1196 1253
/// Produces a function with qualified name `Type::method`.
1197 -
unsafe fn lowerMethodDecl(
1198 -
    self: &mut Lowerer,
1254 +
unsafe fn lowerMethodDecl 'arena 'phase (
1255 +
    self: &mut Lowerer 'arena 'phase,
1199 1256
    node: *ast::Node,
1200 1257
    name: *ast::Node,
1201 1258
    receiverName: *ast::Node,
1202 1259
    sig: ast::FnSig,
1203 1260
    body: *ast::Node,
1204 -
) -> ?*unsafe il::Fn throws (LowerError) {
1261 +
    functionArena: &mut alloc::Arena,
1262 +
) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase {
1205 1263
    let sym = resolver::nodeData(self.resolver, node).sym
1206 1264
        else throw LowerError::MissingSymbol(node);
1207 1265
    let case ast::NodeValue::Ident(mName) = name.value
1208 1266
        else throw LowerError::ExpectedIdentifier;
1209 1267
    let me = resolver::findMethodBySymbol(self.resolver, sym)
1210 1268
        else throw LowerError::MissingMetadata;
1211 1269
    let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName);
1212 1270
1213 -
    return try lowerMethod(self, node, qualName, receiverName, sig, body);
1271 +
    return try lowerMethod(self, node, qualName, receiverName, sig, body, functionArena);
1214 1272
}
1215 1273
1216 1274
/// Check if a function should be lowered.
1217 1275
fn shouldLowerFn(decl: &ast::FnDecl, buildTest: bool) -> bool {
1218 1276
    if checkAttr(decl.attrs, ast::Attribute::Test) {
1229 1287
    return false;
1230 1288
}
1231 1289
1232 1290
/// Create a label with a numeric suffix, eg. `@base0`.
1233 1291
/// This ensures unique labels like `@then0`, `@then1`, etc.
1234 -
unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) {
1292 +
unsafe fn labelWithSuffix 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1235 1293
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1236 1294
    let start = fmt::formatU32(suffix, &mut digits[..]);
1237 1295
    let totalLen = base.len + digits.len - start;
1238 -
    let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8];
1296 +
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1239 1297
1240 1298
    try! mem::copy(&mut buf[..base.len], base);
1241 1299
    try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]);
1242 1300
1243 1301
    return &buf[..totalLen];
1244 1302
}
1245 1303
1246 1304
/// Generate a unique label by appending the global counter to the base.
1247 -
unsafe fn nextLabel(self: &mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) {
1305 +
unsafe fn nextLabel 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8]) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1248 1306
    let idx = self.labelCounter;
1249 1307
    set self.labelCounter += 1;
1250 1308
1251 1309
    return try labelWithSuffix(self, base, idx);
1252 1310
}
1274 1332
        else => panic,
1275 1333
    }
1276 1334
}
1277 1335
1278 1336
/// Convert a constant value to an IL value.
1279 -
unsafe fn constValueToVal(self: &mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) {
1337 +
unsafe fn constValueToVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1280 1338
    if let case resolver::ConstValue::String(s) = val {
1281 1339
        return try lowerStringLit(self, node, s);
1282 1340
    }
1283 1341
    return il::Val::Imm(constToScalar(val));
1284 1342
}
1285 1343
1286 1344
/// Convert a resolver constant value to an IL data initializer item.
1287 -
unsafe fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem {
1345 +
unsafe fn constValueToDataItem 'arena 'phase (self: &mut Lowerer 'arena 'phase, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem where 'arena: 'phase {
1288 1346
    if let case resolver::ConstValue::String(s) = val {
1289 1347
        return il::DataItem::Str(s);
1290 1348
    }
1291 1349
    // Bool and char are byte-sized; integer uses the declared type.
1292 1350
    let mut irTyp = il::Type::W8;
1296 1354
    return il::DataItem::Val { typ: irTyp, val: constToScalar(val) };
1297 1355
}
1298 1356
1299 1357
/// Lower scalar-like constant nodes into data values, including fallback handling
1300 1358
/// for void-variant tags and slice string initializers.
1301 -
unsafe fn lowerConstScalarDataInto(
1302 -
    self: &mut Lowerer,
1359 +
unsafe fn lowerConstScalarDataInto 'arena 'phase (
1360 +
    self: &mut Lowerer 'arena 'phase,
1303 1361
    node: *ast::Node,
1304 1362
    ty: resolver::Type,
1305 1363
    dataPrefix: *[u8],
1306 1364
    b: &mut DataValueBuilder
1307 -
) throws (LowerError) {
1365 +
) throws (LowerError) where 'arena: 'phase {
1308 1366
    let val = resolver::constValueEntry(self.resolver, node) else {
1309 1367
        if let idx = voidVariantIndex(self.resolver, node) {
1310 1368
            dataBuilderPush(b, il::DataValue {
1311 1369
                item: il::DataItem::Val { typ: il::Type::W8, val: idx },
1312 1370
                count: 1
1328 1386
        count: 1
1329 1387
    });
1330 1388
}
1331 1389
1332 1390
/// Lower a constant or static declaration to the data section.
1333 -
unsafe fn lowerDataDecl(
1334 -
    self: &mut Lowerer,
1391 +
unsafe fn lowerDataDecl 'arena 'phase (
1392 +
    self: &mut Lowerer 'arena 'phase,
1335 1393
    node: *ast::Node,
1336 1394
    value: *ast::Node,
1337 1395
    readOnly: bool
1338 -
) throws (LowerError) {
1396 +
) throws (LowerError) where 'arena: 'phase {
1339 1397
    let data = resolver::nodeData(self.resolver, node);
1340 1398
    let sym = data.sym else {
1341 1399
        throw LowerError::MissingSymbol(node);
1342 1400
    };
1343 1401
    if data.ty == resolver::Type::Unknown {
1344 1402
        throw LowerError::MissingType(node);
1345 1403
    }
1346 1404
    let layout = resolver::getTypeLayout(data.ty);
1347 -
    let qualName = qualifyName(self, nil, sym.name);
1348 -
    let mut b = dataBuilder(self.allocator);
1405 +
    let qualName = lookupSymbolName(self, sym) else qualifyName(self, nil, sym.name);
1406 +
    let mut b = dataBuilder(alloc::arenaAllocator(self.arena));
1349 1407
    try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b);
1350 1408
    let result = dataBuilderFinish(b);
1351 1409
1352 1410
    self.data.append(il::Data {
1353 1411
        name: qualName,
1354 1412
        size: layout.size,
1355 1413
        alignment: layout.alignment,
1356 1414
        readOnly,
1357 1415
        isZeroInit: not readOnly and result.zeroInit,
1358 1416
        values: result.values,
1359 -
    }, self.allocator);
1417 +
    }, alloc::arenaAllocator(self.arena));
1360 1418
}
1361 1419
1362 1420
/// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`.
1363 1421
fn dataSliceHeader(b: &mut DataValueBuilder, dataSym: *[u8], len: u32) {
1364 1422
    dataBuilderPush(b, il::DataValue {
1380 1438
        count: 1
1381 1439
    });
1382 1440
}
1383 1441
1384 1442
/// Lower a compile-time `&[...]` expression to a concrete slice header.
1385 -
unsafe fn lowerConstAddressSliceInto(
1386 -
    self: &mut Lowerer,
1443 +
unsafe fn lowerConstAddressSliceInto 'arena 'phase (
1444 +
    self: &mut Lowerer 'arena 'phase,
1387 1445
    addr: ast::AddressOf,
1388 1446
    ty: resolver::Type,
1389 1447
    dataPrefix: *[u8],
1390 1448
    b: &mut DataValueBuilder
1391 -
) throws (LowerError) {
1449 +
) throws (LowerError) where 'arena: 'phase {
1392 1450
    let case resolver::Type::Slice { mutable, .. } = ty
1393 1451
        else throw LowerError::ExpectedSliceOrArray;
1394 1452
    let targetTy = resolver::typeFor(self.resolver, addr.target)
1395 1453
        else throw LowerError::MissingType(addr.target);
1396 1454
    let case resolver::Type::Array(arrInfo) = targetTy
1397 1455
        else throw LowerError::ExpectedArray;
1398 1456
1399 -
    let mut nested = dataBuilder(self.allocator);
1457 +
    let mut nested = dataBuilder(alloc::arenaAllocator(self.arena));
1400 1458
    let layout = resolver::getTypeLayout(targetTy);
1401 1459
    try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested);
1402 1460
1403 1461
    let backing = dataBuilderFinish(nested);
1404 1462
    let readOnly = not mutable;
1414 1472
    }
1415 1473
    dataSliceHeader(b, dataName, arrInfo.length);
1416 1474
}
1417 1475
1418 1476
/// Lower a constant expression payload into a builder without slot padding.
1419 -
/// Compute the type layout only when undefined data needs a byte count.
1420 -
unsafe fn lowerConstDataPayloadInto(
1421 -
    self: &mut Lowerer,
1477 +
/// Compute the type layout when nil or undefined data needs a byte count.
1478 +
unsafe fn lowerConstDataPayloadInto 'arena 'phase (
1479 +
    self: &mut Lowerer 'arena 'phase,
1422 1480
    node: *ast::Node,
1423 1481
    ty: resolver::Type,
1424 1482
    dataPrefix: *[u8],
1425 1483
    b: &mut DataValueBuilder
1426 -
) throws (LowerError) {
1484 +
) throws (LowerError) where 'arena: 'phase {
1485 +
    // Optional coercions define the tag and payload layout of constant values.
1486 +
    if let case resolver::Type::Optional(inner) = ty {
1487 +
        if let coercion = resolver::coercionFor(self.resolver, node) {
1488 +
            if let case resolver::Coercion::OptionalLift(target) = coercion {
1489 +
                if resolver::typesEqual(target, ty) {
1490 +
                    if resolver::isNullableType(*inner) {
1491 +
                        try lowerConstDataPayloadInto(self, node, *inner, dataPrefix, b);
1492 +
                    } else {
1493 +
                        let layout = resolver::getTypeLayout(ty);
1494 +
                        let offset = resolver::getOptionalValOffset(*inner);
1495 +
                        dataBuilderPush(b, il::DataValue {
1496 +
                            item: il::DataItem::Val { typ: il::Type::W8, val: 1 }, count: 1,
1497 +
                        });
1498 +
                        if offset > 1 {
1499 +
                            dataBuilderPush(b, il::DataValue { item: il::DataItem::Undef, count: offset - 1 });
1500 +
                        }
1501 +
                        try lowerConstDataInto(self, node, *inner, layout.size - offset, dataPrefix, b);
1502 +
                    }
1503 +
                    return;
1504 +
                }
1505 +
            }
1506 +
        }
1507 +
    }
1427 1508
    // Function pointer references in constant data.
1428 1509
    if let case resolver::Type::Fn(_) = ty {
1429 1510
        let sym = resolver::nodeData(self.resolver, node).sym
1430 1511
            else throw LowerError::MissingSymbol(node);
1431 1512
        let modId = resolver::moduleIdForSymbol(self.resolver, sym);
1444 1525
                return;
1445 1526
            }
1446 1527
        }
1447 1528
    }
1448 1529
    match node.value {
1530 +
        case ast::NodeValue::Nil => {
1531 +
            let case resolver::Type::Optional(_) = ty else throw LowerError::NilInNonOptional;
1532 +
            let layout = resolver::getTypeLayout(ty);
1533 +
            dataBuilderPush(b, il::DataValue {
1534 +
                item: il::DataItem::Val { typ: il::Type::W8, val: 0 }, count: layout.size,
1535 +
            });
1536 +
        },
1449 1537
        case ast::NodeValue::Undef => {
1450 1538
            let layout = resolver::getTypeLayout(ty);
1451 1539
            dataBuilderPush(b, il::DataValue {
1452 1540
                item: il::DataItem::Undef,
1453 1541
                count: layout.size
1498 1586
        }
1499 1587
    }
1500 1588
}
1501 1589
1502 1590
/// Lower a constant expression into a builder, padding to the given slot size.
1503 -
unsafe fn lowerConstDataInto(
1504 -
    self: &mut Lowerer,
1591 +
unsafe fn lowerConstDataInto 'arena 'phase (
1592 +
    self: &mut Lowerer 'arena 'phase,
1505 1593
    node: *ast::Node,
1506 1594
    ty: resolver::Type,
1507 1595
    slotSize: u32,
1508 1596
    dataPrefix: *[u8],
1509 1597
    b: &mut DataValueBuilder
1510 -
) throws (LowerError) {
1598 +
) throws (LowerError) where 'arena: 'phase {
1511 1599
    let layout = resolver::getTypeLayout(ty);
1512 1600
    try lowerConstDataPayloadInto(self, node, ty, dataPrefix, b);
1513 1601
    // Pad to fill the enclosing slot.
1514 1602
    let padding = slotSize - layout.size;
1515 1603
    if padding > 0 {
1517 1605
    }
1518 1606
}
1519 1607
1520 1608
/// Flatten a constant array literal `[a, b, c]` into a builder.
1521 1609
/// Each element payload fills its type size; no extra slot padding is needed.
1522 -
unsafe fn lowerConstArrayLitInto(
1523 -
    self: &mut Lowerer,
1610 +
unsafe fn lowerConstArrayLitInto 'arena 'phase (
1611 +
    self: &mut Lowerer 'arena 'phase,
1524 1612
    elems: *[*ast::Node],
1525 1613
    ty: resolver::Type,
1526 1614
    dataPrefix: *[u8],
1527 1615
    b: &mut DataValueBuilder
1528 -
) throws (LowerError) {
1616 +
) throws (LowerError) where 'arena: 'phase {
1529 1617
    let case resolver::Type::Array(arrInfo) = ty
1530 1618
        else throw LowerError::ExpectedArray;
1531 1619
    let elemTy = *arrInfo.item;
1532 1620
1533 1621
    for elem in elems {
1536 1624
}
1537 1625
1538 1626
/// Build data values for a constant array repeat literal `[item; count]`.
1539 1627
/// Repeat element payloads without extra slot padding. Undefined data uses
1540 1628
/// the element layout to compute the total byte count.
1541 -
unsafe fn lowerConstArrayRepeatInto(
1542 -
    self: &mut Lowerer,
1629 +
unsafe fn lowerConstArrayRepeatInto 'arena 'phase (
1630 +
    self: &mut Lowerer 'arena 'phase,
1543 1631
    repeat: ast::ArrayRepeatLit,
1544 1632
    ty: resolver::Type,
1545 1633
    dataPrefix: *[u8],
1546 1634
    b: &mut DataValueBuilder
1547 -
) throws (LowerError) {
1635 +
) throws (LowerError) where 'arena: 'phase {
1548 1636
    let case resolver::Type::Array(arrInfo) = ty
1549 1637
        else throw LowerError::ExpectedArray;
1550 1638
    let length = arrInfo.length;
1551 1639
    let elemTy = *arrInfo.item;
1552 1640
1641 +
    if let case resolver::Type::Optional(_) = elemTy {
1642 +
        for _ in 0..length {
1643 +
            try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b);
1644 +
        }
1645 +
        return;
1646 +
    }
1647 +
1553 1648
    if let case ast::NodeValue::Undef = repeat.item.value {
1554 1649
        let elemLayout = resolver::getTypeLayout(elemTy);
1555 1650
        dataBuilderPush(b, il::DataValue {
1556 1651
            item: il::DataItem::Undef,
1557 1652
            count: elemLayout.size * length
1576 1671
    }
1577 1672
}
1578 1673
1579 1674
/// Build data values for a constant record literal.
1580 1675
/// Each field is lowered with a slot size that includes trailing padding.
1581 -
unsafe fn lowerConstRecordLitInto(
1582 -
    self: &mut Lowerer,
1676 +
unsafe fn lowerConstRecordLitInto 'arena 'phase (
1677 +
    self: &mut Lowerer 'arena 'phase,
1583 1678
    node: *ast::Node,
1584 1679
    recLit: ast::RecordLit,
1585 1680
    ty: resolver::Type,
1586 1681
    dataPrefix: *[u8],
1587 1682
    b: &mut DataValueBuilder
1588 -
) throws (LowerError) {
1683 +
) throws (LowerError) where 'arena: 'phase {
1589 1684
    match ty {
1590 1685
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
1591 1686
            try lowerConstRecordCtorInto(self, recLit.fields, recInfo, dataPrefix, b);
1592 1687
        }
1593 1688
        case resolver::Type::Nominal(resolver::NominalType::Union(_)) => {
1602 1697
        else => throw LowerError::ExpectedRecord,
1603 1698
    }
1604 1699
}
1605 1700
1606 1701
/// Build data values for record constants.
1607 -
unsafe fn lowerConstRecordCtorInto(
1608 -
    self: &mut Lowerer,
1702 +
unsafe fn lowerConstRecordCtorInto 'arena 'phase (
1703 +
    self: &mut Lowerer 'arena 'phase,
1609 1704
    args: *[*ast::Node],
1610 1705
    recInfo: resolver::RecordType,
1611 1706
    dataPrefix: *[u8],
1612 1707
    b: &mut DataValueBuilder
1613 -
) throws (LowerError) {
1614 -
    let layout = recInfo.layout;
1708 +
) throws (LowerError) where 'arena: 'phase {
1709 +
    let layout = *recInfo.layout;
1615 1710
    for argNode, i in args {
1616 1711
        let mut valueNode = argNode;
1617 1712
        if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value {
1618 1713
            set valueNode = fieldLit.value;
1619 1714
        }
1628 1723
        try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b);
1629 1724
    }
1630 1725
}
1631 1726
1632 1727
/// Build data values for a constant union variant value from payload fields/args.
1633 -
unsafe fn lowerConstUnionVariantInto(
1634 -
    self: &mut Lowerer,
1728 +
unsafe fn lowerConstUnionVariantInto 'arena 'phase (
1729 +
    self: &mut Lowerer 'arena 'phase,
1635 1730
    node: *ast::Node,
1636 1731
    variantSym: *unsafe mut resolver::Symbol,
1637 1732
    ty: resolver::Type,
1638 1733
    payloadArgs: *[*ast::Node],
1639 1734
    dataPrefix: *[u8],
1640 1735
    b: &mut DataValueBuilder
1641 -
) throws (LowerError) {
1642 -
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data
1736 +
) throws (LowerError) where 'arena: 'phase {
1737 +
    let case resolver::SymbolData::Variant { ordinal, index, .. } = variantSym.data
1643 1738
        else throw LowerError::UnexpectedNodeValue(node);
1644 1739
1645 1740
    let unionInfo = unionInfoFromType(ty) else {
1646 1741
        throw LowerError::MissingMetadata;
1647 1742
    };
1743 +
    let payloadType = unionInfo.variants[ordinal].valueType;
1648 1744
    let unionLayout = resolver::getTypeLayout(ty);
1649 1745
    let payloadSlotSize = unionLayout.size - unionInfo.valOffset;
1650 1746
1651 1747
    // Tag byte.
1652 1748
    dataBuilderPush(b, il::DataValue {
1674 1770
    }
1675 1771
1676 1772
    let case resolver::Type::Nominal(resolver::NominalType::Record(payloadRec)) = payloadType else {
1677 1773
        throw LowerError::ExpectedRecord;
1678 1774
    };
1679 -
    let payloadLayout = payloadRec.layout;
1775 +
    let payloadLayout = *payloadRec.layout;
1680 1776
    try lowerConstRecordCtorInto(self, payloadArgs, payloadRec, dataPrefix, b);
1681 1777
1682 1778
    // Unused bytes in the union payload slot for smaller variants.
1683 1779
    if payloadSlotSize > payloadLayout.size {
1684 1780
        dataBuilderPush(b, il::DataValue {
1688 1784
    }
1689 1785
}
1690 1786
1691 1787
/// Find an existing string data entry with matching content.
1692 1788
// TODO: Optimize with hash table or remove?
1693 -
fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] {
1694 -
    for d in &self.data[..] {
1789 +
fn findStringData 'arena 'phase (self: &Lowerer 'arena 'phase, s: *[u8]) -> ?*[u8] where 'arena: 'phase {
1790 +
    for d in &self.data[self.packageDataStart..] {
1695 1791
        if d.values.len == 1 {
1696 1792
            if let case il::DataItem::Str(existing) = d.values[0].item {
1697 1793
                if mem::eq(existing, s) {
1698 1794
                    return d.name;
1699 1795
                }
1703 1799
    return nil;
1704 1800
}
1705 1801
1706 1802
/// Compose a segmented symbol name from a list of path segments.
1707 1803
/// Example: `["func", "nominal", "VALUE"]` -> `func$nominal$VALUE`.
1708 -
unsafe fn buildSegmentedName(
1709 -
    self: &mut Lowerer,
1804 +
unsafe fn buildSegmentedName 'arena 'phase (
1805 +
    self: &mut Lowerer 'arena 'phase,
1710 1806
    segments: &[*[u8]]
1711 -
) -> *[u8] throws (LowerError) {
1807 +
) -> *[u8] throws (LowerError) where 'arena: 'phase {
1712 1808
    assert segments.len > 0;
1713 1809
1714 1810
    let mut totalLen: u32 = 0;
1715 1811
    for segment in segments {
1716 1812
        set totalLen += segment.len;
1732 1828
1733 1829
    return &buf[..totalLen];
1734 1830
}
1735 1831
1736 1832
/// Generate a unique name for declaration-local backing data entries.
1737 -
unsafe fn nextDeclDataName(
1738 -
    self: &mut Lowerer,
1833 +
unsafe fn nextDeclDataName 'arena 'phase (
1834 +
    self: &mut Lowerer 'arena 'phase,
1739 1835
    prefix: *[u8],
1740 1836
    count: u32,
1741 1837
    namespace: *[u8]
1742 -
) -> *[u8] throws (LowerError) {
1838 +
) -> *[u8] throws (LowerError) where 'arena: 'phase {
1743 1839
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1744 1840
    let start = fmt::formatU32(count, &mut digits[..]);
1745 1841
    let suffix = try! alloc::allocSlice(self.arena, 1, 1, digits.len - start) as *mut [u8];
1746 1842
    try! mem::copy(suffix, &digits[start..]);
1747 1843
    let segments = [prefix, namespace, suffix];
1748 1844
1749 1845
    return try buildSegmentedName(self, &segments[..]);
1750 1846
}
1751 1847
1752 1848
/// Append a data entry using a function-local literal namespace (`prefix$literal$N`).
1753 -
unsafe fn pushDeclData(
1754 -
    self: &mut Lowerer,
1849 +
unsafe fn pushDeclData 'arena 'phase (
1850 +
    self: &mut Lowerer 'arena 'phase,
1755 1851
    size: u32,
1756 1852
    alignment: u32,
1757 1853
    readOnly: bool,
1758 1854
    values: *[il::DataValue],
1759 1855
    dataPrefix: *[u8]
1760 -
) -> *[u8] throws (LowerError) {
1856 +
) -> *[u8] throws (LowerError) where 'arena: 'phase {
1761 1857
    let dataCount = self.data.len;
1762 1858
    let name = try nextDeclDataName(self, dataPrefix, dataCount, "literal");
1763 1859
    self.data.append(il::Data {
1764 1860
        name,
1765 1861
        size,
1766 1862
        alignment,
1767 1863
        readOnly,
1768 1864
        isZeroInit: not readOnly and dataValuesAreZeroInit(values),
1769 1865
        values,
1770 -
    }, self.allocator);
1866 +
    }, alloc::arenaAllocator(self.arena));
1771 1867
1772 1868
    return name;
1773 1869
}
1774 1870
1775 1871
/// Find or create read-only string data and return its symbol name.
1776 -
unsafe fn getOrCreateStringData(
1777 -
    self: &mut Lowerer,
1872 +
unsafe fn getOrCreateStringData 'arena 'phase (
1873 +
    self: &mut Lowerer 'arena 'phase,
1778 1874
    s: *[u8],
1779 1875
    dataPrefix: *[u8]
1780 -
) -> *[u8] throws (LowerError) {
1876 +
) -> *[u8] throws (LowerError) where 'arena: 'phase {
1781 1877
    if let existing = findStringData(self, s) {
1782 1878
        return existing;
1783 1879
    }
1784 1880
    let values = try! alloc::allocSlice(
1785 1881
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1
1845 1941
    return true;
1846 1942
}
1847 1943
1848 1944
/// Find an existing read-only slice data entry with matching values.
1849 1945
// TODO: Optimize with hash table or remove?
1850 -
fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1851 -
    for d in &self.data[..] {
1946 +
fn findSliceData 'arena 'phase (self: &Lowerer 'arena 'phase, values: *[il::DataValue], alignment: u32) -> ?*[u8] where 'arena: 'phase {
1947 +
    for d in &self.data[self.packageDataStart..] {
1852 1948
        if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) {
1853 1949
            return d.name;
1854 1950
        }
1855 1951
    }
1856 1952
    return nil;
1857 1953
}
1858 1954
1859 1955
/// Find existing constant data entry with matching content.
1860 1956
/// Handles both string data and slice data.
1861 -
fn findConstData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1957 +
fn findConstData 'arena 'phase (self: &Lowerer 'arena 'phase, values: *[il::DataValue], alignment: u32) -> ?*[u8] where 'arena: 'phase {
1862 1958
    // Fast path for strings.
1863 1959
    if values.len == 1 and alignment == 1 {
1864 1960
        if let case il::DataItem::Str(s) = values[0].item {
1865 1961
            return findStringData(self, s);
1866 1962
        }
1869 1965
    return findSliceData(self, values, alignment);
1870 1966
}
1871 1967
1872 1968
/// Lower constant data to a slice value.
1873 1969
/// Creates or reuses a data section entry, then builds a slice header on the stack.
1874 -
unsafe fn lowerConstDataAsSlice(
1875 -
    self: &mut FnLowerer,
1970 +
unsafe fn lowerConstDataAsSlice 'arena 'phase 'function (
1971 +
    self: &mut FnLowerer 'arena 'phase 'function,
1876 1972
    result: &ConstDataResult,
1877 1973
    alignment: u32,
1878 1974
    readOnly: bool,
1879 1975
    elemTy: *resolver::Type,
1880 1976
    mutable: bool,
1881 1977
    length: u32
1882 -
) -> il::Val throws (LowerError) {
1978 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1883 1979
    let values = result.values;
1884 1980
    let elemLayout = resolver::getTypeLayout(*elemTy);
1885 1981
    let size = elemLayout.size * length;
1886 1982
    let mut dataName: *[u8] = undefined;
1887 1983
    let mut found: ?*[u8] = nil;
1897 1993
            size,
1898 1994
            alignment,
1899 1995
            readOnly,
1900 1996
            isZeroInit: not readOnly and result.zeroInit,
1901 1997
            values,
1902 -
        }, self.low.allocator);
1998 +
        }, alloc::arenaAllocator(self.low.arena));
1903 1999
    }
1904 2000
1905 2001
    // Get data address.
1906 2002
    let ptrReg = nextReg(self);
1907 2003
    emit(self, il::Instr::Copy { dst: ptrReg, val: il::Val::DataSym(dataName) });
1910 2006
        self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64)
1911 2007
    );
1912 2008
}
1913 2009
1914 2010
/// Generate a unique data name for inline literals, eg. `fnName$literal$N`.
1915 -
unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) {
2011 +
unsafe fn nextDataName 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1916 2012
    let counter = self.dataCounter;
1917 2013
    set self.dataCounter += 1;
1918 2014
    let fnName = self.fnName;
1919 2015
    return try nextDeclDataName(self.low, fnName, counter, "literal");
1920 2016
}
1921 2017
1922 2018
/// Assign a unique function-local data symbol name.
1923 -
unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
2019 +
unsafe fn registerLocalDataDeclName 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1924 2020
    let sym = resolver::nodeData(self.low.resolver, node).sym
1925 2021
        else throw LowerError::MissingSymbol(node);
1926 2022
1927 2023
    let prefix = self.fnName;
1928 2024
    let segments = [prefix, "nominal", sym.name];
1929 2025
    let name = try buildSegmentedName(self.low, &segments[..]);
1930 2026
1931 -
    set sym.name = name;
2027 +
    let qualified = qualifyName(self.low, nil, name);
2028 +
    registerSymbolName(self.low, sym, qualified);
1932 2029
}
1933 2030
1934 2031
/// Get the next available SSA register.
1935 -
fn nextReg(self: &mut FnLowerer) -> il::Reg {
2032 +
fn nextReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> il::Reg where 'arena: 'phase, 'phase: 'function {
1936 2033
    let reg = il::Reg { n: self.regCounter };
1937 2034
    set self.regCounter += 1;
1938 2035
    return reg;
1939 2036
}
1940 2037
1941 2038
/// Look up the resolved type of an AST node, or throw `MissingType`.
1942 -
unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
2039 +
unsafe fn typeOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1943 2040
    let ty = resolver::typeFor(self.low.resolver, node)
1944 2041
        else throw LowerError::MissingType(node);
1945 2042
    return ty;
1946 2043
}
1947 2044
1948 2045
/// Look up the symbol for an AST node, or throw `MissingSymbol`.
1949 -
unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) {
2046 +
unsafe fn symOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) where 'arena: 'phase, 'phase: 'function {
1950 2047
    let sym = resolver::nodeData(self.low.resolver, node).sym
1951 2048
        else throw LowerError::MissingSymbol(node);
1952 2049
    return sym;
1953 2050
}
1954 2051
1955 2052
/// Remove the last block parameter and its associated variable.
1956 2053
/// Used when detecting a trivial phi that can be eliminated.
1957 -
unsafe fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) {
2054 +
unsafe fn removeLastBlockParam 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) where 'arena: 'phase, 'phase: 'function {
1958 2055
    let blk = getBlockMut(self, block);
1959 2056
    if blk.params.len > 0 {
1960 2057
        // TODO: Use `pop`?
1961 2058
        set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap);
1962 2059
    }
1969 2066
/// Rewrite cached SSA values for a variable across all blocks, and also
1970 2067
/// rewrite any terminator arguments that reference the provisional register.
1971 2068
/// The latter is necessary because recursive SSA resolution may have already
1972 2069
/// patched terminator arguments with the provisional value before it was
1973 2070
/// found to be trivial.
1974 -
unsafe fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) {
2071 +
unsafe fn rewriteCachedVarValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, from: il::Val, to: il::Val) where 'arena: 'phase, 'phase: 'function {
1975 2072
    for i in 0..self.blockData.len {
1976 2073
        let blk = getBlockMut(self, BlockId(i));
1977 2074
        if blk.vars[*v] == from {
1978 2075
            set blk.vars[*v] = to;
1979 2076
        }
2026 2123
/// Create a new basic block with the given label base.
2027 2124
///
2028 2125
/// The block is initially unsealed (predecessors may be added later) and empty.
2029 2126
/// Returns a [`BlockId`] that can be used for jumps and branches. The block must
2030 2127
/// be switched to via [`switchToBlock`] before instructions can be emitted.
2031 -
unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) {
2128 +
unsafe fn createBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, labelBase: *[u8]) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2032 2129
    let label = try nextLabel(self, labelBase);
2033 2130
    let id = BlockId(self.blockData.len);
2034 2131
    let varCount = self.localCount;
2035 -
    let vars = try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val];
2132 +
    let vars = try! alloc::allocRawSlice(self.arena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val];
2036 2133
2037 2134
    for i in 0..varCount {
2038 2135
        set vars[i] = nil;
2039 2136
    }
2040 2137
    self.blockData.append(BlockData {
2045 2142
        locs: &mut [],
2046 2143
        preds: &mut [],
2047 2144
        vars,
2048 2145
        sealState: Sealed::No,
2049 2146
        loopDepth: self.loopDepth,
2050 -
    }, self.allocator);
2147 +
    }, alloc::arenaAllocator(self.arena));
2051 2148
2052 2149
    return id;
2053 2150
}
2054 2151
2055 2152
/// Create a new block with a single parameter.
2056 -
unsafe fn createBlockWithParam(
2057 -
    self: &mut FnLowerer,
2153 +
unsafe fn createBlockWithParam 'arena 'phase 'function (
2154 +
    self: &mut FnLowerer 'arena 'phase 'function,
2058 2155
    labelBase: *[u8],
2059 2156
    param: il::Param
2060 -
) -> BlockId throws (LowerError) {
2157 +
) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2061 2158
    let block = try createBlock(self, labelBase);
2062 2159
    let blk = getBlockMut(self, block);
2063 -
    blk.params.append(param, self.allocator);
2160 +
    blk.params.append(param, alloc::arenaAllocator(self.arena));
2064 2161
2065 2162
    return block;
2066 2163
}
2067 2164
2068 2165
/// Switch to building a different block.
2069 2166
/// All subsequent `emit` calls will add instructions to this block.
2070 -
fn switchToBlock(self: &mut FnLowerer, block: BlockId) {
2167 +
fn switchToBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) where 'arena: 'phase, 'phase: 'function {
2071 2168
    set self.currentBlock = block;
2072 2169
}
2073 2170
2074 2171
/// Seal a block, indicating all predecessor edges are now known.
2075 2172
///
2076 2173
/// Sealing enables SSA construction to resolve variable uses by looking up
2077 2174
/// values from predecessors and inserting block parameters as needed. It
2078 2175
/// does not prevent instructions from being added to the block.
2079 -
unsafe fn sealBlock(self: &mut FnLowerer, block: BlockId) throws (LowerError) {
2176 +
unsafe fn sealBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2080 2177
    let blk = getBlockMut(self, block);
2081 2178
    let case Sealed::No = blk.sealState else {
2082 2179
        return; // Already sealed.
2083 2180
    };
2084 2181
    // Keep the current parameter list. Resolution can add more parameters.
2090 2187
        try resolveBlockArgs(self, block, Var(varId), paramIdx);
2091 2188
    }
2092 2189
}
2093 2190
2094 2191
/// Seal a block and switch to it.
2095 -
unsafe fn switchToAndSeal(self: &mut FnLowerer, block: BlockId) throws (LowerError) {
2192 +
unsafe fn switchToAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2096 2193
    try sealBlock(self, block);
2097 2194
    switchToBlock(self, block);
2098 2195
}
2099 2196
2100 2197
/// Get block data by block id.
2101 -
unsafe fn getBlock(self: &FnLowerer, block: BlockId) -> *unsafe BlockData {
2198 +
unsafe fn getBlock 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, block: BlockId) -> *unsafe BlockData where 'arena: 'phase, 'phase: 'function {
2102 2199
    return &self.blockData[*block];
2103 2200
}
2104 2201
2105 2202
/// Get mutable block data by block id.
2106 -
unsafe fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *unsafe mut BlockData {
2203 +
unsafe fn getBlockMut 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) -> *unsafe mut BlockData where 'arena: 'phase, 'phase: 'function {
2107 2204
    return &mut self.blockData[*block];
2108 2205
}
2109 2206
2110 2207
/// Get the current block being built.
2111 -
fn currentBlock(self: &FnLowerer) -> BlockId {
2208 +
fn currentBlock 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> BlockId where 'arena: 'phase, 'phase: 'function {
2112 2209
    let block = self.currentBlock else {
2113 2210
        panic "currentBlock: no current block";
2114 2211
    };
2115 2212
    return block;
2116 2213
}
2118 2215
//////////////////////////
2119 2216
// Instruction Emission //
2120 2217
//////////////////////////
2121 2218
2122 2219
/// Emit an instruction to the current block.
2123 -
unsafe fn emit(self: &mut FnLowerer, instr: il::Instr) {
2220 +
unsafe fn emit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, instr: il::Instr) where 'arena: 'phase, 'phase: 'function {
2124 2221
    let blk = self.currentBlock else panic;
2125 2222
    let mut block = getBlockMut(self, blk);
2126 2223
2127 2224
    // Track whether this function is a leaf.
2128 -
    if self.isLeaf {
2129 -
        match instr {
2130 -
            case il::Instr::Call { .. },
2131 -
                 il::Instr::Ecall { .. } => set self.isLeaf = false,
2132 -
            else => {},
2133 -
        }
2225 +
    if il::isCall(instr) {
2226 +
        set self.isLeaf = false;
2134 2227
    }
2135 2228
    // Record source location alongside instruction when enabled.
2136 2229
    if self.low.options.debug {
2137 -
        block.locs.append(self.srcLoc, self.allocator);
2230 +
        block.locs.append(self.srcLoc, alloc::arenaAllocator(self.arena));
2138 2231
    }
2139 -
    block.instrs.append(instr, self.allocator);
2232 +
    block.instrs.append(instr, alloc::arenaAllocator(self.arena));
2140 2233
}
2141 2234
2142 2235
/// Emit an unconditional jump to `target`.
2143 -
unsafe fn emitJmp(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2236 +
unsafe fn emitJmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2144 2237
    emit(self, il::Instr::Jmp { target: *target, args: &mut [] });
2145 2238
    addPredecessor(self, target, currentBlock(self));
2146 2239
}
2147 2240
2148 2241
/// Emit an unconditional jump to `target` with a single argument.
2149 -
unsafe fn emitJmpWithArg(self: &mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) {
2242 +
unsafe fn emitJmpWithArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, arg: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2150 2243
    let args = try allocVal(self, arg);
2151 2244
    emit(self, il::Instr::Jmp { target: *target, args });
2152 2245
    addPredecessor(self, target, currentBlock(self));
2153 2246
}
2154 2247
2155 2248
/// Emit an unconditional jump to `target` and switch to it.
2156 -
unsafe fn switchAndJumpTo(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2249 +
unsafe fn switchAndJumpTo 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2157 2250
    try emitJmp(self, target);
2158 2251
    switchToBlock(self, target);
2159 2252
}
2160 2253
2161 2254
/// Emit a conditional branch based on `cond`.
2162 -
unsafe fn emitBr(self: &mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) {
2255 +
unsafe fn emitBr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2163 2256
    assert thenBlock <> elseBlock;
2164 2257
    emit(self, il::Instr::Br {
2165 2258
        op: il::CmpOp::Ne,
2166 2259
        typ: il::Type::W32,
2167 2260
        a: il::Val::Reg(cond),
2174 2267
    addPredecessor(self, thenBlock, currentBlock(self));
2175 2268
    addPredecessor(self, elseBlock, currentBlock(self));
2176 2269
}
2177 2270
2178 2271
/// Emit a compare-and-branch instruction with the given comparison op.
2179 -
unsafe fn emitBrCmp(
2180 -
    self: &mut FnLowerer,
2272 +
unsafe fn emitBrCmp 'arena 'phase 'function (
2273 +
    self: &mut FnLowerer 'arena 'phase 'function,
2181 2274
    op: il::CmpOp,
2182 2275
    typ: il::Type,
2183 2276
    a: il::Val,
2184 2277
    b: il::Val,
2185 2278
    thenBlock: BlockId,
2186 2279
    elseBlock: BlockId
2187 -
) throws (LowerError) {
2280 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2188 2281
    assert thenBlock <> elseBlock;
2189 2282
    emit(self, il::Instr::Br {
2190 2283
        op, typ, a, b,
2191 2284
        thenTarget: *thenBlock, thenArgs: &mut [],
2192 2285
        elseTarget: *elseBlock, elseArgs: &mut [],
2194 2287
    addPredecessor(self, thenBlock, currentBlock(self));
2195 2288
    addPredecessor(self, elseBlock, currentBlock(self));
2196 2289
}
2197 2290
2198 2291
/// Emit a guard that traps with `ebreak` when a comparison is false.
2199 -
unsafe fn emitTrapUnlessCmp(
2200 -
    self: &mut FnLowerer,
2292 +
unsafe fn emitTrapUnlessCmp 'arena 'phase 'function (
2293 +
    self: &mut FnLowerer 'arena 'phase 'function,
2201 2294
    op: il::CmpOp,
2202 2295
    typ: il::Type,
2203 2296
    a: il::Val,
2204 2297
    b: il::Val
2205 -
) throws (LowerError) {
2298 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2206 2299
    let passBlock = try createBlock(self, "guard#pass");
2207 2300
    let trapBlock = try createBlock(self, "guard#trap");
2208 2301
2209 2302
    try emitBrCmp(self, op, typ, a, b, passBlock, trapBlock);
2210 2303
    try switchToAndSeal(self, trapBlock);
2234 2327
    }
2235 2328
    return aa <= bb;
2236 2329
}
2237 2330
2238 2331
/// Emit an `ebreak` when `a < b` holds.
2239 -
unsafe fn emitTrapIfLt(
2240 -
    self: &mut FnLowerer,
2332 +
unsafe fn emitTrapIfLt 'arena 'phase 'function (
2333 +
    self: &mut FnLowerer 'arena 'phase 'function,
2241 2334
    typ: il::Type,
2242 2335
    a: il::Val,
2243 2336
    b: il::Val
2244 -
) throws (LowerError) {
2337 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2245 2338
    let trapBlock = try createBlock(self, "guard#trap");
2246 2339
    let passBlock = try createBlock(self, "guard#pass");
2247 2340
2248 2341
    try emitBrCmp(self, il::CmpOp::Ult, typ, a, b, trapBlock, passBlock);
2249 2342
    try switchToAndSeal(self, trapBlock);
2254 2347
    try switchToAndSeal(self, passBlock);
2255 2348
}
2256 2349
2257 2350
/// Emit a conditional branch. Uses fused compare-and-branch for simple scalar
2258 2351
/// comparisons, falls back to separate comparison plus branch otherwise.
2259 -
unsafe fn emitCondBranch(
2260 -
    self: &mut FnLowerer,
2352 +
unsafe fn emitCondBranch 'arena 'phase 'function (
2353 +
    self: &mut FnLowerer 'arena 'phase 'function,
2261 2354
    cond: *ast::Node,
2262 2355
    thenBlock: BlockId,
2263 2356
    elseBlock: BlockId
2264 -
) throws (LowerError) {
2357 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2265 2358
    // Try fused compare-and-branch for simple scalar comparisons.
2266 2359
    if let case ast::NodeValue::BinOp(binop) = cond.value {
2267 2360
        let leftTy = try typeOf(self, binop.left);
2268 2361
        let rightTy = try typeOf(self, binop.right);
2269 2362
        if not isAggregateType(leftTy) and not isAggregateType(rightTy) {
2295 2388
2296 2389
    try emitBr(self, condReg, thenBlock, elseBlock);
2297 2390
}
2298 2391
2299 2392
/// Emit a 32-bit store instruction at the given offset.
2300 -
unsafe fn emitStoreW32At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2393 +
unsafe fn emitStoreW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2301 2394
    emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset });
2302 2395
}
2303 2396
2304 2397
/// Emit a 32-bit load instruction at the given offset.
2305 -
unsafe fn emitLoadW32At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2398 +
unsafe fn emitLoadW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2306 2399
    emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset });
2307 2400
}
2308 2401
2309 2402
/// Emit an 8-bit store instruction at the given offset.
2310 -
unsafe fn emitStoreW8At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2403 +
unsafe fn emitStoreW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2311 2404
    emit(self, il::Instr::Store { typ: il::Type::W8, src, dst, offset });
2312 2405
}
2313 2406
2314 2407
/// Emit an 8-bit load instruction at the given offset.
2315 -
unsafe fn emitLoadW8At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2408 +
unsafe fn emitLoadW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2316 2409
    emit(self, il::Instr::Load { typ: il::Type::W8, dst, src, offset });
2317 2410
}
2318 2411
2319 2412
/// Emit a 64-bit store instruction at the given offset.
2320 -
unsafe fn emitStoreW64At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2413 +
unsafe fn emitStoreW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2321 2414
    emit(self, il::Instr::Store { typ: il::Type::W64, src, dst, offset });
2322 2415
}
2323 2416
2324 2417
/// Emit a 64-bit load instruction at the given offset.
2325 -
unsafe fn emitLoadW64At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2418 +
unsafe fn emitLoadW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function {
2326 2419
    emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset });
2327 2420
}
2328 2421
2329 2422
/// Load a tag from memory at `src` plus `offset` with the given IL type.
2330 -
unsafe fn loadTag(self: &mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val {
2423 +
unsafe fn loadTag 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val where 'arena: 'phase, 'phase: 'function {
2331 2424
    let dst = nextReg(self);
2332 2425
    emit(self, il::Instr::Load { typ: tagType, dst, src, offset });
2333 2426
    return il::Val::Reg(dst);
2334 2427
}
2335 2428
2336 2429
/// Load the data pointer from a slice value.
2337 -
unsafe fn loadSlicePtr(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Reg {
2430 +
unsafe fn loadSlicePtr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function {
2338 2431
    let ptrReg = nextReg(self);
2339 2432
    emitLoadW64At(self, ptrReg, sliceReg, SLICE_PTR_OFFSET);
2340 2433
    return ptrReg;
2341 2434
}
2342 2435
2343 2436
/// Load the length from a slice value.
2344 -
unsafe fn loadSliceLen(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2437 +
unsafe fn loadSliceLen 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function {
2345 2438
    let lenReg = nextReg(self);
2346 2439
    emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET);
2347 2440
    return il::Val::Reg(lenReg);
2348 2441
}
2349 2442
2350 2443
/// Load the capacity from a slice value.
2351 -
unsafe fn loadSliceCap(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2444 +
unsafe fn loadSliceCap 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function {
2352 2445
    let capReg = nextReg(self);
2353 2446
    emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET);
2354 2447
    return il::Val::Reg(capReg);
2355 2448
}
2356 2449
2357 2450
/// Emit a load instruction for a scalar value at `src` plus `offset`.
2358 2451
/// For reading values that may be aggregates, use `emitRead` instead.
2359 -
unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2452 +
unsafe fn emitLoad 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function {
2360 2453
    let dst = nextReg(self);
2361 2454
    let ilTyp = ilType(self.low, typ);
2362 2455
2363 2456
    if isSignedType(typ) {
2364 2457
        emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset });
2368 2461
    return il::Val::Reg(dst);
2369 2462
}
2370 2463
2371 2464
/// Read a value from memory at `src` plus `offset`. Aggregates are represented
2372 2465
/// as pointers, so we return the address directly. Scalars are loaded via [`emitLoad`].
2373 -
unsafe fn emitRead(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2466 +
unsafe fn emitRead 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function {
2374 2467
    if isAggregateType(typ) {
2375 2468
        let ptr = emitPtrOffset(self, src, offset);
2376 2469
        return il::Val::Reg(ptr);
2377 2470
    }
2378 2471
    return emitLoad(self, src, offset, typ);
2379 2472
}
2380 2473
2381 2474
/// Emit a copy instruction that loads a data symbol's address into a register.
2382 -
unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg {
2475 +
unsafe fn emitDataAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol) -> il::Reg where 'arena: 'phase, 'phase: 'function {
2383 2476
    let dst = nextReg(self);
2384 2477
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2385 -
    let qualName = qualifyName(self.low, modId, sym.name);
2478 +
    let qualName = lookupSymbolName(self.low, sym) else qualifyName(self.low, modId, sym.name);
2386 2479
2387 2480
    emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) });
2388 2481
2389 2482
    return dst;
2390 2483
}
2391 2484
2392 2485
/// Emit a copy instruction that loads a function's address into a register.
2393 -
unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg {
2486 +
unsafe fn emitFnAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol) -> il::Reg where 'arena: 'phase, 'phase: 'function {
2394 2487
    let dst = nextReg(self);
2395 2488
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2396 2489
    let qualName = qualifyName(self.low, modId, sym.name);
2397 2490
2398 2491
    emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) });
2399 2492
2400 2493
    return dst;
2401 2494
}
2402 2495
2403 2496
/// Emit pattern tests for a single pattern.
2404 -
unsafe fn emitPatternMatch(
2405 -
    self: &mut FnLowerer,
2497 +
unsafe fn emitPatternMatch 'arena 'phase 'function (
2498 +
    self: &mut FnLowerer 'arena 'phase 'function,
2406 2499
    subject: &MatchSubject,
2407 2500
    pattern: *ast::Node,
2408 2501
    matchBlock: BlockId,
2409 2502
    fallthrough: BlockId
2410 -
) throws (LowerError) {
2503 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2411 2504
    // Wildcards always match; array patterns are tested element-by-element
2412 2505
    // during binding, so they also unconditionally enter the match block.
2413 2506
    if isWildcardPattern(pattern) {
2414 2507
        try emitJmp(self, matchBlock);
2415 2508
        return;
2417 2510
    if let case ast::NodeValue::ArrayLit(_) = pattern.value {
2418 2511
        try emitJmp(self, matchBlock);
2419 2512
        return;
2420 2513
    }
2421 2514
    if let case resolver::Type::Nominal(resolver::NominalType::Record(_)) = subject.type {
2422 -
        if let case ast::NodeValue::RecordLit(_) = pattern.value {
2423 -
            try emitJmp(self, matchBlock);
2424 -
            return;
2515 +
        match pattern.value {
2516 +
            case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
2517 +
                try emitJmp(self, matchBlock);
2518 +
                return;
2519 +
            }
2520 +
            else => {
2521 +
            },
2425 2522
        }
2426 2523
    }
2427 2524
    let isNil = pattern.value == ast::NodeValue::Nil;
2428 2525
2429 2526
    match subject.kind {
2497 2594
}
2498 2595
2499 2596
/// Emit branches for multiple patterns. The first pattern that matches
2500 2597
/// causes a jump to the match block. If no patterns match, we jump to the
2501 2598
/// fallthrough block.
2502 -
unsafe fn emitPatternMatches(
2503 -
    self: &mut FnLowerer,
2599 +
unsafe fn emitPatternMatches 'arena 'phase 'function (
2600 +
    self: &mut FnLowerer 'arena 'phase 'function,
2504 2601
    subject: &MatchSubject,
2505 2602
    patterns: &[*ast::Node],
2506 2603
    matchBlock: BlockId,
2507 2604
    fallthrough: BlockId
2508 -
) throws (LowerError) {
2605 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2509 2606
    assert patterns.len > 0;
2510 2607
2511 2608
    for i in 0..(patterns.len - 1) {
2512 2609
        let pattern = patterns[i];
2513 2610
        let nextArm = try createBlock(self, "arm");
2526 2623
2527 2624
/// Emit a match binding pattern.
2528 2625
/// Binding patterns always match for regular values, but for optionals they
2529 2626
/// check for the presence of a value. Jumps to `valuePresent` on success,
2530 2627
/// `valueAbsent` on failure.
2531 -
unsafe fn emitBindingTest(
2532 -
    self: &mut FnLowerer,
2628 +
unsafe fn emitBindingTest 'arena 'phase 'function (
2629 +
    self: &mut FnLowerer 'arena 'phase 'function,
2533 2630
    subject: &MatchSubject,
2534 2631
    valuePresent: BlockId,
2535 2632
    valueAbsent: BlockId
2536 -
) throws (LowerError) {
2633 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2537 2634
    match subject.kind {
2538 2635
        case MatchSubjectKind::OptionalPtr, MatchSubjectKind::OptionalAggregate => {
2539 2636
            let nilReg = try optionalNilReg(self, subject.val, subject.type);
2540 2637
            try emitBr(self, nilReg, valuePresent, valueAbsent);
2541 2638
        }
2545 2642
        }
2546 2643
    }
2547 2644
}
2548 2645
2549 2646
/// Emit a jump to target if the current block hasn't terminated, then seal the target block.
2550 -
unsafe fn emitJmpAndSeal(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2647 +
unsafe fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2551 2648
    if not blockHasTerminator(self) {
2552 2649
        try emitJmp(self, target);
2553 2650
    }
2554 2651
    try sealBlock(self, target);
2555 2652
}
2556 2653
2557 2654
/// Check if the current block already has a terminator instruction.
2558 -
unsafe fn blockHasTerminator(self: &FnLowerer) -> bool {
2655 +
unsafe fn blockHasTerminator 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> bool where 'arena: 'phase, 'phase: 'function {
2559 2656
    let blk = getBlock(self, currentBlock(self));
2560 2657
    if blk.instrs.len == 0 {
2561 2658
        return false;
2562 2659
    }
2563 2660
    match blk.instrs[blk.instrs.len - 1] {
2585 2682
///         return 0;   // @else diverges, no jump to merge.
2586 2683
///     }
2587 2684
///
2588 2685
/// In the above example, the merge block stays `nil`, and no code is generated
2589 2686
/// after the `if`. The merge block is created on first use.
2590 -
unsafe fn emitMergeIfUnterminated(self: &mut FnLowerer, mergeBlock: &mut ?BlockId) throws (LowerError) {
2687 +
unsafe fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2591 2688
    if not blockHasTerminator(self) {
2592 2689
        if *mergeBlock == nil {
2593 2690
            set *mergeBlock = try createBlock(self, "merge");
2594 2691
        }
2595 2692
        let target = *mergeBlock else { throw LowerError::MissingTarget; };
2601 2698
// Control Flow Edge Management //
2602 2699
//////////////////////////////////
2603 2700
2604 2701
/// Add a predecessor edge from `pred` to `target`.
2605 2702
/// Must be called before the target block is sealed. Duplicates are ignored.
2606 -
unsafe fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) {
2703 +
unsafe fn addPredecessor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, pred: BlockId) where 'arena: 'phase, 'phase: 'function {
2607 2704
    let blk = getBlockMut(self, target);
2608 2705
    assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block";
2609 2706
    let preds = &mut blk.preds;
2610 2707
    for i in 0..preds.len {
2611 2708
        if preds[i] == *pred { // Avoid duplicate predecessor entries.
2612 2709
            return;
2613 2710
        }
2614 2711
    }
2615 -
    preds.append(*pred, self.allocator);
2712 +
    preds.append(*pred, alloc::arenaAllocator(self.arena));
2616 2713
}
2617 2714
2618 2715
/// Finalize all blocks and return the block array.
2619 -
unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *unsafe [il::Block] throws (LowerError) {
2716 +
unsafe fn finalizeBlocks 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> *unsafe [il::Block] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2620 2717
    let blockCount = self.blockData.len;
2621 2718
    let blocks = try! alloc::allocRawSlice(
2622 -
        self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount
2719 +
        self.arena, @sizeOf(il::Block), @alignOf(il::Block), blockCount
2623 2720
    ) as *unsafe mut [il::Block];
2624 2721
2625 2722
    for i in 0..self.blockData.len {
2626 2723
        let data = &self.blockData[i];
2627 2724
2641 2738
// Loop Management //
2642 2739
/////////////////////
2643 2740
2644 2741
/// Enter a loop context for break/continue handling.
2645 2742
/// `continueBlock` is `nil` when the continue target is created lazily.
2646 -
unsafe fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) {
2743 +
fn enterLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, breakBlock: BlockId, continueBlock: ?BlockId) where 'arena: 'phase, 'phase: 'function {
2647 2744
    assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow";
2648 2745
    let slot = &mut self.loopStack[self.loopDepth];
2649 2746
2650 2747
    set slot.breakTarget = breakBlock;
2651 2748
    set slot.continueTarget = continueBlock;
2652 2749
    set self.loopDepth += 1;
2653 2750
}
2654 2751
2655 2752
/// Exit the current loop context.
2656 -
fn exitLoop(self: &mut FnLowerer) {
2753 +
fn exitLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) where 'arena: 'phase, 'phase: 'function {
2657 2754
    assert self.loopDepth <> 0, "exitLoop: loopDepth is zero";
2658 2755
    set self.loopDepth -= 1;
2659 2756
}
2660 2757
2661 -
/// Get the current loop context.
2662 -
unsafe fn currentLoop(self: &mut FnLowerer) -> ?*unsafe mut LoopCtx {
2758 +
/// Copy the current loop targets.
2759 +
fn currentLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> ?LoopCtx where 'arena: 'phase, 'phase: 'function {
2663 2760
    if self.loopDepth == 0 {
2664 2761
        return nil;
2665 2762
    }
2666 -
    return &mut self.loopStack[self.loopDepth - 1];
2763 +
    return self.loopStack[self.loopDepth - 1];
2667 2764
}
2668 2765
2669 2766
/// Get or lazily create the continue target block for the current loop.
2670 -
unsafe fn getOrCreateContinueBlock(self: &mut FnLowerer) -> BlockId throws (LowerError) {
2767 +
unsafe fn getOrCreateContinueBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2671 2768
    let ctx = currentLoop(self) else {
2672 2769
        throw LowerError::OutsideOfLoop;
2673 2770
    };
2674 2771
    if let block = ctx.continueTarget {
2675 2772
        return block;
2676 2773
    }
2677 2774
    let block = try createBlock(self, "step");
2678 -
    set ctx.continueTarget = block;
2775 +
    set self.loopStack[self.loopDepth - 1].continueTarget = block;
2679 2776
    return block;
2680 2777
}
2681 2778
2682 2779
/// Allocate a slice of values in the lowering arena.
2683 -
unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *unsafe mut [il::Val] throws (LowerError) {
2684 -
    return try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val];
2780 +
unsafe fn allocVals 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, len: u32) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2781 +
    return try! alloc::allocRawSlice(self.arena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val];
2685 2782
}
2686 2783
2687 2784
/// Allocate a single-value slice in the lowering arena.
2688 -
unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) {
2785 +
unsafe fn allocVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2689 2786
    let args = try allocVals(self, 1);
2690 2787
    set args[0] = val;
2691 2788
    return args;
2692 2789
}
2693 2790
2779 2876
// a block parameter, but defer filling in the terminator arguments. When the
2780 2877
// block is later sealed via [`sealBlock`], all incomplete block params are resolved.
2781 2878
2782 2879
/// Declare a new source-level variable and define its initial value.
2783 2880
/// If called before any block exists (e.g., for parameters), the definition is skipped.
2784 -
unsafe fn newVar(
2785 -
    self: &mut FnLowerer,
2881 +
unsafe fn newVar 'arena 'phase 'function (
2882 +
    self: &mut FnLowerer 'arena 'phase 'function,
2786 2883
    name: ?*[u8],
2787 2884
    type: il::Type,
2788 2885
    mutable: bool,
2789 2886
    val: il::Val
2790 -
) -> Var {
2887 +
) -> Var where 'arena: 'phase, 'phase: 'function {
2791 2888
    let id = self.vars.len;
2792 -
    self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator);
2889 +
    self.vars.append(
2890 +
        VarData { name, type, mutable, addressTaken: false },
2891 +
        alloc::arenaAllocator(self.arena)
2892 +
    );
2793 2893
2794 2894
    let v = Var(id);
2795 2895
    if self.currentBlock <> nil {
2796 2896
        defVar(self, v, val);
2797 2897
    }
2800 2900
2801 2901
/// Define (write) a variable. Record the SSA value of a variable in the
2802 2902
/// current block. Called when a variable is assigned or initialized (`let`
2803 2903
/// bindings, assignments, loop updates). When [`useVar`] is later called,
2804 2904
/// it will retrieve this value.
2805 -
unsafe fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) {
2905 +
unsafe fn defVar 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, val: il::Val) where 'arena: 'phase, 'phase: 'function {
2806 2906
    assert *v < self.vars.len;
2807 2907
    set getBlockMut(self, currentBlock(self)).vars[*v] = val;
2808 2908
}
2809 2909
2810 2910
/// Use (read) the current value of a variable in the current block.
2811 2911
/// May insert block parameters if the value must come from predecessors.
2812 -
unsafe fn useVar(self: &mut FnLowerer, v: Var) -> il::Val throws (LowerError) {
2912 +
unsafe fn useVar 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2813 2913
    return try useVarInBlock(self, currentBlock(self), v);
2814 2914
}
2815 2915
2816 2916
/// Resolve which SSA definition of a variable reaches a use point in a given block.
2817 2917
///
2818 2918
/// Given a variable and a block where it's used, this function finds the
2819 2919
/// correct [`il::Val`] that holds the variable's value at that program point.
2820 2920
/// When control flow merges from multiple predecessors with different
2821 2921
/// definitions, it creates a block parameter to unify them.
2822 -
unsafe fn useVarInBlock(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
2922 +
unsafe fn useVarInBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2823 2923
    assert *v < self.vars.len;
2824 2924
2825 2925
    let blk = getBlockMut(self, block);
2826 2926
    if let val = blk.vars[*v] {
2827 2927
        return val;
2852 2952
    return try createBlockParam(self, block, v);
2853 2953
}
2854 2954
2855 2955
/// Look up a variable by name in the current scope.
2856 2956
/// Searches from most recently declared to first, enabling shadowing.
2857 -
unsafe fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var {
2957 +
unsafe fn lookupVarByName 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, name: *[u8]) -> ?Var where 'arena: 'phase, 'phase: 'function {
2858 2958
    let mut id = self.vars.len;
2859 2959
    while id > 0 {
2860 2960
        set id -= 1;
2861 2961
        if let varName = self.vars[id].name {
2862 2962
            // Names are interned strings, so pointer comparison suffices.
2867 2967
    }
2868 2968
    return nil;
2869 2969
}
2870 2970
2871 2971
/// Look up a local variable bound to an identifier node.
2872 -
unsafe fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var {
2972 +
unsafe fn lookupLocalVar 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, node: *ast::Node) -> ?Var where 'arena: 'phase, 'phase: 'function {
2873 2973
    let case ast::NodeValue::Ident(name) = node.value else {
2874 2974
        return nil;
2875 2975
    };
2876 2976
    return lookupVarByName(self, name);
2877 2977
}
2878 2978
2879 2979
/// Save current lexical variable scope depth.
2880 -
unsafe fn enterVarScope(self: &FnLowerer) -> u32 {
2980 +
unsafe fn enterVarScope 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> u32 where 'arena: 'phase, 'phase: 'function {
2881 2981
    return self.vars.len;
2882 2982
}
2883 2983
2884 2984
/// Restore lexical variable scope depth.
2885 -
unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) {
2985 +
unsafe fn exitVarScope 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, savedVarsLen: u32) where 'arena: 'phase, 'phase: 'function {
2886 2986
    set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap);
2887 2987
}
2888 2988
2889 2989
/// Get the metadata for a variable.
2890 -
unsafe fn getVar(self: &FnLowerer, v: Var) -> *unsafe VarData {
2990 +
unsafe fn getVar 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, v: Var) -> *unsafe VarData where 'arena: 'phase, 'phase: 'function {
2891 2991
    assert *v < self.vars.len;
2892 2992
    return &self.vars[*v];
2893 2993
}
2894 2994
2895 2995
/// Create a block parameter to merge a variable's value from multiple
2906 3006
///     @end(w32 %1)              // x = %1, merged from predecessors
2907 3007
///       ret %1;
2908 3008
///
2909 3009
/// Create a register `%1` as a block parameter. In a sealed block, patch each
2910 3010
/// predecessor's jump to pass its value of `x`. Otherwise, defer until sealing.
2911 -
unsafe fn createBlockParam(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
3011 +
unsafe fn createBlockParam 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2912 3012
    // Entry block must not have block parameters.
2913 3013
    assert block <> self.entryBlock, "createBlockParam: entry block must not have block parameters";
2914 3014
    // Allocate a register to hold the merged value.
2915 3015
    let reg = nextReg(self);
2916 3016
    let type = getVar(self, v).type;
2917 3017
2918 3018
    // Create block parameter and add it to the block.
2919 3019
    let param = il::Param { value: reg, type };
2920 3020
    let blk = getBlockMut(self, block);
2921 3021
    let paramIdx = blk.paramVars.len;
2922 -
    blk.params.append(param, self.allocator);
2923 -
    blk.paramVars.append(*v, self.allocator); // Associate variable with parameter.
3022 +
    blk.params.append(param, alloc::arenaAllocator(self.arena));
3023 +
    blk.paramVars.append(*v, alloc::arenaAllocator(self.arena)); // Associate variable with parameter.
2924 3024
2925 3025
    // Record that this variable's value in this block is now the parameter register.
2926 3026
    // This must happen before the predecessor loop to handle self-referential loops.
2927 3027
    set blk.vars[*v] = il::Val::Reg(reg);
2928 3028
2952 3052
///     x3 = phi(x1, x2)
2953 3053
///
2954 3054
/// This representation avoids the need for phi nodes to reference their
2955 3055
/// predecessor blocks explicitly, since the control flow edges already encode
2956 3056
/// that information.
2957 -
unsafe fn resolveBlockArgs(self: &mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) {
3057 +
unsafe fn resolveBlockArgs 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2958 3058
    let blk = getBlock(self, block);
2959 3059
2960 3060
    // For each predecessor, recursively look up the variable's reaching definition
2961 3061
    // in that block, then patch the predecessor's terminator to pass that value
2962 3062
    // as an argument to this block's parameter.
2970 3070
    }
2971 3071
}
2972 3072
2973 3073
/// Check if a block parameter is trivial, i.e. all predecessors provide
2974 3074
/// the same value. Returns the trivial value if so.
2975 -
unsafe fn getTrivialPhiVal(self: &mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) {
3075 +
unsafe fn getTrivialPhiVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> ?il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2976 3076
    let blk = getBlock(self, block);
2977 3077
    // Get the block parameter register.
2978 3078
    let paramReg = blk.vars[*v];
2979 3079
    // Check if all predecessors provide the same value.
2980 3080
    let mut sameVal: ?il::Val = nil;
3004 3104
    return sameVal;
3005 3105
}
3006 3106
3007 3107
/// Patch a single terminator argument for a specific edge. This is used during
3008 3108
/// SSA construction to pass variable values along control flow edges.
3009 -
unsafe fn patchTerminatorArg(
3010 -
    self: &mut FnLowerer,
3109 +
unsafe fn patchTerminatorArg 'arena 'phase 'function (
3110 +
    self: &mut FnLowerer 'arena 'phase 'function,
3011 3111
    from: BlockId,         // The predecessor block containing the terminator to patch.
3012 3112
    target: u32,           // The index of the target block we're passing the value to.
3013 3113
    paramIdx: u32,         // The index of the block parameter to set.
3014 3114
    val: il::Val           // The value to pass as the argument.
3015 -
) {
3115 +
) where 'arena: 'phase, 'phase: 'function {
3016 3116
    let data = getBlockMut(self, from);
3017 3117
    let ix = data.instrs.len - 1; // The terminator is always the last instruction.
3018 3118
3019 3119
    // TODO: We shouldn't need to use a mutable subscript here, given that the
3020 3120
    // fields are already mutable.
3052 3152
        }
3053 3153
    }
3054 3154
}
3055 3155
3056 3156
/// Grow an args array to hold at least the given capacity.
3057 -
unsafe fn growArgs(self: &mut FnLowerer, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] {
3157 +
unsafe fn growArgs 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] where 'arena: 'phase, 'phase: 'function {
3058 3158
    if args.len >= capacity {
3059 3159
        return args;
3060 3160
    }
3061 3161
    let newArgs = try! alloc::allocRawSlice(
3062 -
        self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3162 +
        self.arena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3063 3163
    ) as *unsafe mut [il::Val];
3064 3164
3065 3165
    for arg, i in args {
3066 3166
        set newArgs[i] = arg;
3067 3167
    }
3082 3182
    return name;
3083 3183
}
3084 3184
3085 3185
/// Lower function parameters. Declares variables for each parameter.
3086 3186
/// When a receiver name is passed, we're handling a trait method.
3087 -
unsafe fn lowerParams(
3088 -
    self: &mut FnLowerer,
3187 +
unsafe fn lowerParams 'arena 'phase 'function (
3188 +
    self: &mut FnLowerer 'arena 'phase 'function,
3089 3189
    fnType: resolver::FnType,
3090 3190
    astParams: *[*ast::Node],
3091 3191
    receiverName: ?*ast::Node
3092 -
) -> *unsafe [il::Param] throws (LowerError) {
3192 +
) -> *unsafe [il::Param] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3093 3193
    let offset: u32 = 1 if self.returnReg <> nil else 0;
3094 3194
    let totalLen = fnType.paramTypes.len as u32 + offset;
3095 3195
    if totalLen == 0 {
3096 3196
        return &[];
3097 3197
    }
3098 3198
    assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS;
3099 3199
3100 3200
    let params = try! alloc::allocRawSlice(
3101 -
        self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3201 +
        self.arena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3102 3202
    ) as *unsafe mut [il::Param];
3103 3203
3104 3204
    if let reg = self.returnReg {
3105 3205
        set params[0] = il::Param { value: reg, type: il::Type::W64 };
3106 3206
    }
3126 3226
        } else {
3127 3227
            set name = try paramName(&astParams[i].value);
3128 3228
        }
3129 3229
        let v = newVar(self, name, type, false, il::Val::Undef);
3130 3230
3131 -
        self.params.append(FnParamBinding { var: v, reg }, self.allocator);
3231 +
        set self.params[i] = FnParamBinding { var: v, reg };
3232 +
        set self.paramsLen += 1;
3132 3233
    }
3133 3234
    return params;
3134 3235
}
3135 3236
3136 3237
/// Resolve match subject.
3137 -
unsafe fn lowerMatchSubject(self: &mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) {
3238 +
unsafe fn lowerMatchSubject 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: *ast::Node) -> MatchSubject throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3138 3239
    let mut val = try lowerExpr(self, subject);
3139 3240
    let subjectType = try typeOf(self, subject);
3140 3241
    let unwrapped = resolver::unwrapMatchSubject(subjectType);
3141 3242
3142 3243
    // When matching an aggregate by value, copy it to a fresh stack slot so
3172 3273
        else => return false,
3173 3274
    }
3174 3275
}
3175 3276
3176 3277
/// Load the tag byte from a tagged value aggregate (optionals and unions).
3177 -
unsafe fn tvalTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg {
3278 +
unsafe fn tvalTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function {
3178 3279
    let tagReg = nextReg(self);
3179 3280
    emitLoadW8At(self, tagReg, base, TVAL_TAG_OFFSET);
3180 3281
    return tagReg;
3181 3282
}
3182 3283
3183 3284
/// Load the tag word from a result aggregate.
3184 -
unsafe fn resultTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg {
3285 +
unsafe fn resultTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function {
3185 3286
    let tagReg = nextReg(self);
3186 3287
    emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET);
3187 3288
    return tagReg;
3188 3289
}
3189 3290
3190 3291
/// Get the register to compare against `0` for optional `nil` checking.
3191 3292
/// For null-ptr-optimized types, loads the data pointer, or returns it
3192 3293
/// directly for scalar pointers. For aggregates, returns the tag register.
3193 -
unsafe fn optionalNilReg(self: &mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) {
3294 +
unsafe fn optionalNilReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3194 3295
    let reg = emitValToReg(self, val);
3195 3296
3196 3297
    match typ {
3197 3298
        case resolver::Type::Optional(resolver::Type::Slice { .. }) => {
3198 3299
            let ptrReg = nextReg(self);
3204 3305
        else => return reg,
3205 3306
    }
3206 3307
}
3207 3308
3208 3309
/// Lower an optional nil check (`opt == nil` or `opt <> nil`).
3209 -
unsafe fn lowerNilCheck(self: &mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) {
3310 +
unsafe fn lowerNilCheck 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3210 3311
    let optTy = try typeOf(self, opt);
3211 3312
    // Handle `nil == nil` or `nil <> nil`.
3212 3313
    if optTy == resolver::Type::Nil {
3213 3314
        return il::Val::Imm(1) if isEq else il::Val::Imm(0);
3214 3315
    }
3222 3323
    let op = il::BinOp::Eq if isEq else il::BinOp::Ne;
3223 3324
    return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0));
3224 3325
}
3225 3326
3226 3327
/// Load the payload value from a tagged value aggregate at the given offset.
3227 -
unsafe fn tvalPayloadVal(self: &mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val {
3328 +
unsafe fn tvalPayloadVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val where 'arena: 'phase, 'phase: 'function {
3228 3329
    if payload == resolver::Type::Void {
3229 3330
        return il::Val::Undef;
3230 3331
    }
3231 3332
    return emitRead(self, base, valOffset, payload);
3232 3333
}
3233 3334
3234 3335
/// Compute the address of the payload in a tagged value aggregate.
3235 -
unsafe fn tvalPayloadAddr(self: &mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val {
3336 +
unsafe fn tvalPayloadAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, valOffset: i32) -> il::Val where 'arena: 'phase, 'phase: 'function {
3236 3337
    return il::Val::Reg(emitPtrOffset(self, base, valOffset));
3237 3338
}
3238 3339
3239 3340
/// Bind a variable to a tagged value's payload.
3240 -
unsafe fn bindPayloadVariable(
3241 -
    self: &mut FnLowerer,
3341 +
unsafe fn bindPayloadVariable 'arena 'phase 'function (
3342 +
    self: &mut FnLowerer 'arena 'phase 'function,
3242 3343
    name: *[u8],
3243 3344
    subjectVal: il::Val,
3244 3345
    bindType: resolver::Type,
3245 3346
    matchBy: resolver::MatchBy,
3246 3347
    valOffset: i32,
3247 3348
    mutable: bool
3248 -
) -> Var throws (LowerError) {
3349 +
) -> Var throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3249 3350
    let base = emitValToReg(self, subjectVal);
3250 3351
    let mut payload: il::Val = undefined;
3251 3352
3252 3353
    match matchBy {
3253 3354
        case resolver::MatchBy::Value =>
3257 3358
    };
3258 3359
    return newVar(self, name, ilType(self.low, bindType), mutable, payload);
3259 3360
}
3260 3361
3261 3362
/// Bind an identifier from a matched subject.
3262 -
unsafe fn bindMatchVariable(
3263 -
    self: &mut FnLowerer,
3363 +
unsafe fn bindMatchVariable 'arena 'phase 'function (
3364 +
    self: &mut FnLowerer 'arena 'phase 'function,
3264 3365
    subject: &MatchSubject,
3265 3366
    binding: *ast::Node,
3266 3367
    mutable: bool
3267 -
) -> ?Var throws (LowerError) {
3368 +
) -> ?Var throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3268 3369
    // Only bind if the pattern is an identifier.
3269 3370
    let case ast::NodeValue::Ident(name) = binding.value else {
3270 3371
        return nil;
3271 3372
    };
3272 3373
    // For optional aggregates, extract the payload from the tagged value.
3280 3381
}
3281 3382
3282 3383
/// Bind variables from inside case patterns (union variants, records, slices).
3283 3384
/// `failBlock` is passed when nested patterns may require additional tests
3284 3385
/// that branch on mismatch (e.g. nested union variant tests).
3285 -
unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) {
3386 +
unsafe fn bindPatternVariables 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3286 3387
    for pattern in patterns {
3287 3388
3288 3389
        // Handle simple variant patterns like `Variant(x)`.
3289 3390
        if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) {
3290 3391
            let case MatchSubjectKind::Union(unionInfo) = subject.kind
3319 3420
                    try bindFieldVariable(self, arg, payloadBase, fieldInfo, subject.by, failBlock);
3320 3421
                }
3321 3422
            }
3322 3423
        }
3323 3424
        match pattern.value {
3425 +
            case ast::NodeValue::Call(call) => {
3426 +
                if let body = resolver::getRecord(subject.type) {
3427 +
                    let base = emitValToReg(self, subject.val);
3428 +
                    for binding, i in call.args {
3429 +
                        try bindFieldVariable(self, binding, base, body.fields[i], subject.by, failBlock);
3430 +
                    }
3431 +
                }
3432 +
            }
3324 3433
            // Compound variant patterns like `Variant { a, b }`.
3325 3434
            case ast::NodeValue::RecordLit(lit) =>
3326 3435
                try bindRecordPatternFields(self, subject, pattern, lit, failBlock),
3327 3436
            // Array patterns like `[a, b, c]`.
3328 3437
            case ast::NodeValue::ArrayLit(items) =>
3334 3443
}
3335 3444
3336 3445
/// Bind variables from an array literal pattern (e.g., `[a, 1, c]`).
3337 3446
/// Each element is either bound as a variable, skipped (placeholder), or
3338 3447
/// tested against the subject element, branching to `failBlock` on mismatch.
3339 -
unsafe fn bindArrayPatternElements(
3340 -
    self: &mut FnLowerer,
3448 +
unsafe fn bindArrayPatternElements 'arena 'phase 'function (
3449 +
    self: &mut FnLowerer 'arena 'phase 'function,
3341 3450
    subject: &MatchSubject,
3342 3451
    items: *[*ast::Node],
3343 3452
    failBlock: BlockId
3344 -
) throws (LowerError) {
3453 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3345 3454
    let case resolver::Type::Array(arrInfo) = subject.type
3346 3455
        else throw LowerError::ExpectedSliceOrArray;
3347 3456
3348 3457
    let elemTy = *arrInfo.item;
3349 3458
    let elemLayout = resolver::getTypeLayout(elemTy);
3359 3468
        try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock);
3360 3469
    }
3361 3470
}
3362 3471
3363 3472
/// Bind fields from a record pattern.
3364 -
unsafe fn bindRecordPatternFields(self: &mut FnLowerer, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3473 +
unsafe fn bindRecordPatternFields 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3365 3474
    // No fields to bind (e.g., `{ .. }`).
3366 3475
    if lit.fields.len == 0 {
3367 3476
        return;
3368 3477
    }
3369 3478
    // Optional value patterns were already compared structurally by
3399 3508
    try bindNestedRecordFields(self, payloadBase, lit, recInfo, subject.by, failBlock);
3400 3509
}
3401 3510
3402 3511
/// Bind a single record field to a pattern variable, with support for nested
3403 3512
/// pattern tests that branch to `failBlock` on mismatch.
3404 -
unsafe fn bindFieldVariable(
3405 -
    self: &mut FnLowerer,
3513 +
unsafe fn bindFieldVariable 'arena 'phase 'function (
3514 +
    self: &mut FnLowerer 'arena 'phase 'function,
3406 3515
    binding: *ast::Node,
3407 3516
    base: il::Reg,
3408 3517
    fieldInfo: resolver::RecordField,
3409 3518
    matchBy: resolver::MatchBy,
3410 3519
    failBlock: BlockId
3411 -
) throws (LowerError) {
3520 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3412 3521
    match binding.value {
3413 3522
        case ast::NodeValue::Ident(name) => {
3414 3523
            let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType)
3415 3524
                if matchBy == resolver::MatchBy::Value
3416 3525
                else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset));
3448 3557
}
3449 3558
3450 3559
/// Emit a nested pattern test for a record field value, branching to
3451 3560
/// `failBlock` if the pattern does not match. On success, continues in
3452 3561
/// a fresh block and binds any nested variables.
3453 -
unsafe fn emitNestedFieldTest(
3454 -
    self: &mut FnLowerer,
3562 +
unsafe fn emitNestedFieldTest 'arena 'phase 'function (
3563 +
    self: &mut FnLowerer 'arena 'phase 'function,
3455 3564
    pattern: *ast::Node,
3456 3565
    base: il::Reg,
3457 3566
    fieldInfo: resolver::RecordField,
3458 3567
    matchBy: resolver::MatchBy,
3459 3568
    failBlock: BlockId
3460 -
) throws (LowerError) {
3569 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3461 3570
    let mut fieldType = fieldInfo.fieldType;
3462 3571
    let fieldPtr = emitPtrOffset(self, base, fieldInfo.offset);
3463 3572
3464 3573
    // Auto-deref: when the field is a pointer and the pattern destructures
3465 3574
    // the pointed-to value, load the pointer and use the target type.
3507 3616
    let patterns = [pattern];
3508 3617
    try bindPatternVariables(self, &nestedSubject, &patterns[..], failBlock);
3509 3618
}
3510 3619
3511 3620
/// Bind variables from a nested record literal pattern.
3512 -
unsafe fn bindNestedRecordFields(
3513 -
    self: &mut FnLowerer,
3621 +
unsafe fn bindNestedRecordFields 'arena 'phase 'function (
3622 +
    self: &mut FnLowerer 'arena 'phase 'function,
3514 3623
    base: il::Reg,
3515 3624
    lit: ast::RecordLit,
3516 3625
    recInfo: resolver::RecordType,
3517 3626
    matchBy: resolver::MatchBy,
3518 3627
    failBlock: BlockId
3519 -
) throws (LowerError) {
3628 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3520 3629
    for fieldNode in lit.fields {
3521 3630
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
3522 3631
            throw LowerError::UnexpectedNodeValue(fieldNode);
3523 3632
        };
3524 3633
        let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode)
3531 3640
        try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock);
3532 3641
    }
3533 3642
}
3534 3643
3535 3644
/// Lower function body to a list of basic blocks.
3536 -
unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) {
3645 +
unsafe fn lowerFnBody 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3537 3646
    // Create and switch to entry block.
3538 3647
    let entry = try createBlock(self, "entry");
3539 3648
    set self.entryBlock = entry;
3540 3649
    switchToBlock(self, entry);
3541 3650
3542 3651
    /// Bind parameter registers to variables in the entry block.
3543 -
    for def in self.params {
3652 +
    for i in 0..self.paramsLen {
3653 +
        let def = self.params[i];
3544 3654
        defVar(self, def.var, il::Val::Reg(def.reg));
3545 3655
    }
3546 3656
    /// Lower function body.
3547 3657
    try lowerBlock(self, body);
3548 3658
3566 3676
    }
3567 3677
    return try finalizeBlocks(self);
3568 3678
}
3569 3679
3570 3680
/// Lower a scalar match as a switch instruction.
3571 -
unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) {
3681 +
unsafe fn lowerMatchSwitch 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3572 3682
    let mut blocks: [BlockId; 32] = undefined;
3573 3683
    let mut cases: *unsafe mut [il::SwitchCase] = &mut [];
3574 3684
    let mut defaultIdx: u32 = 0;
3575 3685
    let entry = currentBlock(self);
3576 3686
3591 3701
3592 3702
                    cases.append(il::SwitchCase {
3593 3703
                        value: constToScalar(cv),
3594 3704
                        target: *blocks[i],
3595 3705
                        args: &mut []
3596 -
                    }, self.allocator);
3706 +
                    }, alloc::arenaAllocator(self.arena));
3597 3707
                }
3598 3708
            }
3599 3709
        }
3600 3710
        addPredecessor(self, blocks[i], entry);
3601 3711
    }
3669 3779
///   arm#1:
3670 3780
///       jmp else#0;                     // guard failed, fallthrough to `else`
3671 3781
///   else#0:
3672 3782
///       ret 0;                          // `else` body
3673 3783
///
3674 -
unsafe fn lowerMatch(self: &mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) {
3784 +
unsafe fn lowerMatch 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, m: ast::Match) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3675 3785
    assert m.prongs.len > 0;
3676 3786
3677 3787
    let prongs = m.prongs;
3678 3788
    // Lower the subject expression once; reused across all arms.
3679 3789
    let subject = try lowerMatchSubject(self, m.subject);
3773 3883
        try switchToAndSeal(self, blk);
3774 3884
    }
3775 3885
}
3776 3886
3777 3887
/// Lower an `if let` statement.
3778 -
unsafe fn lowerIfLet(self: &mut FnLowerer, cond: ast::IfLet) throws (LowerError) {
3888 +
unsafe fn lowerIfLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: ast::IfLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3779 3889
    let savedVarsLen = enterVarScope(self);
3780 3890
    let subject = try lowerMatchSubject(self, cond.pattern.scrutinee);
3781 3891
    let mut thenBlock: BlockId = undefined;
3782 3892
    if cond.pattern.guard == nil {
3783 3893
        set thenBlock = try createBlock(self, "then");
3811 3921
/// Emit pattern match branch with optional guard, and bind variables.
3812 3922
/// Used by `if-let`, `let-else`, and `while-let` lowering.
3813 3923
///
3814 3924
/// When a guard is present, the guard block is created before `successBlock`
3815 3925
/// to ensure block indices are in RPO.
3816 -
unsafe fn lowerPatternMatch(
3817 -
    self: &mut FnLowerer,
3926 +
unsafe fn lowerPatternMatch 'arena 'phase 'function (
3927 +
    self: &mut FnLowerer 'arena 'phase 'function,
3818 3928
    subject: &MatchSubject,
3819 3929
    pat: &ast::PatternMatch,
3820 3930
    successBlock: &mut BlockId,
3821 3931
    successLabel: *[u8],
3822 3932
    failBlock: BlockId
3823 -
) throws (LowerError) {
3933 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3824 3934
    // If guard present, pattern match jumps to @guard, then guard evaluation
3825 3935
    // jumps to `successBlock` or `failBlock`. Otherwise, jump directly to
3826 3936
    // `successBlock`.
3827 3937
    let mut targetBlock: BlockId = undefined;
3828 3938
    if pat.guard <> nil {
3863 3973
        try switchToAndSeal(self, *successBlock);
3864 3974
    }
3865 3975
}
3866 3976
3867 3977
/// Lower a `let-else` statement.
3868 -
unsafe fn lowerLetElse(self: &mut FnLowerer, letElse: ast::LetElse) throws (LowerError) {
3978 +
unsafe fn lowerLetElse 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, letElse: ast::LetElse) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3869 3979
    let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee);
3870 3980
    let mut successBlock: BlockId = undefined;
3871 3981
    if letElse.pattern.guard == nil {
3872 3982
        set successBlock = try createBlock(self, "success");
3873 3983
    }
3904 4014
    // Continue at @merge after a successful match or value-producing fallback.
3905 4015
    try switchToAndSeal(self, mergeBlock);
3906 4016
}
3907 4017
3908 4018
/// Lower a `while let` loop as a match-driven loop.
3909 -
unsafe fn lowerWhileLet(self: &mut FnLowerer, w: ast::WhileLet) throws (LowerError) {
4019 +
unsafe fn lowerWhileLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, w: ast::WhileLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3910 4020
    let savedVarsLen = enterVarScope(self);
3911 4021
    // Create control flow blocks: loop header, body (created lazily when
3912 4022
    // there's a guard), and exit.
3913 4023
    let whileBlock = try createBlock(self, "while");
3914 4024
    let mut bodyBlock: BlockId = undefined;
3937 4047
///////////////////
3938 4048
// Node Lowering //
3939 4049
///////////////////
3940 4050
3941 4051
/// Lower an AST node.
3942 -
unsafe fn lowerNode(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
4052 +
unsafe fn lowerNode 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
3943 4053
    if self.low.options.debug {
3944 4054
        set self.srcLoc.offset = node.span.offset;
3945 4055
    }
3946 4056
    match node.value {
4057 +
        case ast::NodeValue::RegionBlock { bindings, body, .. } => {
4058 +
            let savedVarsLen = enterVarScope(self);
4059 +
            let values = try allocVals(self, bindings.len);
4060 +
            for bindingNode, i in bindings {
4061 +
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4062 +
                    else throw LowerError::ExpectedIdentifier;
4063 +
                set values[i] = try lowerExpr(self, binding.value);
4064 +
                if blockHasTerminator(self) {
4065 +
                    exitVarScope(self, savedVarsLen);
4066 +
                    return;
4067 +
                }
4068 +
            }
4069 +
            for bindingNode, i in bindings {
4070 +
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4071 +
                    else throw LowerError::ExpectedIdentifier;
4072 +
                try bindLetValue(self, bindingNode, ast::borrowBinding(binding), values[i]);
4073 +
            }
4074 +
            try lowerBlock(self, body);
4075 +
            exitVarScope(self, savedVarsLen);
4076 +
        }
3947 4077
        case ast::NodeValue::Block(_) => {
3948 4078
            try lowerBlock(self, node);
3949 4079
        }
3950 4080
        case ast::NodeValue::Return { value } => {
3951 4081
            try lowerReturnStmt(self, node, value);
4027 4157
        }
4028 4158
    }
4029 4159
}
4030 4160
4031 4161
/// Lower a code block.
4032 -
unsafe fn lowerBlock(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
4162 +
unsafe fn lowerBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4033 4163
    let case ast::NodeValue::Block(blk) = node.value else {
4034 4164
        throw LowerError::ExpectedBlock(node);
4035 4165
    };
4036 4166
    let savedVarsLen = enterVarScope(self);
4037 4167
    for stmt in blk.statements {
4068 4198
4069 4199
/// Return the effective type of a node after any coercion applied by
4070 4200
/// the resolver. `lowerExpr` already materializes the coercion in the
4071 4201
/// IL value, so the lowerer must use the post-coercion type when
4072 4202
/// choosing how to compare or store that value.
4073 -
unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
4203 +
unsafe fn effectiveType 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4074 4204
    let ty = try typeOf(self, node);
4075 4205
    if let coerce = resolver::coercionFor(self.low.resolver, node) {
4076 4206
        if let case resolver::Coercion::OptionalLift(optTy) = coerce {
4077 4207
            return optTy;
4078 4208
        }
4082 4212
4083 4213
/// Check if a resolver type lowers to an aggregate in memory.
4084 4214
unsafe fn isAggregateType(typ: resolver::Type) -> bool {
4085 4215
    match typ {
4086 4216
        case resolver::Type::Slice { .. },
4087 -
             resolver::Type::TraitObject { .. } => return true,
4217 +
             resolver::Type::TraitObject { .. },
4218 +
             resolver::Type::Session(_) => return true,
4088 4219
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4089 4220
            // Optional pointers are scalar due to NPO.
4090 4221
            return false;
4091 4222
        }
4092 4223
        case resolver::Type::Optional(_) => {
4130 4261
}
4131 4262
4132 4263
/// Check if a node is a void union variant literal (e.g. `Color::Red`).
4133 4264
/// If so, returns the variant's tag index. This enables optimized comparisons
4134 4265
/// that only check the tag instead of doing full aggregate comparison.
4135 -
unsafe fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 {
4266 +
unsafe fn voidVariantIndex 'arena (res: &resolver::Resolver 'arena, node: *ast::Node) -> ?i64 {
4136 4267
    let data = resolver::nodeData(res, node);
4137 4268
    // Optional equality checks both the presence tag and the union value.
4138 4269
    if let case resolver::Coercion::OptionalLift(_) = data.coercion {
4139 4270
        return nil;
4140 4271
    }
4150 4281
    }
4151 4282
    return index as i64;
4152 4283
}
4153 4284
4154 4285
/// Reserve stack storage for a value of the given type.
4155 -
unsafe fn emitReserve(self: &mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) {
4286 +
unsafe fn emitReserve 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4156 4287
    let layout = resolver::getTypeLayout(typ);
4157 4288
    return emitReserveLayout(self, layout);
4158 4289
}
4159 4290
4160 4291
/// Reserve stack storage with an explicit layout.
4161 -
unsafe fn emitReserveLayout(self: &mut FnLowerer, layout: resolver::Layout) -> il::Reg {
4292 +
unsafe fn emitReserveLayout 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, layout: resolver::Layout) -> il::Reg where 'arena: 'phase, 'phase: 'function {
4162 4293
    let dst = nextReg(self);
4163 4294
4164 4295
    emit(self, il::Instr::Reserve {
4165 4296
        dst,
4166 4297
        size: il::Val::Imm(layout.size as i64),
4168 4299
    });
4169 4300
    return dst;
4170 4301
}
4171 4302
4172 4303
/// Store a value into an address.
4173 -
unsafe fn emitStore(self: &mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) {
4304 +
unsafe fn emitStore 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4174 4305
    // `undefined` values need no store.
4175 4306
    if let case il::Val::Undef = src {
4176 4307
        return;
4177 4308
    }
4178 4309
    if isAggregateType(typ) {
4190 4321
        });
4191 4322
    }
4192 4323
}
4193 4324
4194 4325
/// Allocate stack space for a value and store it. Returns a pointer to the value.
4195 -
unsafe fn emitStackVal(self: &mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) {
4326 +
unsafe fn emitStackVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4196 4327
    let ptr = try emitReserve(self, typ);
4197 4328
    try emitStore(self, ptr, 0, typ, val);
4198 4329
    return il::Val::Reg(ptr);
4199 4330
}
4200 4331
4201 4332
/// Generic helper to build any tagged aggregate.
4202 4333
/// Reserves space based on the provided layout, stores the tag, and optionally
4203 4334
/// stores the payload value at `valOffset`.
4204 -
unsafe fn buildTagged(
4205 -
    self: &mut FnLowerer,
4335 +
unsafe fn buildTagged 'arena 'phase 'function (
4336 +
    self: &mut FnLowerer 'arena 'phase 'function,
4206 4337
    layout: resolver::Layout,
4207 4338
    tag: i64,
4208 4339
    payload: ?il::Val,
4209 4340
    payloadType: resolver::Type,
4210 4341
    tagSize: u32,
4211 4342
    valOffset: i32
4212 -
) -> il::Val throws (LowerError) {
4343 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4213 4344
    let dst = nextReg(self);
4214 4345
    emit(self, il::Instr::Reserve {
4215 4346
        dst,
4216 4347
        size: il::Val::Imm(layout.size as i64),
4217 4348
        alignment: layout.alignment,
4233 4364
/// Wrap a value in an optional type.
4234 4365
///
4235 4366
/// For optional pointers (`?*T`), the value is returned as-is since pointers
4236 4367
/// use zero to represent `nil`. For other optionals, builds a tagged aggregate.
4237 4368
/// with the tag set to `1`, and the value as payload.
4238 -
unsafe fn wrapInOptional(self: &mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) {
4369 +
unsafe fn wrapInOptional 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4239 4370
    let case resolver::Type::Optional(inner) = optType else {
4240 4371
        throw LowerError::ExpectedOptional;
4241 4372
    };
4242 4373
    // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null.
4243 4374
    if resolver::isNullableType(*inner) {
4251 4382
4252 4383
/// Build a `nil` value for an optional type.
4253 4384
///
4254 4385
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4255 4386
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4256 -
unsafe fn buildNilOptional(self: &mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4387 +
unsafe fn buildNilOptional 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, optType: resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4257 4388
    let case resolver::Type::Optional(inner) = optType
4258 4389
        else throw LowerError::ExpectedOptional;
4259 4390
    if let case resolver::Type::Pointer { .. } = *inner {
4260 4391
        return il::Val::Imm(0);
4261 4392
    }
4267 4398
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4268 4399
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4269 4400
}
4270 4401
4271 4402
/// Build a result value for throwing functions.
4272 -
unsafe fn buildResult(
4273 -
    self: &mut FnLowerer,
4403 +
unsafe fn buildResult 'arena 'phase 'function (
4404 +
    self: &mut FnLowerer 'arena 'phase 'function,
4274 4405
    tag: i64,
4275 4406
    payload: ?il::Val,
4276 4407
    payloadType: resolver::Type
4277 -
) -> il::Val throws (LowerError) {
4408 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4278 4409
    let successType = *self.fnType.returnType;
4279 4410
    let layout = resolver::getResultLayout(
4280 4411
        successType, self.fnType.throwList
4281 4412
    );
4282 4413
    return try buildTagged(self, layout, tag, payload, payloadType, resolver::PTR_SIZE as i32, RESULT_VAL_OFFSET);
4283 4414
}
4284 4415
4285 4416
/// Build a slice aggregate from a data pointer, length and capacity.
4286 -
unsafe fn buildSliceValue(
4287 -
    self: &mut FnLowerer,
4417 +
unsafe fn buildSliceValue 'arena 'phase 'function (
4418 +
    self: &mut FnLowerer 'arena 'phase 'function,
4288 4419
    elemTy: *resolver::Type,
4289 4420
    mutable: bool,
4290 4421
    ptrVal: il::Val,
4291 4422
    lenVal: il::Val,
4292 4423
    capVal: il::Val
4293 -
) -> il::Val throws (LowerError) {
4424 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4294 4425
    let sliceType = resolver::Type::Slice {
4295 4426
        class: types::PointerClass::Unsafe,
4296 4427
        item: elemTy,
4297 4428
        mutable,
4298 4429
    };
4309 4440
4310 4441
    return il::Val::Reg(dst);
4311 4442
}
4312 4443
4313 4444
/// Build a trait object fat pointer from a data pointer and a v-table.
4314 -
unsafe fn buildTraitObject(
4315 -
    self: &mut FnLowerer,
4445 +
unsafe fn buildTraitObject 'arena 'phase 'function (
4446 +
    self: &mut FnLowerer 'arena 'phase 'function,
4316 4447
    dataVal: il::Val,
4317 4448
    traitInfo: *unsafe resolver::TraitType,
4318 4449
    inst: &resolver::InstanceEntry
4319 -
) -> il::Val throws (LowerError) {
4450 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4320 4451
    let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name);
4321 4452
4322 4453
    // Reserve space for the trait object on the stack.
4323 4454
    let slot = emitReserveLayout(self, resolver::Layout {
4324 4455
        size: resolver::PTR_SIZE * 2,
4342 4473
    });
4343 4474
    return il::Val::Reg(slot);
4344 4475
}
4345 4476
4346 4477
/// Compute a field pointer by adding a byte offset to a base address.
4347 -
unsafe fn emitPtrOffset(self: &mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg {
4478 +
unsafe fn emitPtrOffset 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32) -> il::Reg where 'arena: 'phase, 'phase: 'function {
4348 4479
    if offset == 0 {
4349 4480
        return base;
4350 4481
    }
4351 4482
    let dst = nextReg(self);
4352 4483
4360 4491
    return dst;
4361 4492
}
4362 4493
4363 4494
/// Emit an element address computation for array/slice indexing.
4364 4495
/// Computes: `base + idx * stride`.
4365 -
unsafe fn emitElem(self: &mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg {
4496 +
unsafe fn emitElem 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function {
4366 4497
    // If index is zero, return base directly.
4367 4498
    if idx == il::Val::Imm(0) {
4368 4499
        return base;
4369 4500
    }
4370 4501
    // If stride is `1`, skip the multiply.
4401 4532
    });
4402 4533
    return dst;
4403 4534
}
4404 4535
4405 4536
/// Emit a typed binary operation, returning the result as a value.
4406 -
unsafe fn emitTypedBinOp(self: &mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val {
4537 +
unsafe fn emitTypedBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function {
4407 4538
    let dst = nextReg(self);
4408 4539
    emit(self, il::Instr::BinOp { op, typ, dst, a, b });
4409 4540
    return il::Val::Reg(dst);
4410 4541
}
4411 4542
4412 4543
/// Emit a tag comparison for void variant equality/inequality.
4413 -
unsafe fn emitTagCmp(self: &mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val
4414 -
    throws (LowerError)
4544 +
unsafe fn emitTagCmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val
4545 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
4415 4546
{
4416 4547
    let reg = emitValToReg(self, val);
4417 4548
4418 4549
    // For all-void unions, the value *is* the tag, not a pointer.
4419 4550
    let mut tag: il::Val = undefined;
4425 4556
    let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne;
4426 4557
    return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx));
4427 4558
}
4428 4559
4429 4560
/// Logical "and" between two values. Returns the result in a register.
4430 -
unsafe fn emitLogicalAnd(self: &mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val {
4561 +
unsafe fn emitLogicalAnd 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, left: ?il::Val, right: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function {
4431 4562
    let prev = left else {
4432 4563
        return right;
4433 4564
    };
4434 4565
    return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, prev, right);
4435 4566
}
4437 4568
//////////////////////////
4438 4569
// Aggregate Comparison //
4439 4570
//////////////////////////
4440 4571
4441 4572
/// Emit an equality test for values at an offset of the given base registers.
4442 -
unsafe fn emitEqAtOffset(
4443 -
    self: &mut FnLowerer,
4573 +
unsafe fn emitEqAtOffset 'arena 'phase 'function (
4574 +
    self: &mut FnLowerer 'arena 'phase 'function,
4444 4575
    left: il::Reg,
4445 4576
    right: il::Reg,
4446 4577
    offset: i32,
4447 4578
    fieldType: resolver::Type
4448 -
) -> il::Val throws (LowerError) {
4579 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4449 4580
    // For aggregate types, pass offset through and compare recursively.
4450 4581
    if isAggregateType(fieldType) {
4451 4582
        return try lowerAggregateEq(self, fieldType, left, right, offset);
4452 4583
    }
4453 4584
    // For scalar types, load and compare directly.
4458 4589
4459 4590
    return il::Val::Reg(dst);
4460 4591
}
4461 4592
4462 4593
/// Compare two record values for equality.
4463 -
unsafe fn lowerRecordEq(
4464 -
    self: &mut FnLowerer,
4594 +
unsafe fn lowerRecordEq 'arena 'phase 'function (
4595 +
    self: &mut FnLowerer 'arena 'phase 'function,
4465 4596
    recInfo: resolver::RecordType,
4466 4597
    a: il::Reg,
4467 4598
    b: il::Reg,
4468 4599
    offset: i32
4469 -
) -> il::Val throws (LowerError) {
4600 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4470 4601
    let mut result: ?il::Val = nil;
4471 4602
4472 4603
    for field in recInfo.fields {
4473 4604
        let cmp = try emitEqAtOffset(self, a, b, offset + field.offset, field.fieldType);
4474 4605
4479 4610
    }
4480 4611
    return il::Val::Imm(1);
4481 4612
}
4482 4613
4483 4614
/// Compare two slice values for equality.
4484 -
unsafe fn lowerSliceEq(
4485 -
    self: &mut FnLowerer,
4615 +
unsafe fn lowerSliceEq 'arena 'phase 'function (
4616 +
    self: &mut FnLowerer 'arena 'phase 'function,
4486 4617
    elemTy: *resolver::Type,
4487 4618
    mutable: bool,
4488 4619
    a: il::Reg,
4489 4620
    b: il::Reg,
4490 4621
    offset: i32
4491 -
) -> il::Val throws (LowerError) {
4622 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4492 4623
    let ptrTy = resolver::Type::Pointer {
4493 4624
        class: types::PointerClass::Unsafe,
4494 4625
        target: elemTy,
4495 4626
        mutable,
4496 4627
    };
4509 4640
/// branchless formulation: `tagEq AND (tagNil OR payloadEq)`
4510 4641
///
4511 4642
/// For inner types that may contain uninitialized data when `nil` (unions,
4512 4643
/// nested optionals), the payload comparison is guarded behind a branch
4513 4644
/// so that `nil` payloads are never inspected.
4514 -
unsafe fn lowerOptionalEq(
4515 -
    self: &mut FnLowerer,
4645 +
unsafe fn lowerOptionalEq 'arena 'phase 'function (
4646 +
    self: &mut FnLowerer 'arena 'phase 'function,
4516 4647
    inner: resolver::Type,
4517 4648
    a: il::Reg,
4518 4649
    b: il::Reg,
4519 4650
    offset: i32
4520 -
) -> il::Val throws (LowerError) {
4651 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4521 4652
    let valOffset = resolver::getOptionalValOffset(inner) as i32;
4522 4653
4523 4654
    // Load tags.
4524 4655
    let tagA = loadTag(self, a, offset + TVAL_TAG_OFFSET, il::Type::W8);
4525 4656
    let tagB = loadTag(self, b, offset + TVAL_TAG_OFFSET, il::Type::W8);
4601 4732
/// For all-void unions, we skip the control flow entirely and just compare
4602 4733
/// the tags directly.
4603 4734
///
4604 4735
/// TODO: Could be optimized to branchless when all non-void variants share
4605 4736
/// the same payload type: `tagEq AND (isVoidVariant OR payloadEq)`.
4606 -
unsafe fn lowerUnionEq(
4607 -
    self: &mut FnLowerer,
4737 +
unsafe fn lowerUnionEq 'arena 'phase 'function (
4738 +
    self: &mut FnLowerer 'arena 'phase 'function,
4608 4739
    unionInfo: resolver::UnionType,
4609 4740
    a: il::Reg,
4610 4741
    b: il::Reg,
4611 4742
    offset: i32
4612 -
) -> il::Val throws (LowerError) {
4743 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4613 4744
    // Compare tags.
4614 4745
    let tagA = loadTag(self, a, offset + TVAL_TAG_OFFSET, il::Type::W8);
4615 4746
    let tagB = loadTag(self, b, offset + TVAL_TAG_OFFSET, il::Type::W8);
4616 4747
4617 4748
    // Fast path: all-void union just needs tag comparison.
4645 4776
4646 4777
    // Create comparison blocks for each non-void variant and build switch cases.
4647 4778
    // Void variants jump directly to merge with `true`.
4648 4779
    let trueArgs = try allocVal(self, il::Val::Imm(1));
4649 4780
    let cases = try! alloc::allocRawSlice(
4650 -
        self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4781 +
        self.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4651 4782
    ) as *unsafe mut [il::SwitchCase];
4652 4783
4653 4784
    let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined;
4654 4785
    for variant, i in unionInfo.variants {
4655 4786
        if variant.valueType == resolver::Type::Void {
4708 4839
    try switchToAndSeal(self, mergeBlock);
4709 4840
    return il::Val::Reg(resultReg);
4710 4841
}
4711 4842
4712 4843
/// Compare two array values for equality, element by element.
4713 -
unsafe fn lowerArrayEq(
4714 -
    self: &mut FnLowerer,
4844 +
unsafe fn lowerArrayEq 'arena 'phase 'function (
4845 +
    self: &mut FnLowerer 'arena 'phase 'function,
4715 4846
    arr: resolver::ArrayType,
4716 4847
    a: il::Reg,
4717 4848
    b: il::Reg,
4718 4849
    offset: i32
4719 -
) -> il::Val throws (LowerError) {
4850 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4720 4851
    let elemLayout = resolver::getTypeLayout(*arr.item);
4721 4852
    let stride = elemLayout.size as i32;
4722 4853
    let mut result: ?il::Val = nil;
4723 4854
4724 4855
    for i in 0..arr.length {
4732 4863
    // Empty arrays are always equal.
4733 4864
    return il::Val::Imm(1);
4734 4865
}
4735 4866
4736 4867
/// Compare two aggregate values for equality.
4737 -
unsafe fn lowerAggregateEq(
4738 -
    self: &mut FnLowerer,
4868 +
unsafe fn lowerAggregateEq 'arena 'phase 'function (
4869 +
    self: &mut FnLowerer 'arena 'phase 'function,
4739 4870
    typ: resolver::Type,
4740 4871
    a: il::Reg,
4741 4872
    b: il::Reg,
4742 4873
    offset: i32
4743 -
) -> il::Val throws (LowerError) {
4874 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4744 4875
    match typ {
4745 4876
        case resolver::Type::Slice { item, mutable, .. } =>
4746 4877
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4747 4878
        case resolver::Type::Optional(inner) => {
4748 4879
            if let case resolver::Type::Slice { item, mutable, .. } = *inner {
4766 4897
    }
4767 4898
}
4768 4899
4769 4900
/// Lower a record literal expression. Handles both plain records and union variant
4770 4901
/// record literals like `Union::Variant { field: value }`.
4771 -
unsafe fn lowerRecordLit(self: &mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) {
4902 +
unsafe fn lowerRecordLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4772 4903
    let typ = try typeOf(self, node);
4773 4904
    match typ {
4774 4905
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
4775 4906
            let dst = try emitReserve(self, typ);
4776 4907
            try lowerRecordFields(self, dst, &recInfo, lit.fields, 0);
4803 4934
    }
4804 4935
}
4805 4936
4806 4937
/// Lower fields of a record literal into a destination register.
4807 4938
/// The `offset` is added to each field's offset when storing.
4808 -
unsafe fn lowerRecordFields(
4809 -
    self: &mut FnLowerer,
4939 +
unsafe fn lowerRecordFields 'arena 'phase 'function (
4940 +
    self: &mut FnLowerer 'arena 'phase 'function,
4810 4941
    dst: il::Reg,
4811 4942
    recInfo: &resolver::RecordType,
4812 4943
    fields: *[*ast::Node],
4813 4944
    offset: i32
4814 -
) throws (LowerError) {
4945 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4815 4946
    for fieldNode, i in fields {
4816 4947
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
4817 4948
            throw LowerError::UnexpectedNodeValue(fieldNode);
4818 4949
        };
4819 4950
        let mut fieldIdx: u32 = i;
4833 4964
        }
4834 4965
    }
4835 4966
}
4836 4967
4837 4968
/// Lower an unlabeled record constructor call.
4838 -
unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) {
4969 +
unsafe fn lowerRecordCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4839 4970
    let case resolver::NominalType::Record(recInfo) = *nominal else {
4840 4971
        throw LowerError::ExpectedRecord;
4841 4972
    };
4842 4973
    let typ = resolver::Type::Nominal(nominal);
4843 4974
    let dst = try emitReserve(self, typ);
4852 4983
    }
4853 4984
    return il::Val::Reg(dst);
4854 4985
}
4855 4986
4856 4987
/// Lower an array literal expression like `[1, 2, 3]`.
4857 -
unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *[*ast::Node]) -> il::Val
4858 -
    throws (LowerError)
4988 +
unsafe fn lowerArrayLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, elements: *[*ast::Node]) -> il::Val
4989 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
4859 4990
{
4860 4991
    let typ = try typeOf(self, node);
4861 4992
    let case resolver::Type::Array(arrInfo) = typ else {
4862 4993
        throw LowerError::ExpectedArray;
4863 4994
    };
4875 5006
}
4876 5007
4877 5008
/// Lower an array repeat literal expression like `[42; 3]`.
4878 5009
/// Unrolls the initialization at compile time.
4879 5010
// TODO: Beyond a certain length, lower this to a loop.
4880 -
unsafe fn lowerArrayRepeatLit(self: &mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val
4881 -
    throws (LowerError)
5011 +
unsafe fn lowerArrayRepeatLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val
5012 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
4882 5013
{
4883 5014
    let typ = try typeOf(self, node);
4884 5015
    let case resolver::Type::Array(arrInfo) = typ else {
4885 5016
        throw LowerError::ExpectedArray;
4886 5017
    };
4899 5030
    }
4900 5031
    return il::Val::Reg(dst);
4901 5032
}
4902 5033
4903 5034
/// Lower a union constructor call like `Union::Variant(...)`.
4904 -
unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val
4905 -
    throws (LowerError)
5035 +
unsafe fn lowerUnionCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val
5036 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
4906 5037
{
4907 5038
    let unionTy = try typeOf(self, node);
4908 5039
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else {
4909 5040
        throw LowerError::ExpectedVariant;
4910 5041
    };
4921 5052
    }
4922 5053
    return try buildTagged(self, resolver::getTypeLayout(unionTy), index as i64, payloadVal, payloadType, 1, valOffset);
4923 5054
}
4924 5055
4925 5056
/// Lower a field access into a pointer to the field.
4926 -
unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) {
5057 +
unsafe fn lowerFieldRef 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, access: ast::Access) -> FieldRef throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4927 5058
    let parentTy = try typeOf(self, access.parent);
4928 5059
    let subjectTy = resolver::autoDeref(parentTy);
4929 5060
    let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else {
4930 5061
        throw LowerError::MissingMetadata;
4931 5062
    };
4941 5072
        fieldType: fieldInfo.fieldType,
4942 5073
    };
4943 5074
}
4944 5075
4945 5076
/// Lower a field access expression.
4946 -
unsafe fn lowerFieldAccess(self: &mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) {
5077 +
unsafe fn lowerFieldAccess 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, access: ast::Access) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4947 5078
    let fieldRef = try lowerFieldRef(self, access);
4948 5079
    return emitRead(self, fieldRef.base, fieldRef.offset, fieldRef.fieldType);
4949 5080
}
4950 5081
4951 5082
/// Compute data pointer and element count for a range into a container.
4952 5083
/// Used by both slice range expressions (`&a[start..end]`) and slice
4953 5084
/// assignments (`a[start..end] = value`).
4954 -
unsafe fn resolveSliceRangePtr(
4955 -
    self: &mut FnLowerer,
5085 +
unsafe fn resolveSliceRangePtr 'arena 'phase 'function (
5086 +
    self: &mut FnLowerer 'arena 'phase 'function,
4956 5087
    container: *ast::Node,
4957 5088
    range: ast::Range,
4958 5089
    info: resolver::SliceRangeInfo
4959 -
) -> SliceRangeResult throws (LowerError) {
5090 +
) -> SliceRangeResult throws (LowerError) where 'arena: 'phase, 'phase: 'function {
4960 5091
    let baseVal = try lowerExpr(self, container);
4961 5092
    let baseReg = emitValToReg(self, baseVal);
4962 5093
4963 5094
    // Extract data pointer and container length.
4964 5095
    let mut dataReg = baseReg;
5015 5146
    }
5016 5147
    return SliceRangeResult { dataReg, count };
5017 5148
}
5018 5149
5019 5150
/// Lower a slice range expression into a slice header value.
5020 -
unsafe fn lowerSliceRange(
5021 -
    self: &mut FnLowerer,
5151 +
unsafe fn lowerSliceRange 'arena 'phase 'function (
5152 +
    self: &mut FnLowerer 'arena 'phase 'function,
5022 5153
    container: *ast::Node,
5023 5154
    range: ast::Range,
5024 5155
    sliceNode: *ast::Node
5025 -
) -> il::Val throws (LowerError) {
5156 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5026 5157
    let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else {
5027 5158
        throw LowerError::MissingMetadata;
5028 5159
    };
5029 5160
    let r = try resolveSliceRangePtr(self, container, range, info);
5030 5161
    return try buildSliceValue(
5031 5162
        self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count
5032 5163
    );
5033 5164
}
5034 5165
5035 5166
/// Lower an address-of (`&x`) expression.
5036 -
unsafe fn lowerAddressOf(self: &mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) {
5167 +
unsafe fn lowerAddressOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5037 5168
    // Handle subscript: `&ary[i]` or `&ary[start..end]`.
5038 5169
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
5039 5170
        if let case ast::NodeValue::Range(range) = index.value {
5040 5171
            return try lowerSliceRange(self, container, range, node);
5041 5172
        }
5048 5179
        let fieldRef = try lowerFieldRef(self, access);
5049 5180
        let ptr = emitPtrOffset(self, fieldRef.base, fieldRef.offset);
5050 5181
5051 5182
        return il::Val::Reg(ptr);
5052 5183
    }
5184 +
    // A qualified constant or static uses its resolved owner's data symbol.
5185 +
    if let case ast::NodeValue::ScopeAccess(_) = addr.target.value {
5186 +
        let sym = resolver::nodeData(self.low.resolver, addr.target).sym else {
5187 +
            throw LowerError::MissingSymbol(addr.target);
5188 +
        };
5189 +
        return il::Val::Reg(emitDataAddr(self, sym));
5190 +
    }
5053 5191
    // Handle variable address: `&x`
5054 5192
    if let case ast::NodeValue::Ident(_) = addr.target.value {
5055 5193
        if let v = lookupLocalVar(self, addr.target) {
5056 5194
            let val = try useVar(self, v);
5057 5195
            let typ = try typeOf(self, addr.target);
5099 5237
    }
5100 5238
    throw LowerError::UnexpectedNodeValue(addr.target);
5101 5239
}
5102 5240
5103 5241
/// Lower an addressed array literal as a slice.
5104 -
unsafe fn lowerArrayLiteralSlice(
5105 -
    self: &mut FnLowerer,
5242 +
unsafe fn lowerArrayLiteralSlice 'arena 'phase 'function (
5243 +
    self: &mut FnLowerer 'arena 'phase 'function,
5106 5244
    sliceNode: *ast::Node,
5107 5245
    arrayNode: *ast::Node
5108 -
) -> il::Val throws (LowerError) {
5246 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5109 5247
    let sliceTy = try typeOf(self, sliceNode);
5110 5248
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
5111 5249
        throw LowerError::UnexpectedType(sliceTy);
5112 5250
    };
5113 5251
    let arrayTy = try typeOf(self, arrayNode);
5120 5258
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
5121 5259
        );
5122 5260
    }
5123 5261
    if resolver::isConstExpr(self.low.resolver, arrayNode) {
5124 5262
        let fnName = self.fnName;
5125 -
        let mut b = dataBuilder(self.low.allocator);
5263 +
        let mut b = dataBuilder(alloc::arenaAllocator(self.low.arena));
5126 5264
        match arrayNode.value {
5127 5265
            case ast::NodeValue::ArrayLit(elements) =>
5128 5266
                try lowerConstArrayLitInto(self.low, elements, arrayTy, fnName, &mut b),
5129 5267
            case ast::NodeValue::ArrayRepeatLit(repeat) =>
5130 5268
                try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, fnName, &mut b),
5144 5282
5145 5283
/// Lower the common element pointer computation for subscript operations.
5146 5284
/// Handles both arrays and slices by resolving the container type, extracting
5147 5285
/// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute
5148 5286
/// the element address.
5149 -
unsafe fn lowerElemPtr(
5150 -
    self: &mut FnLowerer, container: *ast::Node, index: *ast::Node
5151 -
) -> ElemPtrResult throws (LowerError) {
5287 +
unsafe fn lowerElemPtr 'arena 'phase 'function (
5288 +
    self: &mut FnLowerer 'arena 'phase 'function, container: *ast::Node, index: *ast::Node
5289 +
) -> ElemPtrResult throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5152 5290
    let containerTy = try typeOf(self, container);
5153 5291
    let subjectTy = resolver::autoDeref(containerTy);
5154 5292
    let baseVal = try lowerExpr(self, container);
5155 5293
    let indexVal = try lowerExpr(self, index);
5156 5294
    let baseReg = emitValToReg(self, baseVal);
5186 5324
}
5187 5325
5188 5326
/// Lower a dereference expression.
5189 5327
/// Handles both pointer deref (`*ptr`) and record deref (`*r` on single-field
5190 5328
/// unlabeled record). Both read at offset 0 using the resolver-assigned type.
5191 -
unsafe fn lowerDeref(self: &mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) {
5329 +
unsafe fn lowerDeref 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5192 5330
    let type = try typeOf(self, node);
5193 5331
    let ptrVal = try lowerExpr(self, target);
5194 5332
    let ptrReg = emitValToReg(self, ptrVal);
5195 5333
5196 -
    return emitRead(self, ptrReg, 0, type);
5334 +
    let value = emitRead(self, ptrReg, 0, type);
5335 +
    if let case resolver::Type::Cell { .. } = try typeOf(self, target); isAggregateType(type) {
5336 +
        return try emitStackVal(self, type, value);
5337 +
    }
5338 +
    return value;
5197 5339
}
5198 5340
5199 5341
/// Lower a subscript expression.
5200 -
unsafe fn lowerSubscript(self: &mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val
5201 -
    throws (LowerError)
5342 +
unsafe fn lowerSubscript 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val
5343 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
5202 5344
{
5203 5345
    if let case ast::NodeValue::Range(_) = index.value {
5204 5346
        panic "lowerSubscript: range subscript must use address-of (&)";
5205 5347
    }
5206 5348
    let result = try lowerElemPtr(self, container, index);
5207 5349
5208 5350
    return emitRead(self, result.elemReg, 0, result.elemType);
5209 5351
}
5210 5352
5211 5353
/// Lower a let binding.
5212 -
unsafe fn lowerLet(self: &mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) {
5354 +
unsafe fn lowerLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5213 5355
    // Evaluate value.
5214 5356
    let val = try lowerExpr(self, l.value);
5215 5357
    if blockHasTerminator(self) {
5216 5358
        return;
5217 5359
    }
5360 +
    try bindLetValue(self, node, l, val);
5361 +
}
5362 +
5363 +
/// Bind an evaluated initializer in the current variable scope.
5364 +
unsafe fn bindLetValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let, val: il::Val)
5365 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
5366 +
{
5218 5367
    // Handle placeholder pattern: `let _ = expr;`
5219 5368
    if let case ast::NodeValue::Placeholder = l.ident.value {
5220 5369
        return;
5221 5370
    }
5222 5371
    let case ast::NodeValue::Ident(name) = l.ident.value else {
5223 5372
        throw LowerError::ExpectedIdentifier;
5224 5373
    };
5225 -
    let typ = try typeOf(self, l.value);
5374 +
    let typ = try typeOf(self, l.ident);
5226 5375
    let ilType = ilType(self.low, typ);
5227 5376
    let mut varVal = val;
5228 5377
5229 5378
    // Aggregates with persistent storage need a local copy to avoid aliasing.
5230 5379
    // Temporaries such as literals or call results can be adopted directly.
5235 5384
    // Void variant literals (e.g. `Option::None`) use scope access syntax and
5236 5385
    // are flagged as place expressions, but they are freshly constructed
5237 5386
    // temporaries with no persistent storage.
5238 5387
    if isAggregateType(typ) and
5239 5388
        ast::isPlaceExpr(l.value) and
5389 +
        not resolver::isCellDeref(self.low.resolver, l.value) and
5240 5390
        voidVariantIndex(self.low.resolver, l.value) == nil {
5241 5391
        set varVal = try emitStackVal(self, typ, val);
5242 5392
    }
5243 5393
5244 5394
    // If the resolver determined that this variable's address is taken
5274 5424
///
5275 5425
///     @entry -> (true)  @then ---> @end <--.
5276 5426
///         |                                 )
5277 5427
///         `---- (false) -------------------'
5278 5428
///
5279 -
unsafe fn lowerIf(self: &mut FnLowerer, i: ast::If) throws (LowerError) {
5429 +
unsafe fn lowerIf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, i: ast::If) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5280 5430
    let thenBlock = try createBlock(self, "then");
5281 5431
5282 5432
    if let elseNode = i.elseBranch { // If-else case.
5283 5433
        let elseBlock = try createBlock(self, "else");
5284 5434
        try emitCondBranch(self, i.condition, thenBlock, elseBlock);
5338 5488
}
5339 5489
5340 5490
/// Lower an assignment target that designates a memory location, and return
5341 5491
/// the address to store through. Returns `nil` without emitting anything for
5342 5492
/// targets that aren't memory-backed, such as locals tracked in SSA.
5343 -
unsafe fn lowerPlace(self: &mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) {
5493 +
unsafe fn lowerPlace 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: *ast::Node) -> ?FieldRef throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5344 5494
    match target.value {
5345 5495
        case ast::NodeValue::ScopeAccess(_) => {
5346 5496
            let sym = try symOf(self, target);
5347 5497
            let case resolver::SymbolData::Value { type, .. } = sym.data else {
5348 5498
                throw LowerError::ImmutableAssignment;
5371 5521
/// whether it was handled. Nothing is emitted when it isn't.
5372 5522
///
5373 5523
/// Compound assignments share their target node with the left operand of the
5374 5524
/// desugared binary expression. Resolve that place once so side effects in a
5375 5525
/// dereference, field parent, or subscript index are not repeated for the store.
5376 -
unsafe fn lowerCompoundAssign(
5377 -
    self: &mut FnLowerer, expr: *ast::Node, binop: ast::BinOp
5378 -
) -> bool throws (LowerError) {
5526 +
unsafe fn lowerCompoundAssign 'arena 'phase 'function (
5527 +
    self: &mut FnLowerer 'arena 'phase 'function, expr: *ast::Node, binop: ast::BinOp
5528 +
) -> bool throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5379 5529
    let place = try lowerPlace(self, binop.left) else return false;
5380 5530
    let current = emitRead(self, place.base, place.offset, place.fieldType);
5381 5531
    let left = try applyCoercion(self, binop.left, current);
5382 5532
    let right = try lowerExpr(self, binop.right);
5383 5533
    let exprType = try typeOf(self, expr);
5389 5539
5390 5540
    return true;
5391 5541
}
5392 5542
5393 5543
/// Lower an assignment statement.
5394 -
unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) {
5544 +
unsafe fn lowerAssign 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, a: ast::Assign) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5395 5545
    // Slice assignment: `slice[range] = value`.
5396 5546
    if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) {
5397 5547
        let case ast::NodeValue::Subscript { container, index } = a.left.value
5398 5548
            else panic "lowerAssign: slice assign without subscript";
5399 5549
        let case ast::NodeValue::Range(range) = index.value
5446 5596
        }
5447 5597
    }
5448 5598
}
5449 5599
5450 5600
/// Lower `slice[range] = value`.
5451 -
unsafe fn lowerSliceAssign(
5452 -
    self: &mut FnLowerer,
5601 +
unsafe fn lowerSliceAssign 'arena 'phase 'function (
5602 +
    self: &mut FnLowerer 'arena 'phase 'function,
5453 5603
    rhs: *ast::Node,
5454 5604
    container: *ast::Node,
5455 5605
    range: ast::Range,
5456 5606
    info: resolver::SliceRangeInfo
5457 -
) throws (LowerError) {
5607 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5458 5608
    let r = try resolveSliceRangePtr(self, container, range, info);
5459 5609
    let elemSize = resolver::getTypeLayout(*info.itemType).size;
5460 5610
    let rhsTy = try typeOf(self, rhs);
5461 5611
5462 5612
    if let case resolver::Type::Slice { .. } = rhsTy {
5478 5628
        try emitFillLoop(self, r.dataReg, fillVal, r.count, *info.itemType, elemSize);
5479 5629
    }
5480 5630
}
5481 5631
5482 5632
/// Emit a typed fill loop: `for i in 0..count { dst[i * stride] = value; }`.
5483 -
unsafe fn emitFillLoop(
5484 -
    self: &mut FnLowerer,
5633 +
unsafe fn emitFillLoop 'arena 'phase 'function (
5634 +
    self: &mut FnLowerer 'arena 'phase 'function,
5485 5635
    dst: il::Reg,
5486 5636
    value: il::Val,
5487 5637
    count: il::Val,
5488 5638
    elemType: resolver::Type,
5489 5639
    elemSize: u32
5490 -
) throws (LowerError) {
5640 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5491 5641
    let iReg = nextReg(self);
5492 5642
    let header = try createBlockWithParam(
5493 5643
        self, "fill", il::Param { value: iReg, type: il::Type::W32 }
5494 5644
    );
5495 5645
    let body = try createBlock(self, "fill");
5528 5678
///
5529 5679
///   @entry -> @loop -> @loop
5530 5680
///               |
5531 5681
///               `----> @end
5532 5682
///
5533 -
unsafe fn lowerLoop(self: &mut FnLowerer, body: *ast::Node) throws (LowerError) {
5683 +
unsafe fn lowerLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, body: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5534 5684
    let loopBlock = try createBlock(self, "loop");
5535 5685
    let endBlock = try createBlock(self, "merge");
5536 5686
5537 5687
    // Enter the loop with the given break and continue targets.
5538 5688
    // `break` jumps to `endBlock`,
5561 5711
///
5562 5712
///   @entry -> @loop -> (true)  @body -> @loop
5563 5713
///               |
5564 5714
///               `----> (false) @end
5565 5715
///
5566 -
unsafe fn lowerWhile(self: &mut FnLowerer, w: ast::While) throws (LowerError) {
5716 +
unsafe fn lowerWhile 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, w: ast::While) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5567 5717
    let whileBlock = try createBlock(self, "while");
5568 5718
    let bodyBlock = try createBlock(self, "body");
5569 5719
    let endBlock = try createBlock(self, "merge");
5570 5720
5571 5721
    enterLoop(self, endBlock, whileBlock);
5585 5735
    try switchToAndSeal(self, endBlock);
5586 5736
    exitLoop(self);
5587 5737
}
5588 5738
5589 5739
/// Emit an increment of a variable by `1`.
5590 -
unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) {
5740 +
unsafe fn emitIncrement 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, typ: il::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5591 5741
    let cur = try useVar(self, v);
5592 5742
    let next = nextReg(self);
5593 5743
5594 5744
    emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) });
5595 5745
    defVar(self, v, il::Val::Reg(next));
5600 5750
/// The step block is created lazily (after the loop body) so that it gets
5601 5751
/// a block index higher than all body blocks. This ensures the register
5602 5752
/// allocator processes definitions before uses in forward block order,
5603 5753
/// avoiding stale assignments when a value defined deep in the body flows
5604 5754
/// through the step block as a block argument.
5605 -
unsafe fn lowerForLoop(self: &mut FnLowerer, iter: &ForIter, body: *ast::Node) throws (LowerError) {
5755 +
unsafe fn lowerForLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, iter: &ForIter, body: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5606 5756
    let loopBlock = try createBlock(self, "loop");
5607 5757
    let bodyBlock = try createBlock(self, "body");
5608 5758
    let endBlock = try createBlock(self, "merge");
5609 5759
5610 5760
    enterLoop(self, endBlock, nil);
5669 5819
5670 5820
    switchToBlock(self, endBlock);
5671 5821
}
5672 5822
5673 5823
/// Lower a `for` loop over a range, array, or slice.
5674 -
unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) {
5824 +
unsafe fn lowerFor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, f: ast::For) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5675 5825
    let savedVarsLen = enterVarScope(self);
5676 5826
    let info = resolver::forLoopInfoFor(self.low.resolver, node) else {
5677 5827
        throw LowerError::MissingMetadata;
5678 5828
    };
5679 5829
    match info {
5733 5883
    }
5734 5884
    exitVarScope(self, savedVarsLen);
5735 5885
}
5736 5886
5737 5887
/// Lower a break statement.
5738 -
unsafe fn lowerBreak(self: &mut FnLowerer) throws (LowerError) {
5888 +
unsafe fn lowerBreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5739 5889
    let ctx = currentLoop(self) else {
5740 5890
        throw LowerError::OutsideOfLoop;
5741 5891
    };
5742 5892
    try emitJmp(self, ctx.breakTarget);
5743 5893
}
5744 5894
5745 5895
/// Lower a continue statement.
5746 -
unsafe fn lowerContinue(self: &mut FnLowerer) throws (LowerError) {
5896 +
unsafe fn lowerContinue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5747 5897
    let block = try getOrCreateContinueBlock(self);
5748 5898
    try emitJmp(self, block);
5749 5899
}
5750 5900
5751 5901
/// Emit a return, blitting into the caller's return buffer if needed.
5752 5902
///
5753 5903
/// When the function has a return buffer parameter, the value is blitted
5754 5904
/// into the buffer and the buffer pointer is returned. Otherwise, the value is
5755 5905
/// returned directly.
5756 -
unsafe fn emitRetVal(self: &mut FnLowerer, val: il::Val) throws (LowerError) {
5906 +
unsafe fn emitRetVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5757 5907
    if let retReg = self.returnReg {
5758 5908
        let src = emitValToReg(self, val);
5759 5909
        let size = resolver::getResultLayout(*self.fnType.returnType, self.fnType.throwList).size
5760 5910
            if self.fnType.throwList.len > 0
5761 5911
            else resolver::getTypeLayout(*self.fnType.returnType).size;
5762 5912
5763 5913
        emit(self, il::Instr::Blit { dst: retReg, src, size: il::Val::Imm(size as i64) });
5764 5914
        emit(self, il::Instr::Ret { val: il::Val::Reg(retReg) });
5765 5915
    } else if isSmallAggregate(*self.fnType.returnType) {
5766 -
        let src = emitValToReg(self, val);
5916 +
        let mut src = emitValToReg(self, val);
5917 +
        let layout = resolver::getTypeLayout(*self.fnType.returnType);
5918 +
        if layout.alignment < resolver::PTR_SIZE or layout.size < resolver::PTR_SIZE {
5919 +
            // The return word must not read beyond the value or its alignment.
5920 +
            let word = emitReserveLayout(self, resolver::Layout {
5921 +
                size: resolver::PTR_SIZE, alignment: resolver::PTR_SIZE,
5922 +
            });
5923 +
            emitStoreW64At(self, il::Val::Imm(0), word, 0);
5924 +
            emit(self, il::Instr::Blit { dst: word, src, size: il::Val::Imm(layout.size as i64) });
5925 +
            set src = word;
5926 +
        }
5767 5927
        let dst = nextReg(self);
5768 5928
5769 5929
        emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset: 0 });
5770 5930
        emit(self, il::Instr::Ret { val: il::Val::Reg(dst) });
5771 5931
    } else {
5772 5932
        emit(self, il::Instr::Ret { val });
5773 5933
    }
5774 5934
}
5775 5935
5776 5936
/// Lower a return statement.
5777 -
unsafe fn lowerReturnStmt(self: &mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) {
5937 +
unsafe fn lowerReturnStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, value: ?*ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5778 5938
    let mut val = il::Val::Undef;
5779 5939
    if let expr = value {
5780 5940
        set val = try lowerExpr(self, expr);
5781 5941
    }
5782 5942
    if blockHasTerminator(self) {
5785 5945
    set val = try applyCoercion(self, node, val);
5786 5946
    try emitRetVal(self, val);
5787 5947
}
5788 5948
5789 5949
/// Lower a throw statement.
5790 -
unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) {
5950 +
unsafe fn lowerThrowStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, expr: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5791 5951
    assert self.fnType.throwList.len > 0;
5792 5952
5793 5953
    let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1
5794 5954
        else try typeOf(self, expr);
5795 5955
    let tag = getOrAssignErrorTag(self.low, errType) as i64;
5798 5958
5799 5959
    try emitRetVal(self, resultVal);
5800 5960
}
5801 5961
5802 5962
/// Ensure a value is in a register (eg. for branch conditions).
5803 -
unsafe fn emitValToReg(self: &mut FnLowerer, val: il::Val) -> il::Reg {
5963 +
unsafe fn emitValToReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function {
5804 5964
    match val {
5805 5965
        case il::Val::Reg(r) => return r,
5806 5966
        case il::Val::Imm(_), il::Val::DataSym(_), il::Val::FnAddr(_) => {
5807 5967
            let dst = nextReg(self);
5808 5968
            emit(self, il::Instr::Copy { dst, val });
5850 6010
///       // ...
5851 6011
///       jmp @end(%b);
5852 6012
///     @end(w8 %result)
5853 6013
///       ret %result;
5854 6014
///
5855 -
unsafe fn lowerLogicalOp(
5856 -
    self: &mut FnLowerer,
6015 +
unsafe fn lowerLogicalOp 'arena 'phase 'function (
6016 +
    self: &mut FnLowerer 'arena 'phase 'function,
5857 6017
    binop: ast::BinOp,
5858 6018
    thenLabel: *[u8],
5859 6019
    elseLabel: *[u8],
5860 6020
    mergeLabel: *[u8],
5861 6021
    op: LogicalOp
5862 -
) -> il::Val throws (LowerError) {
6022 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5863 6023
    let thenBlock = try createBlock(self, thenLabel);
5864 6024
    let elseBlock = try createBlock(self, elseLabel);
5865 6025
5866 6026
    let resultReg = nextReg(self);
5867 6027
    let mergeBlock = try createBlockWithParam(
5900 6060
    try switchToAndSeal(self, mergeBlock);
5901 6061
    return il::Val::Reg(resultReg);
5902 6062
}
5903 6063
5904 6064
/// Lower a conditional expression (`thenExpr if condition else elseExpr`).
5905 -
unsafe fn lowerCondExpr(self: &mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val
5906 -
    throws (LowerError)
6065 +
unsafe fn lowerCondExpr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, cond: ast::CondExpr) -> il::Val
6066 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
5907 6067
{
5908 6068
    let typ = try typeOf(self, node);
5909 6069
    let thenBlock = try createBlock(self, "cond#then");
5910 6070
    let elseBlock = try createBlock(self, "cond#else");
5911 6071
5967 6127
        else => return nil,
5968 6128
    }
5969 6129
}
5970 6130
5971 6131
/// Lower a binary operation.
5972 -
unsafe fn lowerBinOp(self: &mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) {
6132 +
unsafe fn lowerBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5973 6133
    // Short-circuit logical operators don't evaluate both operands eagerly.
5974 6134
    if binop.op == ast::BinaryOp::And {
5975 6135
        return try lowerLogicalOp(self, binop, "and#then", "and#else", "and#end", LogicalOp::And);
5976 6136
    } else if binop.op == ast::BinaryOp::Or {
5977 6137
        return try lowerLogicalOp(self, binop, "or#then", "or#else", "or#end", LogicalOp::Or);
6026 6186
    }
6027 6187
    return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy));
6028 6188
}
6029 6189
6030 6190
/// Emit an aggregate equality or inequality comparison.
6031 -
unsafe fn emitAggregateEqOp(
6032 -
    self: &mut FnLowerer,
6191 +
unsafe fn emitAggregateEqOp 'arena 'phase 'function (
6192 +
    self: &mut FnLowerer 'arena 'phase 'function,
6033 6193
    op: ast::BinaryOp,
6034 6194
    typ: resolver::Type,
6035 6195
    a: il::Val,
6036 6196
    b: il::Val
6037 -
) -> il::Val throws (LowerError) {
6197 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6038 6198
    let regA = emitValToReg(self, a);
6039 6199
    let regB = emitValToReg(self, b);
6040 6200
    let result = try lowerAggregateEq(self, typ, regA, regB, 0);
6041 6201
6042 6202
    if op == ast::BinaryOp::Ne {
6044 6204
    }
6045 6205
    return result;
6046 6206
}
6047 6207
6048 6208
/// Emit a scalar binary operation instruction.
6049 -
unsafe fn emitScalarBinOp(
6050 -
    self: &mut FnLowerer,
6209 +
unsafe fn emitScalarBinOp 'arena 'phase 'function (
6210 +
    self: &mut FnLowerer 'arena 'phase 'function,
6051 6211
    op: ast::BinaryOp,
6052 6212
    typ: il::Type,
6053 6213
    a: il::Val,
6054 6214
    b: il::Val,
6055 6215
    unsigned: bool
6056 -
) -> il::Val {
6216 +
) -> il::Val where 'arena: 'phase, 'phase: 'function {
6057 6217
    let dst = nextReg(self);
6058 6218
    let mut needsExt: bool = false;
6059 6219
    match op {
6060 6220
        case ast::BinaryOp::Add => {
6061 6221
            emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst, a, b });
6121 6281
    }
6122 6282
    return il::Val::Reg(dst);
6123 6283
}
6124 6284
6125 6285
/// Normalize sub-word values to well-defined high bits.
6126 -
unsafe fn normalizeSubword(self: &mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val {
6286 +
unsafe fn normalizeSubword 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function {
6127 6287
    if typ == il::Type::W8 or typ == il::Type::W16 {
6128 6288
        let extDst: il::Reg = nextReg(self);
6129 6289
        if unsigned {
6130 6290
            emit(self, il::Instr::Zext { typ, dst: extDst, val });
6131 6291
        } else {
6135 6295
    }
6136 6296
    return val;
6137 6297
}
6138 6298
6139 6299
/// Lower a unary operation.
6140 -
unsafe fn lowerUnOp(self: &mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) {
6300 +
unsafe fn lowerUnOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6141 6301
    if unop.op == ast::UnaryOp::Neg {
6142 6302
        if let case ast::NodeValue::Number(lit) = unop.value.value {
6143 6303
            return il::Val::Imm((0 - lit.magnitude) as i64);
6144 6304
        }
6145 6305
    }
6167 6327
    }
6168 6328
    return il::Val::Reg(dst);
6169 6329
}
6170 6330
6171 6331
/// Lower a cast expression (`x as T`).
6172 -
unsafe fn lowerCast(self: &mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) {
6332 +
unsafe fn lowerCast 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6173 6333
    let val = try lowerExpr(self, cast.value);
6174 6334
6175 6335
    let srcType = try typeOf(self, cast.value);
6176 6336
    let dstType = try typeOf(self, node);
6177 6337
    if resolver::typesEqual(srcType, dstType) {
6199 6359
6200 6360
/// Lower a string literal to a slice value.
6201 6361
///
6202 6362
/// String literals are stored as global data and the result is a slice
6203 6363
/// pointing to the data with the appropriate length.
6204 -
unsafe fn lowerStringLit(self: &mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) {
6364 +
unsafe fn lowerStringLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6205 6365
    // Get the slice type from the node.
6206 6366
    let sliceTy = try typeOf(self, node);
6207 6367
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6208 6368
        else throw LowerError::ExpectedSliceOrArray;
6209 6369
    // Build the string data value.
6218 6378
        self, &result, 1, true, item, mutable, s.len
6219 6379
    );
6220 6380
}
6221 6381
6222 6382
/// Lower a builtin call expression.
6223 -
unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) {
6383 +
unsafe fn lowerBuiltinCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6224 6384
    match kind {
6225 6385
        case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args),
6226 6386
        case ast::Builtin::SizeOf, ast::Builtin::AlignOf => {
6227 6387
            let constVal = resolver::constValueEntry(self.low.resolver, node) else {
6228 6388
                throw LowerError::MissingConst(node);
6231 6391
        }
6232 6392
    }
6233 6393
}
6234 6394
6235 6395
/// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call.
6236 -
unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) {
6396 +
unsafe fn lowerSliceOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6237 6397
    if args.len <> 2 and args.len <> 3 {
6238 6398
        throw LowerError::InvalidArgCount;
6239 6399
    }
6240 6400
    let sliceTy = try typeOf(self, node);
6241 6401
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6251 6411
    }
6252 6412
    return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal);
6253 6413
}
6254 6414
6255 6415
/// Lower a `try` expression.
6256 -
unsafe fn lowerTry(self: &mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) {
6416 +
unsafe fn lowerTry 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6257 6417
    let case ast::NodeValue::Call(callExpr) = t.expr.value else {
6258 6418
        throw LowerError::ExpectedCall;
6259 6419
    };
6260 6420
    let calleeTy = try typeOf(self, callExpr.callee);
6261 6421
    let case resolver::Type::Fn(calleeInfo) = calleeTy else {
6266 6426
    // Type of the try expression, which is either the return type of the function
6267 6427
    // if successful, or an optional of it, if using `try?`.
6268 6428
    let tryExprTy = try typeOf(self, node);
6269 6429
    // Check for trait method dispatch or standalone method call.
6270 6430
    let mut resVal: il::Val = undefined;
6431 +
    let mut initialization: ?SessionInitialization = nil;
6271 6432
    let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra;
6272 -
    if let case resolver::NodeExtra::TraitMethodCall {
6433 +
    if let case resolver::NodeExtra::SessionAllocation(allocation) = callNodeExtra {
6434 +
        let prepared = try prepareSessionAllocation(self, callExpr, allocation);
6435 +
        set initialization = prepared;
6436 +
        set resVal = prepared.result;
6437 +
    } else if let case resolver::NodeExtra::TraitMethodCall {
6273 6438
        traitInfo, methodIndex
6274 6439
    } = callNodeExtra {
6275 6440
        set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex);
6276 6441
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6277 6442
        set resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6278 6443
    } else {
6279 6444
        set resVal = try lowerCall(self, t.expr, callExpr);
6280 6445
    }
6446 +
    if blockHasTerminator(self) {
6447 +
        return il::Val::Undef;
6448 +
    }
6281 6449
    let base = emitValToReg(self, resVal); // The result value.
6282 6450
    let tagReg = resultTagReg(self, base); // The result tag.
6283 6451
6284 6452
    let okBlock = try createBlock(self, "ok"); // Block if success.
6285 6453
    let errBlock = try createBlock(self, "err"); // Block if failure.
6301 6469
    try sealBlock(self, errBlock);
6302 6470
6303 6471
    // Success path: extract the successful value from the result and store it
6304 6472
    // in the result slot for later use after the merge point.
6305 6473
    switchToBlock(self, okBlock);
6474 +
    if let prepared = initialization {
6475 +
        try initializeSessionAllocation(self, prepared);
6476 +
    }
6306 6477
6307 6478
    if okValueTy == resolver::Type::Never {
6308 6479
        emit(self, il::Instr::Unreachable);
6309 6480
    } else if let slot = resultSlot {
6310 6481
        // Extract the success payload. If the result type differs from the payload
6397 6568
/// Lower typed multi-catch clauses.
6398 6569
///
6399 6570
/// Emits a switch on the global error tag to dispatch to the correct catch
6400 6571
/// clause. Each typed clause extracts the error payload for its specific type
6401 6572
/// and binds it to the clause's identifier.
6402 -
unsafe fn lowerMultiCatch(
6403 -
    self: &mut FnLowerer,
6573 +
unsafe fn lowerMultiCatch 'arena 'phase 'function (
6574 +
    self: &mut FnLowerer 'arena 'phase 'function,
6404 6575
    catches: *[*ast::Node],
6405 6576
    calleeInfo: *resolver::FnType,
6406 6577
    base: il::Reg,
6407 6578
    tagReg: il::Reg,
6408 6579
    mergeBlock: &mut ?BlockId
6409 -
) throws (LowerError) {
6580 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6410 6581
    let entry = currentBlock(self);
6411 6582
6412 6583
    // First pass: create blocks, resolve error types, and build switch cases.
6413 6584
    let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined;
6414 6585
    let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined;
6428 6599
6429 6600
            cases.append(il::SwitchCase {
6430 6601
                value: getOrAssignErrorTag(self.low, errTy) as i64,
6431 6602
                target: *blocks[i],
6432 6603
                args: &mut []
6433 -
            }, self.allocator);
6604 +
            }, alloc::arenaAllocator(self.arena));
6434 6605
        } else {
6435 6606
            set errTypes[i] = nil;
6436 6607
            set defaultIdx = i;
6437 6608
        }
6438 6609
    }
6485 6656
/// Emit a byte-copy loop: `for i in 0..size { dst[i] = src[i]; }`.
6486 6657
///
6487 6658
/// Used when `blit` cannot be used because the copy size is dynamic.
6488 6659
/// Terminates the current block and leaves the builder positioned
6489 6660
/// after the loop.
6490 -
unsafe fn emitByteCopyLoop(
6491 -
    self: &mut FnLowerer,
6661 +
unsafe fn emitByteCopyLoop 'arena 'phase 'function (
6662 +
    self: &mut FnLowerer 'arena 'phase 'function,
6492 6663
    dst: il::Reg,
6493 6664
    src: il::Reg,
6494 6665
    size: il::Val,
6495 6666
    label: *[u8]
6496 -
) throws (LowerError) {
6667 +
) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6497 6668
    let iReg = nextReg(self);
6498 6669
    let header = try createBlockWithParam(
6499 6670
        self, label, il::Param { value: iReg, type: il::Type::W32 }
6500 6671
    );
6501 6672
    let body = try createBlock(self, label);
6544 6715
///
6545 6716
///     @store:
6546 6717
///       store element at ptr + len * stride
6547 6718
///       increment len
6548 6719
///
6549 -
unsafe fn lowerSliceAppend(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) {
6720 +
unsafe fn lowerSliceAppend 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6550 6721
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6551 6722
        else throw LowerError::MissingMetadata;
6552 6723
6553 6724
    // Get the address of the slice header.
6554 6725
    let sliceVal = try lowerExpr(self, access.parent);
6630 6801
6631 6802
/// Lower `slice.delete(index)`.
6632 6803
///
6633 6804
/// Bounds-check the index, shift elements after it by one stride
6634 6805
/// via a byte-copy loop, and decrement `len`.
6635 -
unsafe fn lowerSliceDelete(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) {
6806 +
unsafe fn lowerSliceDelete 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, elemType: *resolver::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6636 6807
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6637 6808
        else throw LowerError::MissingMetadata;
6638 6809
6639 6810
    let elemLayout = resolver::getTypeLayout(*elemType);
6640 6811
    let stride = elemLayout.size;
6669 6840
    let newLen = emitTypedBinOp(self, il::BinOp::Sub, il::Type::W32, lenVal, il::Val::Imm(1));
6670 6841
6671 6842
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
6672 6843
}
6673 6844
6845 +
/// Initializer values retained across a fallible session reservation.
6846 +
record SessionInitialization: Copy {
6847 +
    /// Resolved allocation operation and element type.
6848 +
    allocation: resolver::SessionAllocation,
6849 +
    /// Runtime reservation result.
6850 +
    result: il::Val,
6851 +
    /// Evaluated object, fill value, or source slice.
6852 +
    value: il::Val,
6853 +
    /// Evaluated number of elements.
6854 +
    count: il::Val,
6855 +
}
6856 +
6857 +
/// Evaluate initializer values and reserve their session storage.
6858 +
unsafe fn prepareSessionAllocation 'arena 'phase 'function (
6859 +
    self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, allocation: resolver::SessionAllocation
6860 +
) -> SessionInitialization throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6861 +
    let case ast::NodeValue::FieldAccess(access) = call.callee.value else throw LowerError::ExpectedCall;
6862 +
    let receiver = try lowerExpr(self, access.parent);
6863 +
    let sessionReg = emitValToReg(self, receiver);
6864 +
    let dataReg = nextReg(self);
6865 +
    emitLoadW64At(self, dataReg, sessionReg, TRAIT_OBJ_DATA_OFFSET);
6866 +
    let vtableReg = nextReg(self);
6867 +
    emitLoadW64At(self, vtableReg, sessionReg, TRAIT_OBJ_VTABLE_OFFSET);
6868 +
    let functionReg = nextReg(self);
6869 +
    emitLoadW64At(
6870 +
        self, functionReg, vtableReg,
6871 +
        (allocation.methodIndex * resolver::PTR_SIZE) as i32,
6872 +
    );
6873 +
    let value = try lowerCallArg(self, call.args[0], true);
6874 +
    if blockHasTerminator(self) {
6875 +
        return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef };
6876 +
    }
6877 +
    let slice = allocation.kind <> resolver::SessionAllocationKind::New;
6878 +
    let mut count = il::Val::Imm(1);
6879 +
    if allocation.kind == resolver::SessionAllocationKind::Copy {
6880 +
        set count = loadSliceLen(self, emitValToReg(self, value));
6881 +
    } else if allocation.kind == resolver::SessionAllocationKind::Fill {
6882 +
        set count = try lowerExpr(self, call.args[1]);
6883 +
        if blockHasTerminator(self) {
6884 +
            return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef };
6885 +
        }
6886 +
    }
6887 +
    let layout = resolver::getTypeLayout(*allocation.item);
6888 +
    let size = layout.size if layout.size > 0 else 1;
6889 +
    let alignment = layout.alignment if layout.alignment > 0 else 1;
6890 +
    let runtime = allocation.traitInfo.methods[allocation.methodIndex].fnType;
6891 +
    let argOffset: u32 = 1 if requiresReturnParam(runtime) else 0;
6892 +
    let args = try allocVals(self, (4 if slice else 3) + argOffset);
6893 +
    set args[argOffset] = il::Val::Reg(dataReg);
6894 +
    set args[argOffset + 1] = il::Val::Imm(size as i64);
6895 +
    set args[argOffset + 2] = il::Val::Imm(alignment as i64);
6896 +
    if slice {
6897 +
        set args[argOffset + 3] = count;
6898 +
    }
6899 +
    let result = try emitCallValue(self, il::Val::Reg(functionReg), runtime, args);
6900 +
    return SessionInitialization { allocation, result, value, count };
6901 +
}
6902 +
6903 +
/// Initialize successful reservation storage before its reference is published.
6904 +
unsafe fn initializeSessionAllocation 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, prepared: SessionInitialization)
6905 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
6906 +
{
6907 +
    let allocation = prepared.allocation;
6908 +
    let base = emitValToReg(self, prepared.result);
6909 +
    let value = prepared.value;
6910 +
    let count = prepared.count;
6911 +
    let layout = resolver::getTypeLayout(*allocation.item);
6912 +
    let destination = nextReg(self);
6913 +
    emitLoadW64At(self, destination, base, RESULT_VAL_OFFSET);
6914 +
    match allocation.kind {
6915 +
        case resolver::SessionAllocationKind::New =>
6916 +
            try emitStore(self, destination, 0, *allocation.item, value),
6917 +
        case resolver::SessionAllocationKind::Copy => {
6918 +
            let source = loadSlicePtr(self, emitValToReg(self, value));
6919 +
            let bytes = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, count, il::Val::Imm(layout.size as i64));
6920 +
            try emitByteCopyLoop(self, destination, source, bytes, "allocate.copy");
6921 +
        }
6922 +
        case resolver::SessionAllocationKind::Fill =>
6923 +
            try emitFillLoop(self, destination, value, count, *allocation.item, layout.size),
6924 +
    }
6925 +
}
6926 +
6674 6927
/// Lower a call expression, which may be a function call or type constructor.
6675 -
unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6928 +
unsafe fn lowerCallOrCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6676 6929
    let nodeData = resolver::nodeData(self.low.resolver, node).extra;
6677 6930
6678 6931
    // Check for slice method dispatch.
6679 6932
    if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData {
6680 6933
        return try lowerSliceAppend(self, call, elemType);
6690 6943
    // Check for standalone method call.
6691 6944
    if let case resolver::NodeExtra::MethodCall { method } = nodeData {
6692 6945
        return try lowerMethodCall(self, node, call, method);
6693 6946
    }
6694 6947
    if let sym = resolver::nodeData(self.low.resolver, call.callee).sym {
6695 -
        if let case resolver::SymbolData::Type(nominal) = sym.data {
6696 -
            let case resolver::NominalType::Record(_) = *nominal else {
6697 -
                throw LowerError::ExpectedRecord;
6698 -
            };
6948 +
        if let case resolver::SymbolData::Type(_) = sym.data {
6949 +
            let ty = try typeOf(self, node);
6950 +
            let case resolver::Type::Nominal(nominal) = ty else throw LowerError::ExpectedRecord;
6699 6951
            return try lowerRecordCtor(self, nominal, call.args);
6700 6952
        }
6701 6953
        if let case resolver::SymbolData::Variant { .. } = sym.data {
6702 6954
            return try lowerUnionCtor(self, node, sym, call);
6703 6955
        }
6712 6964
///     load w64 %data %obj 0          // data pointer
6713 6965
///     load w64 %vtable %obj 8        // v-table pointer
6714 6966
///     load w64 %fn %vtable <slot>    // function pointer
6715 6967
///     call <retTy> %ret %fn(%data, args...)
6716 6968
///
6717 -
unsafe fn lowerTraitMethodCall(
6718 -
    self: &mut FnLowerer,
6969 +
unsafe fn lowerTraitMethodCall 'arena 'phase 'function (
6970 +
    self: &mut FnLowerer 'arena 'phase 'function,
6719 6971
    node: *ast::Node,
6720 6972
    call: ast::Call,
6721 6973
    traitInfo: *unsafe resolver::TraitType,
6722 6974
    methodIndex: u32
6723 -
) -> il::Val throws (LowerError) {
6975 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6724 6976
    // Method calls look like field accesses.
6725 6977
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6726 6978
        else throw LowerError::MissingMetadata;
6727 6979
6728 6980
    // Lower the trait object expression.
6774 7026
6775 7027
/// Lower a call argument, snapshotting aggregate place expressions before
6776 7028
/// evaluating later arguments. Aggregates are represented by addresses in the
6777 7029
/// IL, so retaining the original address would let a later argument mutation
6778 7030
/// change the value already supplied for this argument.
6779 -
unsafe fn lowerCallArg(self: &mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val
6780 -
    throws (LowerError)
7031 +
unsafe fn lowerCallArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, arg: *ast::Node, hasLater: bool) -> il::Val
7032 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
6781 7033
{
6782 7034
    let val = try lowerExpr(self, arg);
6783 7035
6784 7036
    if hasLater and ast::isPlaceExpr(arg) {
6785 7037
        let argType = try effectiveType(self, arg);
6794 7046
///
6795 7047
/// All call lowering paths (regular, trait method, standalone method) converge
6796 7048
/// here after preparing the callee value, function type, and argument array.
6797 7049
/// The `args` slice must already include a slot at index zero for the hidden
6798 7050
/// return parameter; that slot is filled by this function.
6799 -
unsafe fn emitCallValue(
6800 -
    self: &mut FnLowerer,
7051 +
unsafe fn emitCallValue 'arena 'phase 'function (
7052 +
    self: &mut FnLowerer 'arena 'phase 'function,
6801 7053
    callee: il::Val,
6802 7054
    fnInfo: *resolver::FnType,
6803 7055
    args: *unsafe mut [il::Val],
6804 -
) -> il::Val throws (LowerError) {
7056 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6805 7057
    let retTy = *fnInfo.returnType;
6806 7058
6807 7059
    if requiresReturnParam(fnInfo) {
6808 7060
        if fnInfo.throwList.len > 0 {
6809 7061
            let layout = resolver::getResultLayout(retTy, fnInfo.throwList);
6856 7108
6857 7109
/// Lower a method receiver expression to a pointer value.
6858 7110
///
6859 7111
/// If the parent is already a pointer type, the value is used directly.
6860 7112
/// If the parent is a value type (eg. a local record), its address is taken.
6861 -
unsafe fn lowerReceiver(self: &mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val
6862 -
    throws (LowerError)
7113 +
unsafe fn lowerReceiver 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, parent: *ast::Node, parentTy: resolver::Type) -> il::Val
7114 +
    throws (LowerError) where 'arena: 'phase, 'phase: 'function
6863 7115
{
6864 7116
    if let case resolver::Type::Pointer { .. } = parentTy {
6865 7117
        // Already a pointer: lower and use directly.
6866 7118
        return try lowerExpr(self, parent);
6867 7119
    }
6884 7136
/// Given `obj.method(args)` where `method` is a standalone method on a concrete type,
6885 7137
/// emits a direct call with the receiver address as the first argument:
6886 7138
///
6887 7139
///     call <retTy> %ret @Type::method(&obj, args...)
6888 7140
///
6889 -
unsafe fn lowerMethodCall(
6890 -
    self: &mut FnLowerer,
7141 +
unsafe fn lowerMethodCall 'arena 'phase 'function (
7142 +
    self: &mut FnLowerer 'arena 'phase 'function,
6891 7143
    node: *ast::Node,
6892 7144
    call: ast::Call,
6893 7145
    method: &resolver::MethodEntry,
6894 -
) -> il::Val throws (LowerError) {
7146 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6895 7147
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6896 7148
        else throw LowerError::MissingMetadata;
6897 7149
6898 7150
    // Get the receiver as a pointer.
6899 7151
    let parentTy = try typeOf(self, access.parent);
6914 7166
    }
6915 7167
    return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args);
6916 7168
}
6917 7169
6918 7170
/// Check if a call is to a compiler intrinsic and lower it directly.
6919 -
unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) {
7171 +
unsafe fn lowerIntrinsicCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> ?il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6920 7172
    // Get the callee symbol and check if it's marked as an intrinsic.
6921 7173
    let sym = resolver::nodeData(self.low.resolver, call.callee).sym else {
6922 7174
        // Expressions or function pointers may not have an associated symbol.
6923 7175
        return nil;
6924 7176
    };
6926 7178
        return nil;
6927 7179
    }
6928 7180
    // Check for known intrinsic names.
6929 7181
    if mem::eq(sym.name, "ecall") {
6930 7182
        return try lowerEcall(self, call);
7183 +
    } else if mem::eq(sym.name, "deviceRead8") {
7184 +
        return try lowerDevice(self, call, il::Type::W8, false);
7185 +
    } else if mem::eq(sym.name, "deviceWrite8") {
7186 +
        return try lowerDevice(self, call, il::Type::W8, true);
7187 +
    } else if mem::eq(sym.name, "deviceRead16") {
7188 +
        return try lowerDevice(self, call, il::Type::W16, false);
7189 +
    } else if mem::eq(sym.name, "deviceWrite16") {
7190 +
        return try lowerDevice(self, call, il::Type::W16, true);
7191 +
    } else if mem::eq(sym.name, "deviceRead32") {
7192 +
        return try lowerDevice(self, call, il::Type::W32, false);
7193 +
    } else if mem::eq(sym.name, "deviceWrite32") {
7194 +
        return try lowerDevice(self, call, il::Type::W32, true);
7195 +
    } else if mem::eq(sym.name, "deviceRead64") {
7196 +
        return try lowerDevice(self, call, il::Type::W64, false);
7197 +
    } else if mem::eq(sym.name, "deviceWrite64") {
7198 +
        return try lowerDevice(self, call, il::Type::W64, true);
6931 7199
    } else if mem::eq(sym.name, "ebreak") {
6932 7200
        return try lowerEbreak(self, call);
6933 7201
    } else if mem::eq(sym.name, "memoryFence") {
6934 7202
        return try lowerMemoryFence(self, call);
6935 7203
    } else {
6936 7204
        throw LowerError::UnknownIntrinsic;
6937 7205
    }
6938 7206
}
6939 7207
6940 7208
/// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`.
6941 -
unsafe fn lowerEcall(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
7209 +
unsafe fn lowerEcall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6942 7210
    if call.args.len <> 5 {
6943 7211
        throw LowerError::InvalidArgCount;
6944 7212
    }
6945 7213
    let num = try lowerExpr(self, call.args[0]);
6946 7214
    let a0 = try lowerExpr(self, call.args[1]);
6953 7221
6954 7222
    return il::Val::Reg(dst);
6955 7223
}
6956 7224
6957 7225
/// Lower an ebreak intrinsic: `ebreak()`.
6958 -
unsafe fn lowerEbreak(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
7226 +
unsafe fn lowerEbreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6959 7227
    if call.args.len <> 0 {
6960 7228
        throw LowerError::InvalidArgCount;
6961 7229
    }
6962 7230
    emit(self, il::Instr::Ebreak);
6963 7231
6964 7232
    return il::Val::Undef;
6965 7233
}
6966 7234
6967 7235
/// Lower `memoryFence()`.
6968 -
unsafe fn lowerMemoryFence(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
7236 +
unsafe fn lowerMemoryFence 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6969 7237
    if call.args.len <> 0 {
6970 7238
        throw LowerError::InvalidArgCount;
6971 7239
    }
6972 7240
    emit(self, il::Instr::MemoryFence);
6973 7241
    return il::Val::Undef;
6974 7242
}
6975 7243
6976 7244
/// Resolve callee to an IL value. For direct function calls, use the symbol name.
6977 7245
/// For variables holding function pointers or complex expressions (eg. `array[i]()`),
6978 7246
/// lower the callee expression.
6979 -
unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) {
7247 +
unsafe fn lowerCallee 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, callee: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
6980 7248
    if let sym = resolver::nodeData(self.low.resolver, callee).sym {
6981 7249
        if let case ast::NodeValue::FnDecl(_) = sym.node.value {
6982 7250
            // First try to look up the symbol in our registered functions.
6983 7251
            // This handles cross-package calls correctly, since packages are
6984 7252
            // lowered in dependency order.
6985 -
            if let qualName = lookupFnSym(self.low, sym) {
7253 +
            if let qualName = lookupSymbolName(self.low, sym) {
6986 7254
                return il::Val::FnAddr(qualName);
6987 7255
            }
6988 7256
            // Fall back to computing the qualified name from the module graph.
6989 7257
            // This works for functions in the current package.
6990 -
            let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else {
6991 -
                throw LowerError::MissingMetadata;
6992 -
            };
7258 +
            let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
6993 7259
            return il::Val::FnAddr(qualifyName(self.low, modId, sym.name));
6994 7260
        }
6995 7261
    }
6996 7262
    return try lowerExpr(self, callee);
6997 7263
}
6998 7264
6999 7265
/// Lower a function call expression.
7000 -
unsafe fn lowerCall(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
7266 +
unsafe fn lowerCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7001 7267
    // Check for intrinsic calls before normal call lowering.
7002 7268
    if let intrinsicVal = try lowerIntrinsicCall(self, call) {
7003 7269
        return intrinsicVal;
7004 7270
    }
7005 7271
    let calleeTy = try typeOf(self, call.callee);
7017 7283
7018 7284
    return try emitCallValue(self, callee, fnInfo, args);
7019 7285
}
7020 7286
7021 7287
/// Apply coercions requested by the resolver.
7022 -
unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) {
7288 +
unsafe fn applyCoercion 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7023 7289
    let coerce = resolver::coercionFor(self.low.resolver, node) else {
7024 7290
        return val;
7025 7291
    };
7026 7292
    match coerce {
7027 7293
        case resolver::Coercion::OptionalLift(optType) => {
7046 7312
7047 7313
/// Lower an implicit numeric cast coercion.
7048 7314
///
7049 7315
/// Handles widening conversions between integer types. Uses sign-extension
7050 7316
/// for signed source types and zero-extension for unsigned source types.
7051 -
unsafe fn lowerNumericCast(self: &mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val {
7317 +
unsafe fn lowerNumericCast 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function {
7052 7318
    let srcLayout = resolver::getTypeLayout(srcType);
7053 7319
    let dstLayout = resolver::getTypeLayout(dstType);
7054 7320
7055 7321
    if srcLayout.size == dstLayout.size {
7056 7322
        // Same size: bit pattern is unchanged, value is returned as-is.
7070 7336
    }
7071 7337
    return il::Val::Reg(dst);
7072 7338
}
7073 7339
7074 7340
/// Lower a global value symbol.
7075 -
unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val {
7341 +
unsafe fn lowerGlobalValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function {
7076 7342
    // Function pointer reference: return the function's address directly.
7077 7343
    // Functions have no separate storage cell in the data section.
7078 7344
    if let case resolver::Type::Fn(_) = ty {
7079 7345
        return il::Val::Reg(emitFnAddr(self, sym));
7080 7346
    }
7082 7348
7083 7349
    return emitRead(self, src, 0, ty);
7084 7350
}
7085 7351
7086 7352
/// Lower an identifier that refers to a global symbol.
7087 -
unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7353 +
unsafe fn lowerGlobalSymbol 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7088 7354
    // First try to get a compile-time constant value.
7089 7355
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7090 7356
        return try constValueToVal(self, constVal, node);
7091 7357
    }
7092 7358
    // Otherwise get the symbol.
7103 7369
        else => throw LowerError::UnexpectedNodeValue(node),
7104 7370
    }
7105 7371
}
7106 7372
7107 7373
/// Lower an assignment to a static variable.
7108 -
unsafe fn lowerStaticAssign(self: &mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) {
7374 +
unsafe fn lowerStaticAssign 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: *ast::Node, val: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7109 7375
    let sym = try symOf(self, target);
7110 7376
    let case resolver::SymbolData::Value { type, .. } = sym.data else {
7111 7377
        throw LowerError::ImmutableAssignment;
7112 7378
    };
7113 7379
    let dst = emitDataAddr(self, sym);
7115 7381
    try emitStore(self, dst, 0, type, val);
7116 7382
}
7117 7383
7118 7384
/// Lower a scope access expression like `Module::Const` or `Union::Variant`.
7119 7385
/// This doesn't handle record literal variants.
7120 -
unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7386 +
unsafe fn lowerScopeAccess 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7121 7387
    // First try to get a compile-time constant value.
7122 7388
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7123 7389
        return try constValueToVal(self, constVal, node);
7124 7390
    }
7125 7391
    // Otherwise get the associated symbol.
7172 7438
    }
7173 7439
}
7174 7440
7175 7441
/// Lower an expression AST node to an IL value.
7176 7442
/// This is the main expression dispatch, all expression nodes go through here.
7177 -
unsafe fn lowerExpr(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7443 +
unsafe fn lowerExpr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7178 7444
    if self.low.options.debug {
7179 7445
        set self.srcLoc.offset = node.span.offset;
7180 7446
    }
7181 7447
    let mut val: il::Val = undefined;
7182 7448
7263 7529
            set val = try lowerArrayLit(self, node, elements);
7264 7530
        }
7265 7531
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
7266 7532
            set val = try lowerArrayRepeatLit(self, node, repeat);
7267 7533
        }
7534 +
        case ast::NodeValue::RegionApply { value, .. } => {
7535 +
            set val = try lowerExpr(self, value);
7536 +
        }
7268 7537
        case ast::NodeValue::As(cast) => {
7269 7538
            set val = try lowerCast(self, node, cast);
7270 7539
        }
7271 7540
        case ast::NodeValue::CondExpr(cond) => {
7272 7541
            set val = try lowerCondExpr(self, node, cond);
7335 7604
/// are used. If a Radiance type doesn't fit in a machine word, it is passed
7336 7605
/// by reference.
7337 7606
///
7338 7607
/// The IL doesn't track signedness - that's encoded in the instructions
7339 7608
/// (e.g., Slt vs Ult).
7340 -
unsafe fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type {
7609 +
unsafe fn ilType 'arena 'phase (self: &mut Lowerer 'arena 'phase, typ: resolver::Type) -> il::Type where 'arena: 'phase {
7341 7610
    match typ {
7342 7611
        case resolver::Type::Bool,
7343 7612
             resolver::Type::I8,
7344 7613
             resolver::Type::U8 => return il::Type::W8,
7345 7614
        case resolver::Type::I16,
7351 7620
             resolver::Type::Pointer { .. },
7352 7621
             resolver::Type::Slice { .. },
7353 7622
             resolver::Type::TraitObject { .. },
7354 7623
             resolver::Type::Array(_),
7355 7624
             resolver::Type::Optional(_),
7356 -
             resolver::Type::Fn(_) => return il::Type::W64,
7625 +
             resolver::Type::Fn(_),
7626 +
             resolver::Type::Session(_),
7627 +
             resolver::Type::Cell { .. } => return il::Type::W64,
7357 7628
        case resolver::Type::Nominal(_) => {
7358 7629
            if resolver::isVoidUnion(typ) {
7359 7630
                return il::Type::W8;
7360 7631
            }
7361 7632
            return il::Type::W64;
7370 7641
        case resolver::Type::Int => return il::Type::W64,
7371 7642
        case resolver::Type::Opaque => panic "ilType: opaque type must be behind a pointer",
7372 7643
        else => panic "ilType: type cannot be lowered",
7373 7644
    }
7374 7645
}
7646 +
7647 +
/// Lower one fixed-width MMIO operation without exposing a pointer value.
7648 +
unsafe fn lowerDevice 'arena 'phase 'function (
7649 +
    self: &mut FnLowerer 'arena 'phase 'function,
7650 +
    call: ast::Call,
7651 +
    typ: il::Type,
7652 +
    writing: bool
7653 +
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7654 +
    let count: u32 = 3 if writing else 2;
7655 +
    if call.args.len <> count {
7656 +
        throw LowerError::InvalidArgCount;
7657 +
    }
7658 +
    let handle = try lowerExpr(self, call.args[0]);
7659 +
    let offset = try lowerExpr(self, call.args[1]);
7660 +
    if writing {
7661 +
        let value = try lowerExpr(self, call.args[2]);
7662 +
        emit(self, il::Instr::DeviceWrite { typ, handle, offset, value });
7663 +
        return il::Val::Undef;
7664 +
    }
7665 +
    let dst = nextReg(self);
7666 +
    emit(self, il::Instr::DeviceRead { typ, dst, handle, offset });
7667 +
    return il::Val::Reg(dst);
7668 +
}
lib/std/lang/module.rad +90 -53
12 12
use std::lang::alloc;
13 13
use std::lang::ast;
14 14
use std::lang::strings;
15 15
16 16
/// Maximum number of modules tracked in a single compilation graph.
17 -
export constant MAX_MODULES: u32 = 128;
17 +
export constant MAX_MODULES: u32 = 192;
18 18
/// Maximum number of characters for a module path.
19 19
constant MAX_PATH_LEN: u32 = 256;
20 20
/// Maximum number of components that make up a logical module path.
21 21
constant MAX_MODULE_PATH_DEPTH: u32 = 16;
22 22
/// Filesystem separator used when constructing child paths.
70 70
    name: *[u8],
71 71
    /// Logical path from the root module to this module.
72 72
    path: [*[u8]; MAX_MODULE_PATH_DEPTH],
73 73
    /// Number of segments inside `path`.
74 74
    pathDepth: u32,
75 +
    /// Mutable module state shared by all entry views.
76 +
    updates: *cell ModuleUpdates,
77 +
}
78 +
79 +
/// Copyable state stored in a module entry's cell.
80 +
export record ModuleUpdates: Copy {
75 81
    /// Current lifecycle state.
76 82
    state: ModuleState,
77 83
    /// Child module identifiers declared directly inside this module.
78 84
    children: [u16; MAX_MODULES],
79 85
    /// Number of entries stored in `children`.
84 90
    source: ?*[u8],
85 91
}
86 92
87 93
/// Dense storage for all modules referenced by the compilation unit.
88 94
export record ModuleGraph {
89 -
    /// Permanent storage for module entries.
90 -
    entries: *mut [ModuleEntry],
95 +
    /// Entry identities indexed by module identifier.
96 +
    entries: *mut [?*ModuleEntry],
91 97
    /// Number of initialized entries.
92 98
    entriesLen: u32,
93 -
    /// AST arena. It must outlive the graph.
94 -
    arena: ?*unsafe ast::NodeArena,
99 +
    /// Arena for AST nodes and stable entry allocations.
100 +
    arena: ?*unsafe mut ast::NodeArena,
95 101
}
96 102
97 103
/// Initialize an empty module graph backed by the provided storage.
98 -
/// The AST arena must outlive the returned graph.
104 +
/// The AST arena must outlive the graph and all retained entry views.
105 +
/// Entry allocation must occur outside speculative parser allocations.
99 106
export unsafe fn moduleGraph(
100 -
    storage: *mut [ModuleEntry],
107 +
    storage: *mut [?*ModuleEntry],
101 108
    arena: &mut ast::NodeArena
102 109
) -> ModuleGraph {
110 +
    for i in 0..storage.len {
111 +
        set storage[i] = nil;
112 +
    }
103 113
    return ModuleGraph {
104 114
        entries: storage,
105 115
        entriesLen: 0,
106 -
        arena: arena as *unsafe ast::NodeArena,
116 +
        arena: arena as *unsafe mut ast::NodeArena,
107 117
    };
108 118
}
109 119
110 120
/// Register a root module residing at `path` for a package.
111 -
export fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
121 +
export unsafe fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
112 122
    let name = try basenameSlice(filePath);
113 123
    return try registerRootWithName(graph, pool, packageId, name, filePath);
114 124
}
115 125
116 126
/// Register a root module with an explicit name and file path for a package.
117 -
export fn registerRootWithName(
127 +
export unsafe fn registerRootWithName(
118 128
    graph: &mut ModuleGraph, pool: &mut strings::Pool,
119 129
    packageId: u16,
120 130
    name: *[u8],
121 131
    filePath: *[u8]
122 132
) -> u16 throws (ModuleError) {
123 133
    let m = try allocModule(graph, pool, packageId, name, filePath);
124 134
    try appendPathSegment(m, name);
125 135
126 -
    set m.state = ModuleState::Registered;
127 -
128 -
    return m.id;
136 +
    let id = m.id;
137 +
    publish(graph, m);
138 +
    return id;
129 139
}
130 140
131 141
/// Register a child module.
132 142
/// Returns the module identifier.
133 -
export fn registerChild(
143 +
export unsafe fn registerChild(
134 144
    graph: &mut ModuleGraph, pool: &mut strings::Pool,
135 145
    parentId: u16,
136 146
    name: *[u8],
137 147
    filePath: *[u8]
138 148
) -> u16 throws (ModuleError) {
139 149
    assert name.len > 0, "registerChild: name must not be empty";
140 150
    assert filePath.len > 0, "registerChild: file path must not be empty";
141 151
142 -
    let parent = getMut(graph, parentId)
152 +
    let parent = get(graph, parentId)
143 153
        else throw ModuleError::NotFound(parentId);
144 154
145 155
    // If the child already exists under this parent, return it.
146 156
    if let child = findChild(graph, name, parentId) {
147 157
        return child.id;
148 158
    }
149 159
    // Inherit packageId from parent.
150 160
    let m = try allocModule(graph, pool, parent.packageId, name, filePath);
151 161
152 -
    set m.state = ModuleState::Registered;
153 162
    set m.parent = parentId;
154 163
155 164
    // Inherit path prefix from parent.
156 165
    for p in moduleQualifiedPath(parent) {
157 166
        try appendPathSegment(m, p);
158 167
    }
159 168
    try appendPathSegment(m, name);
160 169
161 -
    return try addChild(parent, m.id);
170 +
    let id = try addChild(parent, m.id);
171 +
    publish(graph, m);
172 +
    return id;
162 173
}
163 174
164 175
/// Fetch a read-only view of the module identified by `id`.
165 176
export fn get(graph: &ModuleGraph, id: u16) -> ?*ModuleEntry {
166 177
    if not isValidId(graph, id) {
167 178
        return nil;
168 179
    }
169 -
    return &graph.entries[id as u32];
170 -
}
171 -
172 -
/// Access a mutable entry by identifier.
173 -
fn getMut(graph: &mut ModuleGraph, id: u16) -> ?*mut ModuleEntry {
174 -
    if not isValidId(graph, id) {
175 -
        return nil;
176 -
    }
177 -
    return &mut graph.entries[id as u32];
180 +
    return graph.entries[id as u32];
178 181
}
179 182
180 183
/// Return the identifier of the child stored at `index`.
181 184
export fn childAt(m: *ModuleEntry, index: u32) -> u16 {
182 -
    assert index < m.childrenLen, "childAt: index must be valid";
183 -
    return m.children[index];
185 +
    let updates = *m.updates;
186 +
    assert index < updates.childrenLen, "childAt: index must be valid";
187 +
    return updates.children[index];
184 188
}
185 189
186 190
/// Public accessor for a module's directory prefix.
187 191
export fn moduleDir(m: *ModuleEntry) -> *[u8] {
188 192
    return &m.filePath[..m.dirLen];
197 201
/// Retrieve the lifecycle state for `id`.
198 202
export fn state(graph: &ModuleGraph, id: u16) -> ModuleState throws (ModuleError) {
199 203
    let m = get(graph, id) else {
200 204
        throw ModuleError::NotFound(id);
201 205
    };
202 -
    return m.state;
206 +
    return (*m.updates).state;
203 207
}
204 208
205 209
/// Record the parsed AST root for `id`.
206 210
export fn setAst(graph: &mut ModuleGraph, id: u16, root: *ast::Node) throws (ModuleError) {
207 -
    let m = getMut(graph, id) else throw ModuleError::NotFound(id);
208 -
    set m.ast = root;
209 -
    set m.state = ModuleState::Parsed;
211 +
    let m = get(graph, id) else throw ModuleError::NotFound(id);
212 +
    let mut updates = *m.updates;
213 +
    set updates.ast = root;
214 +
    set updates.state = ModuleState::Parsed;
215 +
    set *m.updates = updates;
210 216
}
211 217
212 218
/// Set the source text for a module.
213 219
export fn setSource(graph: &mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) {
214 -
    let m = getMut(graph, id) else throw ModuleError::NotFound(id);
215 -
    set m.source = source;
220 +
    let m = get(graph, id) else throw ModuleError::NotFound(id);
221 +
    let mut updates = *m.updates;
222 +
    set updates.source = source;
223 +
    set *m.updates = updates;
216 224
}
217 225
218 226
/// Look up a child module by name under the given parent.
219 227
export fn findChild(graph: &ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry {
220 228
    assert isValidId(graph, parentId), "findChild: parent identifier is valid";
221 229
222 -
    let parent = &graph.entries[parentId as u32];
223 -
    for i in 0..parent.childrenLen {
224 -
        let childId = parent.children[i];
225 -
        let child = &graph.entries[childId as u32];
230 +
    let parent = get(graph, parentId) else panic;
231 +
    let updates = *parent.updates;
232 +
    for i in 0..updates.childrenLen {
233 +
        let childId = updates.children[i];
234 +
        let child = get(graph, childId) else panic;
226 235
        if mem::eq(child.name, name) {
227 236
            return child;
228 237
        }
229 238
    }
230 239
    return nil;
266 275
267 276
/// Register a module from a file path, creating the full hierarchy as needed.
268 277
/// The path is split into components and the module hierarchy is built accordingly.
269 278
/// If `rootId` is `nil`, registers a new root for the given package.
270 279
/// Returns the module ID of the last component.
271 -
export fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
280 +
export unsafe fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
272 281
    let root = rootId else {
273 282
        return try registerRoot(graph, pool, packageId, filePath);
274 283
    };
275 284
    let rootEntry = get(graph, root) else {
276 285
        panic "registerFromPath: root is missing from storage";
311 320
    }
312 321
    return try registerChild(graph, pool, parentId, childName, filePath);
313 322
}
314 323
315 324
/// Allocate a fresh entry in the graph.
316 -
fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
325 +
unsafe fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
317 326
    if graph.entriesLen >= graph.entries.len {
318 327
        throw ModuleError::CapacityExceeded;
319 328
    }
320 329
    let idx = graph.entriesLen;
321 -
    set graph.entriesLen += 1;
330 +
    let arena = graph.arena else panic;
331 +
    let state = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleUpdates), @alignOf(ModuleUpdates)) as *mut ModuleUpdates;
332 +
    set *state = ModuleUpdates {
333 +
        state: ModuleState::Registered,
334 +
        children: [0; MAX_MODULES],
335 +
        childrenLen: 0,
336 +
        ast: nil,
337 +
        source: nil,
338 +
    };
339 +
    let updates = state as *cell ModuleUpdates;
322 340
323 341
    // TODO: This is a common pattern that needs better syntax.
324 -
    let m = &mut graph.entries[idx];
342 +
    let m = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleEntry), @alignOf(ModuleEntry)) as *mut ModuleEntry;
325 343
    set *m = ModuleEntry {
326 344
        id: idx as u16,
327 345
        packageId,
328 346
        parent: nil,
329 347
        filePath,
330 -
        dirLen: 0,
348 +
        dirLen: dirLength(filePath),
331 349
        name: strings::intern(pool, name),
332 350
        path: [""; MAX_MODULE_PATH_DEPTH],
333 351
        pathDepth: 0,
334 -
        state: ModuleState::Vacant,
335 -
        children: [0; MAX_MODULES],
336 -
        childrenLen: 0,
337 -
        ast: nil,
338 -
        source: nil,
352 +
        updates,
339 353
    };
340 -
    set m.dirLen = dirLength(m.filePath);
341 354
342 355
    return m;
343 356
}
344 357
345 358
/// Append a logical path segment (module identifier) to the entry.
350 363
    set entry.path[entry.pathDepth] = segment;
351 364
    set entry.pathDepth += 1;
352 365
}
353 366
354 367
/// Append a child identifier to the parent's child list.
355 -
fn addChild(parent: &mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) {
356 -
    if parent.childrenLen >= parent.children.len {
368 +
fn addChild(parent: *ModuleEntry, childId: u16) -> u16 throws (ModuleError) {
369 +
    let mut updates = *parent.updates;
370 +
    if updates.childrenLen >= updates.children.len {
357 371
        throw ModuleError::CapacityExceeded;
358 372
    }
359 -
    set parent.children[parent.childrenLen] = childId;
360 -
    set parent.childrenLen += 1;
373 +
    set updates.children[updates.childrenLen] = childId;
374 +
    set updates.childrenLen += 1;
375 +
    set *parent.updates = updates;
361 376
362 377
    return childId;
363 378
}
364 379
380 +
/// Publish a fully initialized module entry in identifier order.
381 +
fn publish(graph: &mut ModuleGraph, entry: *ModuleEntry) {
382 +
    assert entry.id as u32 == graph.entriesLen;
383 +
    set graph.entries[graph.entriesLen] = entry;
384 +
    set graph.entriesLen += 1;
385 +
}
386 +
387 +
/// Return the number of registered children.
388 +
export fn childCount(entry: *ModuleEntry) -> u32 {
389 +
    return (*entry.updates).childrenLen;
390 +
}
391 +
392 +
/// Return the current parsed root for an entry.
393 +
export fn astFor(entry: *ModuleEntry) -> ?*ast::Node {
394 +
    return (*entry.updates).ast;
395 +
}
396 +
397 +
/// Return the current source text for an entry.
398 +
export fn sourceFor(entry: *ModuleEntry) -> ?*[u8] {
399 +
    return (*entry.updates).source;
400 +
}
401 +
365 402
/// Check if `id` points at an allocated entry.
366 403
fn isValidId(graph: &ModuleGraph, id: u16) -> bool {
367 404
    return (id as u32) < graph.entriesLen;
368 405
}
369 406
lib/std/lang/module/printer.rad +7 -6
42 42
        for seg, i in path { set buf[i] = sexpr::sym(seg); }
43 43
        set pathBuf = buf;
44 44
    }
45 45
46 46
    let mut childBuf: *[sexpr::Expr] = &[];
47 -
    if entry.childrenLen > 0 {
48 -
        let buf = try! sexpr::allocExprs(a, entry.childrenLen);
49 -
        for i in 0..entry.childrenLen {
50 -
            if let child = super::get(graph, entry.children[i]) {
47 +
    let childCount = super::childCount(entry);
48 +
    if childCount > 0 {
49 +
        let buf = try! sexpr::allocExprs(a, childCount);
50 +
        for i in 0..childCount {
51 +
            if let child = super::get(graph, super::childAt(entry, i)) {
51 52
                set buf[i] = subtreeToExpr(a, graph, child);
52 53
            }
53 54
        }
54 55
        set childBuf = buf;
55 56
    }
56 57
57 58
    return sexpr::block(a, "module", &[
58 59
        sexpr::sym(entry.name),
59 60
        sexpr::list(a, "id", &[sexpr::sym(idText)]),
60 -
        sexpr::list(a, "state", &[stateToExpr(entry.state)]),
61 +
        sexpr::list(a, "state", &[stateToExpr((*entry.updates).state)]),
61 62
        sexpr::Expr::Str(entry.filePath),
62 63
        sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false }
63 64
    ], childBuf);
64 65
}
65 66
66 67
/// Print the entire module graph in S-expression format.
67 68
export unsafe fn printGraph(graph: &super::ModuleGraph, arena: &mut alloc::Arena) {
68 69
    // Print all root modules.
69 70
    for i in 0..graph.entriesLen {
70 -
        let entry = &graph.entries[i];
71 +
        let entry = super::get(graph, i as u16) else panic;
71 72
        if entry.parent == nil {
72 73
            sexpr::print(subtreeToExpr(arena, graph, entry), 0);
73 74
            io::print("\n");
74 75
        }
75 76
    }
lib/std/lang/module/tests.rad +58 -13
1 1
//! Tests for the module loader.
2 2
3 3
use std::mem;
4 4
use std::testing;
5 5
use std::lang::ast;
6 +
use std::lang::alloc;
7 +
use std::lang::parser;
8 +
use std::lang::scanner;
6 9
use std::lang::strings;
7 10
8 11
/// Test arena backing storage.
9 -
static TEST_ARENA: [u8; 4096] = [0; 4096];
12 +
static TEST_ARENA: [u8; 16384] = [0; 16384];
10 13
/// Interned string pool.
11 14
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
12 15
13 16
fn expectSliceEq(actual: *[u8], expected: *[u8])
14 17
    throws (testing::TestError)
27 30
        try expectSliceEq(actual[i], expected[i]);
28 31
    }
29 32
}
30 33
31 34
@test unsafe fn testRegisterChildren() throws (testing::TestError) {
32 -
    static storage: [super::ModuleEntry; 4] = undefined;
35 +
    static storage: [?*super::ModuleEntry; 4] = [nil; 4];
33 36
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
34 37
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
35 38
36 39
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch {
37 40
        throw testing::TestError::Failed;
65 68
66 69
    let parent = super::get(&graph, rootId) else {
67 70
        throw testing::TestError::Failed;
68 71
    };
69 72
    try testing::expect(graph.entriesLen == 3);
70 -
    try testing::expect(parent.childrenLen == 2);
73 +
    try testing::expect(super::childCount(parent) == 2);
71 74
    try testing::expect(super::childAt(parent, 0) == firstId);
72 75
    try testing::expect(super::childAt(parent, 1) == secondId);
73 76
}
74 77
75 78
@test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) {
76 -
    static storage: [super::ModuleEntry; 4] = undefined;
79 +
    static storage: [?*super::ModuleEntry; 4] = [nil; 4];
77 80
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
78 81
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
79 82
80 83
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch {
81 84
        throw testing::TestError::Failed;
90 93
91 94
    let parent = super::get(&graph, rootId) else {
92 95
        throw testing::TestError::Failed;
93 96
    };
94 97
    try testing::expect(graph.entriesLen == 2);
95 -
    try testing::expect(parent.childrenLen == 1);
98 +
    try testing::expect(super::childCount(parent) == 1);
96 99
}
97 100
98 101
@test fn testTrimExtensionWithRadExtension() throws (testing::TestError) {
99 102
    let input = "parser.rad";
100 103
    let result = super::trimExtension(input)
142 145
    let result = super::parsePath(path, &mut components[..]);
143 146
    try testing::expect(result == nil);
144 147
}
145 148
146 149
@test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) {
147 -
    static storage: [super::ModuleEntry; 8] = undefined;
150 +
    static storage: [?*super::ModuleEntry; 8] = [nil; 8];
148 151
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
149 152
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
150 153
151 154
    // Register root module.
152 155
    let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch {
183 186
    try expectSliceEq(super::moduleDir(parser), "lib/std/lang/");
184 187
    try expectPathSegments(parser, &["std", "lang", "parser"]);
185 188
186 189
    // Verify parent-child relationships.
187 190
    try testing::expect(graph.entriesLen == 3);
188 -
    try testing::expect(std.childrenLen == 1);
191 +
    try testing::expect(super::childCount(std) == 1);
189 192
    try testing::expect(super::childAt(std, 0) == langId);
190 -
    try testing::expect(lang.childrenLen == 1);
193 +
    try testing::expect(super::childCount(lang) == 1);
191 194
    try testing::expect(super::childAt(lang, 0) == parserId);
192 -
    try testing::expect(parser.childrenLen == 0);
195 +
    try testing::expect(super::childCount(parser) == 0);
193 196
}
194 197
195 198
@test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) {
196 -
    static storage: [super::ModuleEntry; 8] = undefined;
199 +
    static storage: [?*super::ModuleEntry; 8] = [nil; 8];
197 200
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
198 201
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
199 202
200 203
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch {
201 204
        throw testing::TestError::Failed;
206 209
    };
207 210
    throw testing::TestError::Failed;
208 211
}
209 212
210 213
@test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) {
211 -
    static storage: [super::ModuleEntry; 8] = undefined;
214 +
    static storage: [?*super::ModuleEntry; 8] = [nil; 8];
212 215
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
213 216
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
214 217
215 218
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch {
216 219
        throw testing::TestError::Failed;
221 224
    };
222 225
    throw testing::TestError::Failed;
223 226
}
224 227
225 228
@test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) {
226 -
    static storage: [super::ModuleEntry; 8] = undefined;
229 +
    static storage: [?*super::ModuleEntry; 8] = [nil; 8];
227 230
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
228 231
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
229 232
230 233
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch {
231 234
        throw testing::TestError::Failed;
240 243
    try expectSliceEq(root.filePath, "lib/std.rad");
241 244
    try expectPathSegments(root, &["std"]);
242 245
}
243 246
244 247
@test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) {
245 -
    static storage: [super::ModuleEntry; 8] = undefined;
248 +
    static storage: [?*super::ModuleEntry; 8] = [nil; 8];
246 249
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
247 250
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
248 251
249 252
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch {
250 253
        throw testing::TestError::Failed;
268 271
        throw testing::TestError::Failed;
269 272
    };
270 273
    try expectSliceEq(parser.name, "parser");
271 274
    try expectPathSegments(parser, &["root", "lang", "parser"]);
272 275
}
276 +
/// Move the graph owner while its published entries retain their identities.
277 +
fn relocate(graph: super::ModuleGraph) -> super::ModuleGraph {
278 +
    return graph;
279 +
}
280 +
@test unsafe fn testGraphRelocation() throws (testing::TestError) {
281 +
    static DATA: [u8; 16384] = [0; 16384];
282 +
    static ENTRIES: [?*super::ModuleEntry; 2] = [nil; 2];
283 +
    static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
284 +
    let mut arena = ast::nodeArena(&mut DATA[..]);
285 +
    let mut original = super::moduleGraph(&mut ENTRIES[..], &mut arena);
286 +
    let rootId = try! super::registerRoot(&mut original, &mut POOL, 0, "root.rad");
287 +
    let root = super::get(&original, rootId) else panic;
288 +
    let alias = root;
289 +
    let mut graph = relocate(original);
290 +
    let child = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad");
291 +
    try testing::expect(super::childCount(root) == 1);
292 +
    try testing::expect(super::childAt(alias, 0) == child);
293 +
    let published = super::get(&graph, rootId) else panic;
294 +
    try testing::expect(published as u64 == root as u64);
295 +
    let saved = alloc::used(&arena.arena);
296 +
    let reused = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad");
297 +
    try testing::expect(reused == child);
298 +
    try testing::expect(alloc::used(&arena.arena) == saved);
299 +
    let mut failed = false;
300 +
    try super::registerChild(&mut graph, &mut POOL, rootId, "full", "root/full.rad") catch {
301 +
        set failed = true;
302 +
    };
303 +
    try testing::expect(failed);
304 +
    try testing::expect(graph.entriesLen == 2);
305 +
    try testing::expect(super::childCount(alias) == 1);
306 +
    try testing::expect(alloc::used(&arena.arena) == saved);
307 +
    let source = "fn answer() -> u32 { return 42; }";
308 +
    let parsed: *ast::Node = try! parser::parse(scanner::SourceLoc::String, source, &mut arena, &mut POOL);
309 +
    try! super::setAst(&mut graph, rootId, parsed);
310 +
    try! super::setSource(&mut graph, rootId, source);
311 +
    let retainedAst = super::astFor(alias) else panic;
312 +
    try testing::expect(retainedAst as u64 == parsed as u64);
313 +
    let retainedSource = super::sourceFor(root) else panic;
314 +
    try testing::expect(mem::eq(retainedSource, source));
315 +
    try testing::expect(try! super::state(&graph, rootId) == super::ModuleState::Parsed);
316 +
317 +
}
lib/std/lang/package.rad +1 -1
31 31
    set pkg.rootModuleId = nil;
32 32
33 33
}
34 34
35 35
/// Register a module described by the file path.
36 -
export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16
36 +
export unsafe fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16
37 37
    throws (module::ModuleError)
38 38
{
39 39
    let modId = try module::registerFromPath(graph, pool, pkg.id, pkg.rootModuleId, filePath);
40 40
    // First registered module becomes the root.
41 41
    if pkg.rootModuleId == nil {
lib/std/lang/parser.rad +246 -25
3 3
4 4
use std::mem;
5 5
use std::io;
6 6
use std::fmt;
7 7
use std::lang::alloc;
8 -
use std::lang::types;
9 8
use std::lang::ast;
10 9
use std::lang::strings;
11 10
use std::lang::scanner;
12 11
13 12
/// Maximum `u32` value.
85 84
    count: u32,
86 85
}
87 86
88 87
/// Snapshot of parser state for speculative parsing.
89 88
record SavedState: Copy {
89 +
    /// Scanner position, tokens, diagnostics, and expression context.
90 90
    parser: Parser,
91 +
    /// First byte available for tentative allocations.
91 92
    arena: u32,
93 +
    /// First node identifier available to the tentative parse.
92 94
    nextId: u32,
93 95
}
94 96
95 97
/// Operator metadata for precedence climbing.
96 98
record OpInfo: Copy {
383 385
    loop {
384 386
        match p.current.kind {
385 387
            case scanner::TokenKind::Dot => {
386 388
                advance(p);
387 389
388 -
                let field = try parseIdent(p, "expected field name after `.`");
390 +
                let mut field: *ast::Node = undefined;
391 +
                if consume(p, scanner::TokenKind::Set) {
392 +
                    set field = node(p, ast::NodeValue::Ident(p.previous.source));
393 +
                } else {
394 +
                    set field = try parseIdent(p, "expected field name after `.`");
395 +
                }
389 396
                set result = node(p, ast::NodeValue::FieldAccess(
390 397
                    ast::Access { parent: result, child: field }
391 398
                ));
392 399
            }
393 400
            case scanner::TokenKind::ColonColon => {
396 403
                let ident = try parseIdent(p, "expected identifier after `::`");
397 404
                set result = node(p, ast::NodeValue::ScopeAccess(
398 405
                    ast::Access { parent: result, child: ident }
399 406
                ));
400 407
            }
408 +
            case scanner::TokenKind::Region => {
409 +
                let regions = try parseRegions(p);
410 +
                set result = node(p, ast::NodeValue::RegionApply { value: result, regions });
411 +
            }
401 412
            case scanner::TokenKind::LBracket => {
402 413
                set result = try parseSubscriptOrSlice(p, result);
403 414
            }
404 415
            case scanner::TokenKind::LParen => {
405 416
                set result = try parseCall(p, result);
569 580
             ast::NodeValue::WhileLet(_),
570 581
             ast::NodeValue::For(_),
571 582
             ast::NodeValue::Loop { .. },
572 583
             ast::NodeValue::Match(_),
573 584
             ast::NodeValue::Block(_),
585 +
             ast::NodeValue::RegionBlock { .. },
574 586
             ast::NodeValue::FnDecl(_),
575 587
             ast::NodeValue::RecordDecl(_),
576 588
             ast::NodeValue::UnionDecl(_),
577 589
             ast::NodeValue::TraitDecl { .. },
578 590
             ast::NodeValue::InstanceDecl { .. },
906 918
        }
907 919
        case scanner::TokenKind::Match => {
908 920
            return try parseMatch(p);
909 921
        }
910 922
        case scanner::TokenKind::Let => {
923 +
            if isRegionBlock(p) {
924 +
                return try parseRegionBlock(p);
925 +
            }
911 926
            advance(p);
912 927
            if consume(p, scanner::TokenKind::Case) {
913 928
                return try parseLetCase(p);
914 929
            }
915 930
            if consume(p, scanner::TokenKind::Mut) {
935 950
        }
936 951
        case scanner::TokenKind::Record => {
937 952
            return try parseRecordDecl(p, attrs);
938 953
        }
939 954
        case scanner::TokenKind::Use => {
955 +
            if isSessionBlock(p) {
956 +
                return try parseSessionBlock(p);
957 +
            }
940 958
            return try parseUse(p, attrs);
941 959
        }
942 960
        case scanner::TokenKind::Mod => {
943 961
            return try parseMod(p, attrs);
944 962
        }
952 970
            return try parseExprStmt(p);
953 971
        }
954 972
    }
955 973
}
956 974
975 +
/// Return whether the current `let` statement starts a regional block.
976 +
unsafe fn isRegionBlock(p: &Parser) -> bool {
977 +
    let mut lookahead = p.scanner;
978 +
    return scanner::next(&mut lookahead).kind == scanner::TokenKind::Ident
979 +
        and scanner::next(&mut lookahead).kind == scanner::TokenKind::Colon
980 +
        and scanner::next(&mut lookahead).kind == scanner::TokenKind::Region;
981 +
}
982 +
983 +
/// Return whether the current `use` statement has an allocation-session header.
984 +
unsafe fn isSessionBlock(p: &mut Parser) -> bool {
985 +
    let saved = saveState(p);
986 +
    advance(p);
987 +
    let source: ?*ast::Node = try? parseUnaryExpr(p);
988 +
    let result = source <> nil
989 +
        and consume(p, scanner::TokenKind::As)
990 +
        and consume(p, scanner::TokenKind::Ident)
991 +
        and check(p, scanner::TokenKind::In);
992 +
    restoreState(p, &saved);
993 +
    return result;
994 +
}
995 +
957 996
/// Parse statements until the specified ending token is encountered.
958 997
///
959 998
/// Returns the completed immutable statement list.
960 999
export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, capacity: u32) -> *[*ast::Node]
961 1000
    throws (ParseError)
1056 1095
        arena: alloc::save(&p.arena.arena),
1057 1096
        nextId: p.arena.nextId,
1058 1097
    };
1059 1098
}
1060 1099
1061 -
/// Restore parser state from a snapshot, fully undoing any
1062 -
/// side effects of a failed speculative parse.
1100 +
/// Restore scanner state, diagnostics, arena storage, and node identifiers.
1101 +
/// Tentative nodes must be unreachable from all state retained by the caller.
1102 +
/// Interned tokens retain source storage, which must outlive the string pool.
1063 1103
unsafe fn restoreState(p: &mut Parser, s: &SavedState) {
1064 1104
    set *p = s.parser;
1065 1105
    alloc::restore(&mut p.arena.arena, s.arena);
1066 1106
    set p.arena.nextId = s.nextId;
1067 1107
}
1537 1577
    set p.context = saved;
1538 1578
1539 1579
    return pattern;
1540 1580
}
1541 1581
1582 +
/// Parse a region name.
1583 +
unsafe fn parseRegion(p: &mut Parser) -> *ast::Node throws (ParseError) {
1584 +
    let name = try expect(p, scanner::TokenKind::Region, "expected region name");
1585 +
    return node(p, ast::NodeValue::Region { name, parent: nil });
1586 +
}
1587 +
1588 +
/// Parse consecutive region names.
1589 +
unsafe fn parseRegions(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1590 +
    let mut regions = ast::nodeSlice(p.arena, 4);
1591 +
    while check(p, scanner::TokenKind::Region) {
1592 +
        regions.append(try parseRegion(p), p.allocator);
1593 +
    }
1594 +
    return regions;
1595 +
}
1596 +
1597 +
/// Parse region bounds for a declaration.
1598 +
unsafe fn parseRegionBounds(p: &mut Parser, regions: &mut [*ast::Node])
1599 +
    throws (ParseError)
1600 +
{
1601 +
    if not consume(p, scanner::TokenKind::Where) {
1602 +
        return;
1603 +
    }
1604 +
    loop {
1605 +
        let parent = try parseRegion(p);
1606 +
        try expect(p, scanner::TokenKind::Colon, "expected `:` in region bound");
1607 +
        let childName = try expect(p, scanner::TokenKind::Region, "expected region name");
1608 +
        let mut matched = false;
1609 +
        for i in 0..regions.len {
1610 +
            let region = regions[i];
1611 +
            if let case ast::NodeValue::Region { name, parent: declaredParent } = region.value;
1612 +
                mem::eq(name, childName)
1613 +
            {
1614 +
                if declaredParent <> nil {
1615 +
                    throw failParsing(p, "region parameter already has a bound");
1616 +
                }
1617 +
                set regions[i] = node(p, ast::NodeValue::Region { name, parent });
1618 +
                set matched = true;
1619 +
                break;
1620 +
            }
1621 +
        }
1622 +
        if not matched {
1623 +
            throw failParsing(p, "expected declared region parameter in bound");
1624 +
        }
1625 +
        if not consume(p, scanner::TokenKind::Comma) {
1626 +
            break;
1627 +
        }
1628 +
    }
1629 +
}
1630 +
1631 +
/// Parse scoped borrow bindings.
1632 +
unsafe fn parseRegionBlock(p: &mut Parser) -> *ast::Node throws (ParseError) {
1633 +
    advance(p);
1634 +
    let mut binding = try parseIdent(p, "expected region binding name");
1635 +
    try expect(p, scanner::TokenKind::Colon, "expected `:` after region binding");
1636 +
    let regionName = try expect(p, scanner::TokenKind::Region, "expected region name after `:`");
1637 +
    let mut region = node(p, ast::NodeValue::Region { name: regionName, parent: nil });
1638 +
    let mut bindings = ast::nodeSlice(p.arena, 4);
1639 +
    loop {
1640 +
        try expect(p, scanner::TokenKind::Equal, "expected `=` after region binding");
1641 +
        try expect(p, scanner::TokenKind::Amp, "expected `&` before borrowed place");
1642 +
        let mutable = consume(p, scanner::TokenKind::Mut);
1643 +
        let target = try parseUnaryExpr(p);
1644 +
        let value = node(p, ast::NodeValue::AddressOf({ target, mutable }));
1645 +
        bindings.append(node(p, ast::NodeValue::RegionBinding({ label: binding, value })), p.allocator);
1646 +
        if not consume(p, scanner::TokenKind::Comma) {
1647 +
            break;
1648 +
        }
1649 +
        set binding = try parseIdent(p, "expected region binding name");
1650 +
    }
1651 +
    if consume(p, scanner::TokenKind::Where) {
1652 +
        let parent = try parseRegion(p);
1653 +
        try expect(p, scanner::TokenKind::Colon, "expected `:` in region bound");
1654 +
        let childName = try expect(p, scanner::TokenKind::Region, "expected child region in bound");
1655 +
        if not mem::eq(regionName, childName) {
1656 +
            throw failParsing(p, "region bound must name the declared region");
1657 +
        }
1658 +
        set region = node(p, ast::NodeValue::Region { name: regionName, parent });
1659 +
    }
1660 +
    try expect(p, scanner::TokenKind::In, "expected `in` after region bindings");
1661 +
    let body = try parseBlock(p);
1662 +
    return node(p, ast::NodeValue::RegionBlock { region, bindings, body, isSession: false });
1663 +
}
1664 +
1665 +
/// Create a region name from an allocation-session binding name.
1666 +
unsafe fn sessionRegion(p: &mut Parser, binding: *ast::Node) -> *ast::Node {
1667 +
    let case ast::NodeValue::Ident(name) = binding.value
1668 +
        else panic "sessionRegion: invalid binding";
1669 +
    let len = name.len + 1;
1670 +
    let buf = alloc::remainingBuf(&mut p.arena.arena);
1671 +
    assert buf.len >= len, "sessionRegion: node arena is full";
1672 +
    set buf[0] = 39;
1673 +
    try! mem::copy(&mut buf[1..len], name);
1674 +
    alloc::commit(&mut p.arena.arena, len);
1675 +
    return node(p, ast::NodeValue::Region { name: &buf[..len], parent: nil });
1676 +
}
1677 +
1678 +
/// Parse an allocation session with an implicit exclusive source borrow.
1679 +
unsafe fn parseSessionBlock(p: &mut Parser) -> *ast::Node throws (ParseError) {
1680 +
    try expect(p, scanner::TokenKind::Use, "expected `use`");
1681 +
    let target = try parseUnaryExpr(p);
1682 +
    let value = node(p, ast::NodeValue::AddressOf({ target, mutable: true }));
1683 +
    try expect(p, scanner::TokenKind::As, "expected `as` after allocation source");
1684 +
    let binding = try parseIdent(p, "expected allocation binding after `as`");
1685 +
    let region = sessionRegion(p, binding);
1686 +
    try expect(p, scanner::TokenKind::In, "expected `in` after allocation binding");
1687 +
    let bindings = ast::nodeSlice(p.arena, 1).append(
1688 +
        node(p, ast::NodeValue::RegionBinding({ label: binding, value })),
1689 +
        p.allocator,
1690 +
    );
1691 +
    let body = try parseBlock(p);
1692 +
    return node(p, ast::NodeValue::RegionBlock { region, bindings, body, isSession: true });
1693 +
}
1694 +
1542 1695
/// Parse an identifier.
1543 1696
unsafe fn parseIdent(p: &mut Parser, err: *[u8]) -> *ast::Node
1544 1697
    throws (ParseError)
1545 1698
{
1546 1699
    let source = try expect(p, scanner::TokenKind::Ident, err);
1618 1771
    try expect(p, terminator, "expected closing delimiter after record fields");
1619 1772
1620 1773
    return fields;
1621 1774
}
1622 1775
1623 -
/// Parse an optional derives list (`: Trait + Trait`).
1624 -
unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1625 -
    let mut derives = ast::nodeSlice(p.arena, 4);
1626 -
1776 +
/// Parse an optional declaration list and separate regions from derives.
1777 +
unsafe fn parseNominalClauses(
1778 +
    p: &mut Parser,
1779 +
    regions: &mut *mut [*ast::Node],
1780 +
    derives: &mut *mut [*ast::Node],
1781 +
) throws (ParseError) {
1627 1782
    if not consume(p, scanner::TokenKind::Colon) {
1628 -
        return derives;
1783 +
        return;
1629 1784
    }
1785 +
    set *regions = ast::nodeSlice(p.arena, 4);
1786 +
    set *derives = ast::nodeSlice(p.arena, 4);
1630 1787
    loop {
1631 -
        let t = try parseIdent(p, "expected trait name in derive list");
1632 -
        derives.append(t, p.allocator);
1788 +
        if check(p, scanner::TokenKind::Region) {
1789 +
            regions.append(try parseRegion(p), p.allocator);
1790 +
        } else {
1791 +
            derives.append(
1792 +
                try parseIdent(p, "expected region or trait name in declaration list"),
1793 +
                p.allocator,
1794 +
            );
1795 +
        }
1633 1796
1634 1797
        if not consume(p, scanner::TokenKind::Plus) {
1635 1798
            break;
1636 1799
        }
1637 1800
    }
1801 +
}
1802 +
1803 +
/// Parse an optional list of trait names.
1804 +
unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1805 +
    if not consume(p, scanner::TokenKind::Colon) {
1806 +
        return &mut [];
1807 +
    }
1808 +
    let mut derives = ast::nodeSlice(p.arena, 4);
1809 +
    loop {
1810 +
        derives.append(try parseIdent(p, "expected trait name"), p.allocator);
1811 +
        if not consume(p, scanner::TokenKind::Plus) {
1812 +
            break;
1813 +
        }
1814 +
    }
1638 1815
    return derives;
1639 1816
}
1640 1817
1641 1818
/// Parse a single record literal field.
1642 1819
/// Can be either labeled, or shorthand.
1693 1870
    throws (ParseError)
1694 1871
{
1695 1872
    try expect(p, scanner::TokenKind::Record, "expected `record`");
1696 1873
1697 1874
    let name = try parseIdent(p, "expected record name");
1698 -
    let derives = try parseDerives(p);
1875 +
    let mut regions: *mut [*ast::Node] = &mut [];
1876 +
    let mut derives: *mut [*ast::Node] = &mut [];
1877 +
    try parseNominalClauses(p, &mut regions, &mut derives);
1878 +
    try parseRegionBounds(p, &mut regions[..]);
1699 1879
1700 1880
    if consume(p, scanner::TokenKind::LParen) {
1701 1881
        let fields = try parseRecordFields(p, RecordFieldMode::Unlabeled);
1702 1882
        try expect(p, scanner::TokenKind::Semicolon, "expected `;` after record");
1703 1883
        return node(p, ast::NodeValue::RecordDecl(
1704 -
            ast::RecordDecl { name, fields, attrs, derives, labeled: false }
1884 +
            ast::RecordDecl {
1885 +
                name, fields, attrs, regions, derives, labeled: false,
1886 +
            }
1705 1887
        ));
1706 1888
    } else {
1707 1889
        try expect(p, scanner::TokenKind::LBrace, "expected `{` before record body");
1708 1890
        let fields = try parseRecordFields(p, RecordFieldMode::Labeled);
1709 1891
        return node(p, ast::NodeValue::RecordDecl(
1710 -
            ast::RecordDecl { name, fields, attrs, derives, labeled: true }
1892 +
            ast::RecordDecl {
1893 +
                name, fields, attrs, regions, derives, labeled: true,
1894 +
            }
1711 1895
        ));
1712 1896
    }
1713 1897
}
1714 1898
1715 1899
/// Parse a union declaration.
1718 1902
    throws (ParseError)
1719 1903
{
1720 1904
    try expect(p, scanner::TokenKind::Union, "expected `union`");
1721 1905
1722 1906
    let name = try parseIdent(p, "expected union name");
1723 -
    let derives = try parseDerives(p);
1907 +
    let mut regions: *mut [*ast::Node] = &mut [];
1908 +
    let mut derives: *mut [*ast::Node] = &mut [];
1909 +
    try parseNominalClauses(p, &mut regions, &mut derives);
1910 +
    try parseRegionBounds(p, &mut regions[..]);
1724 1911
1725 1912
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body");
1726 1913
1727 1914
    let mut variants = ast::nodeSlice(p.arena, 128);
1728 1915
    while not check(p, scanner::TokenKind::RBrace) {
1763 1950
        }
1764 1951
    }
1765 1952
    try expect(p, scanner::TokenKind::RBrace, "expected `}`");
1766 1953
1767 1954
    return node(p, ast::NodeValue::UnionDecl(
1768 -
        ast::UnionDecl { name, variants, attrs, derives }
1955 +
        ast::UnionDecl {
1956 +
            name, variants, attrs, regions, derives,
1957 +
        }
1769 1958
    ));
1770 1959
}
1771 1960
1772 1961
/// Parse a function parameter.
1773 1962
unsafe fn parseFnParam(p: &mut Parser) -> *ast::Node
1864 2053
    // Method syntax: `fn (recv: *Type) name(params) { body }`.
1865 2054
    if check(p, scanner::TokenKind::LParen) {
1866 2055
        return try parseMethodDecl(p, attrs);
1867 2056
    }
1868 2057
    let name = try parseIdent(p, "expected function name");
2058 +
    let regions = try parseRegions(p);
1869 2059
    let sig = try parseFnTypeSig(p);
2060 +
    try parseRegionBounds(p, &mut regions[..]);
1870 2061
    let mut body: ?*ast::Node = nil;
1871 2062
    let mut fnAttrs = attrs;
1872 2063
1873 2064
    if consume(p, scanner::TokenKind::Semicolon) {
1874 2065
        if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) {
1886 2077
        }
1887 2078
    } else {
1888 2079
        set body = try parseBlock(p);
1889 2080
    }
1890 2081
    return node(p, ast::NodeValue::FnDecl(
1891 -
        ast::FnDecl { name, sig, body, attrs: fnAttrs }
2082 +
        ast::FnDecl { name, regions, sig, body, attrs: fnAttrs }
1892 2083
    ));
1893 2084
}
1894 2085
1895 2086
/// Parse a pointer-like type after its ownership prefix.
1896 2087
unsafe fn parsePointerLikeType(
1897 2088
    p: &mut Parser,
1898 -
    class: types::PointerClass,
2089 +
    class: ast::PointerClass,
1899 2090
) -> *ast::Node throws (ParseError) {
2091 +
    if check(p, scanner::TokenKind::Ident) and mem::eq(p.current.source, "cell") {
2092 +
        advance(p);
2093 +
        let payload = try parseType(p);
2094 +
        return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Cell { class, payload }));
2095 +
    }
1900 2096
    let mutable = consume(p, scanner::TokenKind::Mut);
1901 2097
1902 2098
    if consume(p, scanner::TokenKind::LBracket) {
1903 2099
        let itemType = try parseType(p);
1904 2100
        try expect(p, scanner::TokenKind::RBracket, "expected `]` after slice element type");
1978 2174
                ast::TypeSig::Optional { valueType }
1979 2175
            ));
1980 2176
        }
1981 2177
        case scanner::TokenKind::Star => {
1982 2178
            advance(p);
1983 -
            let class = types::PointerClass::Unsafe
2179 +
            let class = ast::PointerClass::Unsafe
1984 2180
                if consume(p, scanner::TokenKind::Unsafe)
1985 -
                else types::PointerClass::Owned;
2181 +
                else ast::PointerClass::Owned;
1986 2182
            return try parsePointerLikeType(p, class);
1987 2183
        }
1988 2184
        case scanner::TokenKind::Amp => {
1989 2185
            advance(p);
1990 -
            return try parsePointerLikeType(p, types::PointerClass::Ref);
2186 +
            let mut region: ?*ast::Node = nil;
2187 +
            if check(p, scanner::TokenKind::Region) {
2188 +
                set region = try parseRegion(p);
2189 +
            }
2190 +
            let type = try parsePointerLikeType(p, ast::PointerClass::Ref);
2191 +
            if let r = region {
2192 +
                return node(p, ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { region: r, type }));
2193 +
            }
2194 +
            return type;
1991 2195
        }
1992 2196
        case scanner::TokenKind::LBracket => {
1993 2197
            return try parseArrayType(p);
1994 2198
        }
1995 2199
        case scanner::TokenKind::Super, scanner::TokenKind::Ident => {
1996 2200
            let path = try parseTypePath(p);
1997 -
2201 +
            if check(p, scanner::TokenKind::Region) {
2202 +
                let regions = try parseRegions(p);
2203 +
                return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Applied { name: path, regions }));
2204 +
            }
1998 2205
            return node(p, ast::NodeValue::TypeSig(
1999 2206
                ast::TypeSig::Nominal(path)
2000 2207
            ));
2001 2208
        }
2002 2209
        case scanner::TokenKind::U8 => {
2302 2509
    let receiver = try parseType(p);
2303 2510
2304 2511
    try expect(p, scanner::TokenKind::RParen, "expected `)` after receiver");
2305 2512
2306 2513
    let name = try parseIdent(p, "expected method name");
2514 +
    let mut regions = try parseRegions(p);
2307 2515
    let sig = try parseFnTypeSig(p);
2516 +
    try parseRegionBounds(p, &mut regions[..]);
2308 2517
    try expect(p, scanner::TokenKind::Semicolon, "expected `;` after method signature");
2309 2518
2310 -
    return node(p, ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs });
2519 +
    let modifiers = try! alloc::alloc(
2520 +
        &mut p.arena.arena, @sizeOf(ast::MethodModifiers), @alignOf(ast::MethodModifiers)
2521 +
    ) as *mut ast::MethodModifiers;
2522 +
    set *modifiers = ast::MethodModifiers { regions, attrs };
2523 +
    return node(p, ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig });
2311 2524
}
2312 2525
2313 2526
/// Parse an instance block.
2314 2527
/// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }`
2315 2528
///
2320 2533
{
2321 2534
    try expect(p, scanner::TokenKind::Instance, "expected `instance`");
2322 2535
    let traitName = try parseTypePath(p);
2323 2536
    try expect(p, scanner::TokenKind::For, "expected `for` after trait name");
2324 2537
    let targetType = try parseTypePath(p);
2538 +
    let mut regions = try parseRegions(p);
2539 +
    try parseRegionBounds(p, &mut regions[..]);
2325 2540
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type");
2326 2541
2327 2542
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2328 2543
2329 2544
    while not check(p, scanner::TokenKind::RBrace) and
2335 2550
2336 2551
        methods.append(method, p.allocator);
2337 2552
    }
2338 2553
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods");
2339 2554
2340 -
    return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods });
2555 +
    return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods });
2341 2556
}
2342 2557
2343 2558
/// Parse a method declaration with a receiver.
2344 2559
/// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }`
2345 2560
///
2355 2570
    let receiverType = try parseType(p);
2356 2571
2357 2572
    try expect(p, scanner::TokenKind::RParen, "expected `)` after receiver type");
2358 2573
2359 2574
    let name = try parseIdent(p, "expected method name");
2575 +
    let mut regions = try parseRegions(p);
2360 2576
    let sig = try parseFnTypeSig(p);
2577 +
    try parseRegionBounds(p, &mut regions[..]);
2361 2578
    let body = try parseBlock(p);
2362 2579
2580 +
    let modifiers = try! alloc::alloc(
2581 +
        &mut p.arena.arena, @sizeOf(ast::MethodModifiers), @alignOf(ast::MethodModifiers)
2582 +
    ) as *mut ast::MethodModifiers;
2583 +
    set *modifiers = ast::MethodModifiers { regions, attrs };
2363 2584
    return node(p, ast::NodeValue::MethodDecl {
2364 -
        name, receiverName, receiverType, sig, body, attrs,
2585 +
        name, modifiers, receiverName, receiverType, sig, body,
2365 2586
    });
2366 2587
}
2367 2588
2368 2589
/// Parse a comma-separated list enclosed by the given delimiters.
2369 2590
unsafe fn parseList(
lib/std/lang/parser/tests.rad +303 -17
2 2
3 3
use std::mem;
4 4
use std::fmt;
5 5
use std::testing;
6 6
use std::lang::ast;
7 -
use std::lang::types;
8 7
use std::lang::alloc;
9 8
use std::lang::sexpr;
10 9
use std::lang::ast::printer;
11 10
use std::lang::scanner;
12 11
use std::lang::strings;
790 789
}
791 790
792 791
/// Parse an `i32` pointer type and verify its class and mutability.
793 792
unsafe fn expectI32Pointer(
794 793
    source: *[u8],
795 -
    class: types::PointerClass,
794 +
    class: ast::PointerClass,
796 795
    mutable: bool,
797 796
) throws (testing::TestError) {
798 797
    let node = try! parseTypeStr(source);
799 798
    let case ast::NodeValue::TypeSig(sig) = node.value
800 799
        else throw testing::TestError::Failed;
807 806
    assert actualMutable == mutable;
808 807
}
809 808
810 809
/// Test parsing a mutable owned pointer.
811 810
@test unsafe fn testParseTypePointer() throws (testing::TestError) {
812 -
    try expectI32Pointer("*mut i32", types::PointerClass::Owned, true);
811 +
    try expectI32Pointer("*mut i32", ast::PointerClass::Owned, true);
813 812
}
814 813
815 814
/// Test parsing an immutable owned pointer.
816 815
@test unsafe fn testParseTypePointerImmutable() throws (testing::TestError) {
817 -
    try expectI32Pointer("*i32", types::PointerClass::Owned, false);
816 +
    try expectI32Pointer("*i32", ast::PointerClass::Owned, false);
818 817
}
819 818
820 819
/// Test parsing immutable and mutable references.
821 820
@test unsafe fn testParseTypeRef() throws (testing::TestError) {
822 -
    try expectI32Pointer("&i32", types::PointerClass::Ref, false);
823 -
    try expectI32Pointer("&mut i32", types::PointerClass::Ref, true);
821 +
    try expectI32Pointer("&i32", ast::PointerClass::Ref, false);
822 +
    try expectI32Pointer("&mut i32", ast::PointerClass::Ref, true);
824 823
}
825 824
826 825
/// Test parsing immutable and mutable unsafe pointers.
827 826
@test unsafe fn testParseTypeUnsafePointer() throws (testing::TestError) {
828 -
    try expectI32Pointer("*unsafe i32", types::PointerClass::Unsafe, false);
829 -
    try expectI32Pointer("*unsafe mut i32", types::PointerClass::Unsafe, true);
827 +
    try expectI32Pointer("*unsafe i32", ast::PointerClass::Unsafe, false);
828 +
    try expectI32Pointer("*unsafe mut i32", ast::PointerClass::Unsafe, true);
830 829
}
831 830
832 831
/// Test parsing a slice type.
833 832
@test unsafe fn testParseTypeSlice() throws (testing::TestError) {
834 833
    let node = try! parseTypeStr("*[u8]");
835 834
    let case ast::NodeValue::TypeSig(sig) = node.value
836 835
        else throw testing::TestError::Failed;
837 836
    let case ast::TypeSig::Slice { class, itemType, mutable } = sig
838 837
        else throw testing::TestError::Failed;
839 838
840 -
    assert class == types::PointerClass::Owned;
839 +
    assert class == ast::PointerClass::Owned;
841 840
    try expectIntType(itemType, 1, ast::Signedness::Unsigned);
842 841
    assert not mutable;
843 842
}
844 843
845 844
/// Test parsing a mutable slice type.
848 847
    let case ast::NodeValue::TypeSig(sig) = node.value
849 848
        else throw testing::TestError::Failed;
850 849
    let case ast::TypeSig::Slice { class, itemType, mutable } = sig
851 850
        else throw testing::TestError::Failed;
852 851
853 -
    assert class == types::PointerClass::Owned;
852 +
    assert class == ast::PointerClass::Owned;
854 853
    try expectIntType(itemType, 1, ast::Signedness::Unsigned);
855 854
    assert mutable;
856 855
}
857 856
858 857
/// Test parsing reference and unsafe slice classes.
859 858
@test unsafe fn testParseTypeSliceClasses() throws (testing::TestError) {
860 859
    let refNode = try! parseTypeStr("&[u8]");
861 860
    let case ast::NodeValue::TypeSig(ast::TypeSig::Slice {
862 861
        class: refClass, ..
863 862
    }) = refNode.value else throw testing::TestError::Failed;
864 -
    assert refClass == types::PointerClass::Ref;
863 +
    assert refClass == ast::PointerClass::Ref;
865 864
866 865
    let unsafeNode = try! parseTypeStr("*unsafe [u8]");
867 866
    let case ast::NodeValue::TypeSig(ast::TypeSig::Slice {
868 867
        class: unsafeClass, ..
869 868
    }) = unsafeNode.value else throw testing::TestError::Failed;
870 -
    assert unsafeClass == types::PointerClass::Unsafe;
869 +
    assert unsafeClass == ast::PointerClass::Unsafe;
871 870
}
872 871
873 872
/// Test parsing trait object pointer classes.
874 873
@test unsafe fn testParseTypeTraitObjectClasses() throws (testing::TestError) {
875 874
    let ownedNode = try! parseTypeStr("*opaque Read");
876 875
    let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject {
877 876
        class: ownedClass, ..
878 877
    }) = ownedNode.value else throw testing::TestError::Failed;
879 -
    assert ownedClass == types::PointerClass::Owned;
878 +
    assert ownedClass == ast::PointerClass::Owned;
880 879
881 880
    let refNode = try! parseTypeStr("&opaque Read");
882 881
    let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject {
883 882
        class: refClass, ..
884 883
    }) = refNode.value else throw testing::TestError::Failed;
885 -
    assert refClass == types::PointerClass::Ref;
884 +
    assert refClass == ast::PointerClass::Ref;
886 885
887 886
    let unsafeNode = try! parseTypeStr("*unsafe opaque Read");
888 887
    let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject {
889 888
        class: unsafeClass, ..
890 889
    }) = unsafeNode.value else throw testing::TestError::Failed;
891 -
    assert unsafeClass == types::PointerClass::Unsafe;
890 +
    assert unsafeClass == ast::PointerClass::Unsafe;
892 891
}
893 892
894 893
/// Test parsing an array type.
895 894
@test unsafe fn testParseTypeArray() throws (testing::TestError) {
896 895
    let node = try! parseTypeStr("[i32; 4]");
1157 1156
        "instance Read for Value { unsafe fn (value: &Value) get() {} }"
1158 1157
    );
1159 1158
    let case ast::NodeValue::InstanceDecl { methods, .. } = instanceNode.value
1160 1159
        else throw testing::TestError::Failed;
1161 1160
    try testing::expect(methods.len == 1);
1162 -
    let case ast::NodeValue::MethodDecl { attrs, .. } = methods[0].value
1161 +
    let case ast::NodeValue::MethodDecl { modifiers, .. } = methods[0].value
1163 1162
        else throw testing::TestError::Failed;
1163 +
    let attrs = modifiers.attrs;
1164 1164
    let methodAttrs = attrs else throw testing::TestError::Failed;
1165 1165
    assert ast::attributesContains(&methodAttrs, ast::Attribute::Unsafe);
1166 1166
1167 1167
    static printStorage: [u8; 4096] = undefined;
1168 1168
    let mut printArena = alloc::new(&mut printStorage[..]);
1181 1181
        "trait Read { unsafe fn (&Read) get(); }"
1182 1182
    );
1183 1183
    let case ast::NodeValue::TraitDecl { methods: traitMethods, .. } = traitNode.value
1184 1184
        else throw testing::TestError::Failed;
1185 1185
    let case ast::NodeValue::TraitMethodSig {
1186 -
        attrs: traitAttrs, ..
1186 +
        modifiers: traitModifiers, ..
1187 1187
    } = traitMethods[0].value else throw testing::TestError::Failed;
1188 +
    let traitAttrs = traitModifiers.attrs;
1188 1189
    let traitMethodAttrs = traitAttrs else throw testing::TestError::Failed;
1189 1190
    assert ast::attributesContains(&traitMethodAttrs, ast::Attribute::Unsafe);
1190 1191
1191 1192
    let traitMethodExpr = printer::toExpr(&mut printArena, traitMethods[0]);
1192 1193
    let case sexpr::Expr::List { tail: traitMethodItems, .. } = traitMethodExpr
1198 1199
    let case sexpr::Expr::Sym(traitAttr) = printedTraitAttrs[0]
1199 1200
        else throw testing::TestError::Failed;
1200 1201
    assert mem::eq(traitAttr, "@unsafe");
1201 1202
}
1202 1203
1204 +
/// Test region parameters on methods, trait methods, and instances.
1205 +
@test unsafe fn testParseRegionalMethods() throws (testing::TestError) {
1206 +
    let instanceNode = try! parseStmtStr(
1207 +
        "instance Read for View 'view { fn (view: &View 'view) get 'method () -> &'method u32 { panic; } }"
1208 +
    );
1209 +
    let case ast::NodeValue::InstanceDecl { regions, methods, .. } = instanceNode.value
1210 +
        else throw testing::TestError::Failed;
1211 +
    try testing::expect(regions.len == 1 and methods.len == 1);
1212 +
    let case ast::NodeValue::MethodDecl { modifiers, .. } = methods[0].value
1213 +
        else throw testing::TestError::Failed;
1214 +
    try testing::expect(modifiers.regions.len == 1);
1215 +
1216 +
    let traitNode = try! parseStmtStr(
1217 +
        "trait Read { fn (&Read) get 'method () -> &'method u32; }"
1218 +
    );
1219 +
    let case ast::NodeValue::TraitDecl { methods: traitMethods, .. } = traitNode.value
1220 +
        else throw testing::TestError::Failed;
1221 +
    let case ast::NodeValue::TraitMethodSig { modifiers: traitModifiers, .. } = traitMethods[0].value
1222 +
        else throw testing::TestError::Failed;
1223 +
    try testing::expect(traitModifiers.regions.len == 1);
1224 +
}
1225 +
1203 1226
/// Test parsing a function declaration with attributes.
1204 1227
@test unsafe fn testParseFnDeclAttributes() throws (testing::TestError) {
1205 1228
    let node = try! parseStmtStr("export fn run();");
1206 1229
    let case ast::NodeValue::FnDecl(decl) = node.value
1207 1230
        else throw testing::TestError::Failed;
2985 3008
    assert isUnsafe;
2986 3009
    assert sig.params.len == 1;
2987 3010
    assert sig.returnType <> nil;
2988 3011
    assert sig.throwList.len == 1;
2989 3012
}
3013 +
3014 +
/// Region arguments on an inner type remain distinct from a reference region.
3015 +
@test unsafe fn testRegionReferenceTypes() throws (testing::TestError) {
3016 +
    let root = try! parseTypeStr("&'short mut Node 'arena");
3017 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { region, type }) = root.value
3018 +
        else throw testing::TestError::Failed;
3019 +
    let case ast::NodeValue::Region { name, parent: nil } = region.value
3020 +
        else throw testing::TestError::Failed;
3021 +
    try testing::expect(mem::eq(name, "'short"));
3022 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { class, valueType, mutable }) = type.value
3023 +
        else throw testing::TestError::Failed;
3024 +
    try testing::expect(class == ast::PointerClass::Ref and mutable);
3025 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name: nominal, regions }) = valueType.value
3026 +
        else throw testing::TestError::Failed;
3027 +
    try expectIdent(nominal, "Node");
3028 +
    try testing::expect(regions.len == 1);
3029 +
    let case ast::NodeValue::Region { name: argument, .. } = regions[0].value
3030 +
        else throw testing::TestError::Failed;
3031 +
    try testing::expect(mem::eq(argument, "'arena"));
3032 +
    let slice = try! parseTypeStr("&'r [View 'a 'b]");
3033 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { type: sliceType, .. }) = slice.value
3034 +
        else throw testing::TestError::Failed;
3035 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { .. }) = sliceType.value
3036 +
        else throw testing::TestError::Failed;
3037 +
}
3038 +
3039 +
/// Declaration lists accept regions, parent relations, and ownership derives.
3040 +
@test unsafe fn testRegionDeclarations() throws (testing::TestError) {
3041 +
    let parsedRecord = try! parseStmtStr("record View: 'a + 'b + Copy where 'a: 'b { item: &'b u8 }");
3042 +
    let case ast::NodeValue::RecordDecl(decl) = parsedRecord.value
3043 +
        else throw testing::TestError::Failed;
3044 +
    try testing::expect(decl.regions.len == 2 and decl.derives.len == 1);
3045 +
    let case ast::NodeValue::Region { parent: parent, .. } = decl.regions[1].value
3046 +
        else throw testing::TestError::Failed;
3047 +
    try testing::expect(parent <> nil);
3048 +
    let func = try! parseStmtStr(
3049 +
        "fn read 'long 'short (input: &'short u8) -> &'short u8 throws (ReadError) where 'long: 'short { return input; }"
3050 +
    );
3051 +
    let case ast::NodeValue::FnDecl(f) = func.value else throw testing::TestError::Failed;
3052 +
    try testing::expect(f.regions.len == 2 and f.sig.throwList.len == 1);
3053 +
    let case ast::NodeValue::Region { name: shortName, parent: shortParent } = f.regions[1].value
3054 +
        else throw testing::TestError::Failed;
3055 +
    try testing::expect(mem::eq(shortName, "'short") and shortParent <> nil);
3056 +
    let parentNode = shortParent else throw testing::TestError::Failed;
3057 +
    let case ast::NodeValue::Region { name: parentName, parent: nil } = parentNode.value
3058 +
        else throw testing::TestError::Failed;
3059 +
    try testing::expect(mem::eq(parentName, "'long"));
3060 +
    let parsedUnion = try! parseStmtStr("union Result: 'x { Value(&'x u8), Empty }");
3061 +
    let case ast::NodeValue::UnionDecl(u) = parsedUnion.value else throw testing::TestError::Failed;
3062 +
    try testing::expect(u.regions.len == 1 and u.derives.len == 0);
3063 +
}
3064 +
3065 +
/// Regional blocks retain their source bindings and derived session region.
3066 +
@test unsafe fn testRegionBlocks() throws (testing::TestError) {
3067 +
    let root = try! parseStmtStr("let left: 'r = &mut state.left, right = &state.right in { useView(left); }");
3068 +
    let case ast::NodeValue::RegionBlock { bindings, isSession, .. } = root.value
3069 +
        else throw testing::TestError::Failed;
3070 +
    try testing::expect(not isSession and bindings.len == 2);
3071 +
    let nested = try! parseStmtStr("let view: 'inner = &value where 'outer: 'inner in {}");
3072 +
    let case ast::NodeValue::RegionBlock { region: nestedRegion, .. } = nested.value
3073 +
        else throw testing::TestError::Failed;
3074 +
    let case ast::NodeValue::Region { name: nestedName, parent: nestedParent } = nestedRegion.value
3075 +
        else throw testing::TestError::Failed;
3076 +
    let parent = nestedParent else throw testing::TestError::Failed;
3077 +
    let case ast::NodeValue::Region { name: parentName, parent: nil } = parent.value
3078 +
        else throw testing::TestError::Failed;
3079 +
    try testing::expect(mem::eq(nestedName, "'inner") and mem::eq(parentName, "'outer"));
3080 +
    let arena = try! parseStmtStr("use arena as objects in { make(objects); }");
3081 +
    let case ast::NodeValue::RegionBlock {
3082 +
        region: arenaRegion, bindings: arenaBindings, isSession: arenaSession, ..
3083 +
    } = arena.value
3084 +
        else throw testing::TestError::Failed;
3085 +
    let case ast::NodeValue::Region { name: arenaRegionName, parent: nil } = arenaRegion.value
3086 +
        else throw testing::TestError::Failed;
3087 +
    let case ast::NodeValue::RegionBinding(arenaBinding) = arenaBindings[0].value
3088 +
        else throw testing::TestError::Failed;
3089 +
    let arenaLabel = arenaBinding.label else throw testing::TestError::Failed;
3090 +
    let case ast::NodeValue::Ident(arenaLabelName) = arenaLabel.value
3091 +
        else throw testing::TestError::Failed;
3092 +
    let case ast::NodeValue::AddressOf({ target: arenaTarget, mutable: true }) = arenaBinding.value.value
3093 +
        else throw testing::TestError::Failed;
3094 +
    let case ast::NodeValue::Ident(arenaTargetName) = arenaTarget.value
3095 +
        else throw testing::TestError::Failed;
3096 +
    try testing::expect(
3097 +
        arenaSession and arenaBindings.len == 1
3098 +
        and mem::eq(arenaRegionName, "'objects")
3099 +
        and mem::eq(arenaLabelName, "objects")
3100 +
        and mem::eq(arenaTargetName, "arena")
3101 +
    );
3102 +
}
3103 +
3104 +
/// Region grammar rejects incomplete lists and invalid allocation headers.
3105 +
@test unsafe fn testMalformedRegionSyntax() throws (testing::TestError) {
3106 +
    for source in [
3107 +
        "fn bad: Copy () {}",
3108 +
        "record Bad: 'r + {}",
3109 +
        "let 'r  in {}",
3110 +
        "let p: 'r = value in {}",
3111 +
        "let 'r p = &value in {}",
3112 +
        "let 'child < 'parent p = &value in {}",
3113 +
        "let 'parent as p = &value in {}",
3114 +
        "let value: 'child = &source where 'parent: 'other in {}",
3115 +
        "use arena in {}",
3116 +
        "use arena as in {}",
3117 +
        "use arena as first, second in {}",
3118 +
        "use 'r a = &mut arena in {}",
3119 +
        "use 'r a as &mut arena in {}",
3120 +
    ] {
3121 +
        let parsed = try? parseStmtStr(source);
3122 +
        try testing::expect(parsed == nil);
3123 +
    }
3124 +
    for source in ["&'x' u8", "&mut 'x u8", "View '", "&'r"] {
3125 +
        let parsed = try? parseTypeStr(source);
3126 +
        try testing::expect(parsed == nil);
3127 +
    }
3128 +
}
3129 +
3130 +
/// Ordinary identifiers remain available beside regional block syntax.
3131 +
@test unsafe fn testRegionContextualKeywords() throws (testing::TestError) {
3132 +
    for source in [
3133 +
        "let borrow = 1;",
3134 +
        "let session = 2;",
3135 +
        "borrow(value);",
3136 +
        "session(value);",
3137 +
        "record Context { borrow: u32, session: u32 }",
3138 +
    ] {
3139 +
        let parsed = try? parseStmtStr(source);
3140 +
        try testing::expect(parsed <> nil);
3141 +
    }
3142 +
}
3143 +
3144 +
/// The AST printer retains region parameters and qualified reference types.
3145 +
@test unsafe fn testPrintRegionSignature() throws (testing::TestError) {
3146 +
    let root = try! parseStmtStr("fn read 'r (input: &'r u8) -> &'r u8 { return input; }");
3147 +
    static PRINT_STORAGE: [u8; 4096] = [0; 4096];
3148 +
    let mut arena = alloc::new(&mut PRINT_STORAGE[..]);
3149 +
    let printed = printer::toExpr(&mut arena, root);
3150 +
    let case sexpr::Expr::Block { items, .. } = printed
3151 +
        else throw testing::TestError::Failed;
3152 +
    try testing::expect(items.len == 4);
3153 +
    let case sexpr::Expr::List { head, tail, .. } = items[1]
3154 +
        else throw testing::TestError::Failed;
3155 +
    try testing::expect(mem::eq(head, "regions") and tail.len == 1);
3156 +
    let case sexpr::Expr::Sym(name) = tail[0] else throw testing::TestError::Failed;
3157 +
    try testing::expect(mem::eq(name, "'r"));
3158 +
    let case sexpr::Expr::List { head: refHead, tail: refTail, .. } = items[3]
3159 +
        else throw testing::TestError::Failed;
3160 +
    try testing::expect(mem::eq(refHead, "region-ref") and refTail.len == 2);
3161 +
}
3162 +
3163 +
/// Explicit function region arguments precede ordinary call arguments.
3164 +
@test unsafe fn testFunctionRegionApplication() throws (testing::TestError) {
3165 +
    let expr = try! parseExprStr("read 'a 'b (p)");
3166 +
    let case ast::NodeValue::Call(call) = expr.value else throw testing::TestError::Failed;
3167 +
    let case ast::NodeValue::RegionApply { value, regions } = call.callee.value
3168 +
        else throw testing::TestError::Failed;
3169 +
    try expectIdent(value, "read");
3170 +
    try testing::expect(regions.len == 2 and call.args.len == 1);
3171 +
}
3172 +
3173 +
/// Concrete region headers can name a parent and end without a semicolon.
3174 +
@test unsafe fn testRegionParentSyntax() throws (testing::TestError) {
3175 +
    let parsed = try? parseStmtsStr("fn f 'a (p: &'a u32) { let q: 'b = &*p where 'a: 'b in {} return; }");
3176 +
    try testing::expect(parsed <> nil);
3177 +
}
3178 +
3179 +
/// Cell qualifiers retain pointer ownership and payload syntax.
3180 +
@test unsafe fn testCellPointerType() throws (testing::TestError) {
3181 +
    let root = try! parseTypeStr("*cell u32");
3182 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Cell { class, payload }) = root.value
3183 +
        else throw testing::TestError::Failed;
3184 +
    try testing::expect(class == ast::PointerClass::Owned);
3185 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Integer { width: 4, .. }) = payload.value
3186 +
        else throw testing::TestError::Failed;
3187 +
    let borrowed = try! parseTypeStr("&'r cell Pair 's");
3188 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { type, .. }) = borrowed.value
3189 +
        else throw testing::TestError::Failed;
3190 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Cell { class: refClass, payload: inner }) = type.value
3191 +
        else throw testing::TestError::Failed;
3192 +
    try testing::expect(refClass == ast::PointerClass::Ref);
3193 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Applied { regions, .. }) = inner.value
3194 +
        else throw testing::TestError::Failed;
3195 +
    try testing::expect(regions.len == 1);
3196 +
}
3197 +
3198 +
3199 +
/// Measure committed storage for a statement after an existing node.
3200 +
unsafe fn statementStorageUsed(source: *[u8]) -> u32 {
3201 +
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
3202 +
    ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(true));
3203 +
    let start = alloc::used(&arena.arena);
3204 +
    let mut parser = super::mkParser(scanner::SourceLoc::String, source, &mut arena, &mut STRING_POOL);
3205 +
    super::advance(&mut parser);
3206 +
    try! super::parseStmt(&mut parser);
3207 +
    return alloc::used(&arena.arena) - start;
3208 +
}
3209 +
3210 +
/// Rewinding a failed expression preserves published nodes and source tokens.
3211 +
@test unsafe fn testSpeculativeRestoreStorage() throws (testing::TestError) {
3212 +
    let expectedBytes = statementStorageUsed("return");
3213 +
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
3214 +
    let retained = ast::allocNode(&mut arena, ast::Span { offset: 3, length: 1 }, ast::NodeValue::Bool(true));
3215 +
    let mut parser = super::mkParser(scanner::SourceLoc::String,
3216 +
        "return [speculativeRestoreIdentifier, 1 +", &mut arena, &mut STRING_POOL);
3217 +
    super::advance(&mut parser);
3218 +
    try super::expect(&mut parser, scanner::TokenKind::Eof, "retained diagnostic") catch {
3219 +
    };
3220 +
    let mut expected = parser;
3221 +
    super::advance(&mut expected);
3222 +
    let startOffset = alloc::used(&arena.arena);
3223 +
    let startId = arena.nextId;
3224 +
    let statement = try! super::parseStmt(&mut parser);
3225 +
    let case ast::NodeValue::Return { value } = statement.value else throw testing::TestError::Failed;
3226 +
    try testing::expect(value == nil);
3227 +
    try testing::expect(statement.id == startId);
3228 +
    try testing::expect(alloc::used(&arena.arena) == startOffset + expectedBytes);
3229 +
    try testing::expect(arena.nextId == startId + 1);
3230 +
    try testing::expect(parser.scanner.cursor == expected.scanner.cursor);
3231 +
    try testing::expect(parser.scanner.token == expected.scanner.token);
3232 +
    try testing::expect(parser.current.kind == expected.current.kind);
3233 +
    try testing::expect(parser.current.offset == expected.current.offset);
3234 +
    try testing::expect(parser.previous.kind == expected.previous.kind);
3235 +
    try testing::expect(parser.previous.offset == expected.previous.offset);
3236 +
    try testing::expect(parser.context == expected.context);
3237 +
    try testing::expect(parser.errors.count == 1);
3238 +
    for i in alloc::used(&arena.arena)..ARENA_STORAGE.len {
3239 +
        set ARENA_STORAGE[i] = 0xA5;
3240 +
    }
3241 +
    let case ast::NodeValue::Bool(true) = retained.value else throw testing::TestError::Failed;
3242 +
    try testing::expect(retained.span.offset == 3);
3243 +
    try testing::expect(retained.span.length == 1);
3244 +
    try testing::expect(mem::eq(parser.errors.list[0].message, "retained diagnostic"));
3245 +
    try testing::expect(mem::eq(parser.errors.list[0].token.source, "return"));
3246 +
    let interned = strings::find(&STRING_POOL, "speculativeRestoreIdentifier") else throw testing::TestError::Failed;
3247 +
    try testing::expect(mem::eq(interned, "speculativeRestoreIdentifier"));
3248 +
    let replacement = ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(false));
3249 +
    try testing::expect(replacement.id == startId + 1);
3250 +
    let case ast::NodeValue::Bool(true) = retained.value else throw testing::TestError::Failed;
3251 +
}
3252 +
3253 +
/// Optional return and panic expressions publish only their wrapper after failure.
3254 +
@test unsafe fn testSpeculativeStatementPublication() throws (testing::TestError) {
3255 +
    let expectedBytes = statementStorageUsed("return");
3256 +
    for source in ["return (speculativeReturnIdentifier +", "panic (speculativePanicIdentifier +"] {
3257 +
        let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
3258 +
        ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(true));
3259 +
        let startOffset = alloc::used(&arena.arena);
3260 +
        let mut parser = super::mkParser(scanner::SourceLoc::String, source, &mut arena, &mut STRING_POOL);
3261 +
        super::advance(&mut parser);
3262 +
        let startId = arena.nextId;
3263 +
        let statement = try! super::parseStmt(&mut parser);
3264 +
        try testing::expect(statement.id == startId);
3265 +
        try testing::expect(arena.nextId == startId + 1);
3266 +
        try testing::expect(alloc::used(&arena.arena) == startOffset + expectedBytes);
3267 +
        try testing::expect(parser.errors.count == 0);
3268 +
        try testing::expect(parser.current.kind == scanner::TokenKind::LParen);
3269 +
        match statement.value {
3270 +
            case ast::NodeValue::Return { value } => try testing::expect(value == nil),
3271 +
            case ast::NodeValue::Panic { message } => try testing::expect(message == nil),
3272 +
            else => throw testing::TestError::Failed,
3273 +
        }
3274 +
    }
3275 +
}
lib/std/lang/resolver.rad +3240 -824
47 47
export constant MAX_INSTANCES: u32 = 128;
48 48
/// Maximum standalone methods (across all types).
49 49
export constant MAX_METHODS: u32 = 256;
50 50
/// Maximum number of linear bindings active in one function.
51 51
constant MAX_LINEAR_BINDINGS: u32 = 32;
52 +
/// Maximum full-region projections active in nested lexical regions.
53 +
constant MAX_REGIONAL_LOANS: u32 = 32;
52 54
/// Maximum inline field depth used to prove borrow separation.
53 55
constant MAX_BORROW_FIELDS: u32 = 16;
54 56
/// Maximum nesting depth tracked for loops.
55 57
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
56 58
57 59
/// Trait definition stored in the resolver.
58 60
export record TraitType: Copy {
59 61
    /// Trait name.
60 62
    name: *[u8],
63 +
    /// Module that declares the trait.
64 +
    moduleId: u16,
61 65
    /// Method signatures, including from supertraits.
62 66
    methods: *unsafe mut [TraitMethod],
63 67
    /// Supertraits that must also be implemented.
64 68
    supertraits: *unsafe mut [*unsafe TraitType],
65 69
}
166 170
    length: u32,
167 171
}
168 172
169 173
/// Record nominal type.
170 174
export record RecordType: Copy {
175 +
    /// Region parameters of the source declaration.
176 +
    regions: ?*RegionScope,
177 +
    /// Exact region arguments, if this is an applied type.
178 +
    application: ?*unsafe NominalApplication,
171 179
    fields: *unsafe [RecordField],
172 180
    labeled: bool,
173 -
    /// Cached layout.
174 -
    layout: Layout,
181 +
    /// Shared layout of the source declaration.
182 +
    layout: *Layout,
175 183
    /// Whether the declaration explicitly carries the `Once` marker.
176 184
    declaredLinear: bool,
177 185
    /// Whether the declaration explicitly carries the `Copy` marker.
178 186
    declaredCopy: bool,
179 187
}
180 188
181 189
/// Union nominal type.
182 190
export record UnionType: Copy {
191 +
    /// Region parameters of the source declaration.
192 +
    regions: ?*RegionScope,
193 +
    /// Exact region arguments, if this is an applied type.
194 +
    application: ?*unsafe NominalApplication,
183 195
    variants: *unsafe [UnionVariant],
184 -
    /// Cached layout.
185 -
    layout: Layout,
196 +
    /// Shared layout of the source declaration.
197 +
    layout: *Layout,
186 198
    /// Cached payload offset within the union aggregate.
187 199
    valOffset: u32,
188 200
    /// If all variants have void payloads.
189 201
    isAllVoid: bool,
190 202
    /// Whether the declaration explicitly carries the `Once` marker.
196 208
/// Metadata for user-defined types.
197 209
export union NominalType: Copy {
198 210
    /// Placeholder for a type that hasn't been fully resolved yet.
199 211
    /// Stores the declaration node for lazy resolution.
200 212
    Placeholder(*ast::Node),
213 +
    /// Declaration whose value layout is under analysis.
214 +
    Resolving(*ast::Node),
215 +
    /// Applied type whose field or variant view is not yet resolved.
216 +
    Application(*unsafe NominalApplication),
201 217
    Record(RecordType),
202 218
    Union(UnionType),
203 219
}
204 220
205 221
/// Coercion plan, when coercion from one type to another.
274 290
    },
275 291
}
276 292
277 293
/// Resolved function signature details.
278 294
export record FnType: Copy {
295 +
    /// Symbolic regions declared by the source function.
296 +
    regions: ?*RegionScope,
279 297
    /// Parameter types in call order.
280 298
    paramTypes: *[*Type],
281 299
    /// Return value type.
282 300
    returnType: *Type,
283 301
    /// Error types that the function can throw.
294 312
    Nil, Undefined, Int,
295 313
    /// Primitive types.
296 314
    Void, Opaque, Never, Bool,
297 315
    /// Integer types.
298 316
    U8, U16, U32, U64, I8, I16, I32, I64,
317 +
    /// Shared cell pointer with value access to a Copy payload.
318 +
    Cell {
319 +
        /// Storage lifetime and ownership class.
320 +
        class: types::PointerClass,
321 +
        /// Payload type, preserved by all writes.
322 +
        payload: *Type,
323 +
    },
324 +
    /// Affine allocation interface retained by a lexical region.
325 +
    Session(*unsafe types::Region),
299 326
    /// Range types, eg. `start..end`.
300 327
    Range {
301 328
        start: ?*Type,
302 329
        end: ?*Type,
303 330
    },
545 572
    TryNonThrowing,
546 573
    /// Inferred catch binding used with multi-error callee.
547 574
    TryCatchMultiError,
548 575
    /// Duplicate error type in typed catch clauses.
549 576
    TryCatchDuplicateType,
577 +
    /// Distinct error types have the same tag after region erasure.
578 +
    AmbiguousRegionalError,
550 579
    /// Typed catch clauses do not cover all error types.
551 580
    TryCatchNonExhaustive,
552 581
    /// Called a fallible function without using `try`.
553 582
    MissingTry,
554 583
    /// Cannot use opaque type in this context.
625 654
    LinearUndefined,
626 655
    /// A `Copy` declaration contains a non-copy field or variant.
627 656
    CopyContainsNonCopy,
628 657
    /// A declaration carries both `Copy` and `Once`.
629 658
    ConflictingOwnershipMarkers,
659 +
    /// A region name is not visible in this declaration or block.
660 +
    UnknownRegion(*[u8]),
661 +
    /// A region application has the wrong argument count.
662 +
    RegionArgumentCount(CountMismatch),
663 +
    /// A region parameter has no consistent argument from checked references.
664 +
    RegionInference(*[u8]),
665 +
    /// A region argument does not satisfy its declared parent relation.
666 +
    RegionParent(*[u8]),
667 +
    /// A region parent relation contains a cycle.
668 +
    RegionCycle(*[u8]),
669 +
    /// A value retains a region that has left lexical scope.
670 +
    RegionEscape(*[u8]),
671 +
    /// A session requires one exclusive borrow of an allocation trait implementer.
672 +
    InvalidSessionSource,
673 +
    /// Allocation requires a value that can be discarded without destruction.
674 +
    InvalidAllocationValue,
675 +
    /// Cell payload is not a storable plain Copy value.
676 +
    InvalidCellPayload,
677 +
    /// The allocated value has an invalid or overflowing layout.
678 +
    InvalidAllocationLayout,
679 +
    /// A compiler-known allocation trait method has an invalid signature.
680 +
    InvalidAllocationRuntime,
681 +
    /// The function has too many distinct full-region projections.
682 +
    RegionalLoanOverflow,
683 +
    /// A nominal value layout contains itself.
684 +
    RecursiveType,
630 685
    /// A reference appears in a storable or escaping position.
631 686
    InvalidRefPosition,
632 687
    /// A reference local requires a fixed binding to existing storage.
633 688
    RefBinding,
634 689
    /// Call arguments contain overlapping incompatible loans.
675 730
record SuperAccessResult: Copy {
676 731
    scope: *unsafe mut Scope,
677 732
    child: *ast::Node,
678 733
}
679 734
735 +
/// Initialization operation performed after session storage reservation.
736 +
export union SessionAllocationKind: Copy {
737 +
    /// Initialize one object from a value.
738 +
    New,
739 +
    /// Copy plain Copy elements from a slice.
740 +
    Copy,
741 +
    /// Fill a slice with a plain Copy value.
742 +
    Fill,
743 +
}
744 +
745 +
/// Typed session allocation and its checked runtime reservation function.
746 +
export record SessionAllocation: Copy {
747 +
    /// Initialization operation.
748 +
    kind: SessionAllocationKind,
749 +
    /// Initialized element type.
750 +
    item: *Type,
751 +
    /// Allocation trait used by the session source.
752 +
    traitInfo: *unsafe TraitType,
753 +
    /// Reservation method slot in the allocation trait.
754 +
    methodIndex: u32,
755 +
}
756 +
680 757
/// Node-specific resolver metadata.
681 758
export union NodeExtra: Copy {
682 759
    /// No extra data for this node.
683 760
    None,
761 +
    /// Region identities owned by a source declaration.
762 +
    Regions(*RegionScope),
684 763
    /// Resolved field index for record literal fields.
685 764
    RecordField { index: u32 },
686 765
    /// Slice range metadata for subscript expressions with ranges.
687 766
    SliceRange(SliceRangeInfo),
688 767
    /// Cached union variant metadata for patterns/constructors.
700 779
        /// Method index in the v-table.
701 780
        methodIndex: u32,
702 781
    },
703 782
    /// Standalone method call metadata.
704 783
    MethodCall { method: *unsafe MethodEntry },
784 +
    /// Typed allocation through a session interface.
785 +
    SessionAllocation(SessionAllocation),
705 786
    /// Slice `.append(val, allocator)` method call.
706 787
    SliceAppend { elemType: *Type },
707 788
    /// Slice `.delete(index)` method call.
708 789
    SliceDelete { elemType: *Type },
709 790
}
817 898
}
818 899
819 900
/// Per-control-flow-path ownership state.
820 901
/// Read only the initialized symbol prefix below `len`.
821 902
record LinearEnv: Copy {
903 +
    /// Active full-region loans, indexed by the checker's regional loan table.
904 +
    regionalLoans: u64,
822 905
    /// Symbol pointers. Entries below `len` are initialized and not optional.
823 906
    symbols: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS],
824 907
    /// Bit set for each binding that remains available.
825 908
    available: u64,
826 909
    /// Number of initialized entries in `symbols`.
852 935
}
853 936
854 937
/// Function-local exact-use checker state.
855 938
/// Read loop arrays only at indices below `loopDepth`.
856 939
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
857 -
record LinearChecker: Copy {
940 +
record LinearChecker: 'arena + 'checking where 'arena: 'checking {
858 941
    /// Resolver that owns the symbols and diagnostics.
859 -
    resolver: *unsafe mut Resolver,
942 +
    resolver: &'checking mut Resolver 'arena,
943 +
    /// Regional projections discovered in this function.
944 +
    regional: [RegionalLoan; MAX_REGIONAL_LOANS],
945 +
    /// Number of initialized regional loan entries.
946 +
    regionalLen: u32,
947 +
    /// Named regions active at the current source location.
948 +
    regions: ?*RegionScope,
949 +
    /// Regional loans carried to each loop's next iteration.
950 +
    loopBackLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
951 +
    /// Regional loans carried to each loop's exits.
952 +
    loopExitLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
953 +
    /// Regions active at each loop's entry and exit.
954 +
    loopRegions: [?*RegionScope; MAX_LINEAR_LOOP_DEPTH],
860 955
    /// Source places protected by active pattern references.
861 956
    loans: [BorrowPlace; MAX_LINEAR_BINDINGS],
862 957
    /// Number of initialized entries in `loans`.
863 958
    loanLen: u32,
864 959
    /// Reference locals in active lexical scopes.
886 981
        return MatchSubject { effectiveTy: *target, by };
887 982
    }
888 983
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
889 984
}
890 985
986 +
/// Region names introduced by a declaration or lexical block.
987 +
export record RegionScope: Copy {
988 +
    /// Entries in declaration order.
989 +
    entries: *unsafe [*unsafe mut types::Region],
990 +
    /// Enclosing lexical region environment.
991 +
    parent: ?*RegionScope,
992 +
}
993 +
994 +
/// Region arguments for one source declaration.
995 +
record RegionSubstitution: Copy {
996 +
    /// Declared parameters in source order.
997 +
    parameters: *RegionScope,
998 +
    /// Inferred or explicit arguments. Every entry must be set before substitution.
999 +
    arguments: *unsafe mut [?*unsafe types::Region],
1000 +
}
1001 +
1002 +
/// One interned application of a nominal declaration to exact region arguments.
1003 +
export record NominalApplication: Copy {
1004 +
    /// Canonical source declaration identity.
1005 +
    base: *unsafe NominalType,
1006 +
    /// Source parameters in declaration order.
1007 +
    parameters: *RegionScope,
1008 +
    /// Region arguments in parameter order.
1009 +
    arguments: *unsafe [*unsafe types::Region],
1010 +
    /// Stable descriptor for the substituted field or variant view.
1011 +
    view: *unsafe mut NominalType,
1012 +
    /// Next application in the resolver cache.
1013 +
    next: ?*unsafe NominalApplication,
1014 +
}
1015 +
891 1016
/// Global resolver state.
892 -
export record Resolver {
1017 +
export record Resolver: 'arena {
1018 +
    /// Active region names for source type checking.
1019 +
    regionScope: ?*RegionScope,
1020 +
    /// Interned applications of nominal region parameters.
1021 +
    applications: ?*unsafe NominalApplication,
893 1022
    /// Current scope.
894 1023
    scope: *unsafe mut Scope,
895 1024
    /// Package scope containing package roots and top-level symbols.
896 1025
    pkgScope: *unsafe mut Scope,
897 1026
    /// Stack of loop contexts for nested loops.
906 1035
    currentMod: u16,
907 1036
    /// Whether the current lexical context permits unsafe operations.
908 1037
    inUnsafeContext: bool,
909 1038
    /// Configuration for semantic analysis.
910 1039
    config: Config,
911 -
    /// Unified arena for symbols, scopes, and nominal type.
912 -
    arena: alloc::Arena,
1040 +
    /// Caller-owned arena, valid for this resolver and all emitted metadata.
1041 +
    arena: &'arena mut alloc::Arena,
913 1042
    /// Combined semantic metadata table indexed by node ID.
914 1043
    nodeData: NodeDataTable,
915 1044
    /// Linked list of interned types.
916 1045
    types: ?*TypeNode,
917 1046
    /// Diagnostics recorded so far.
939 1068
record TypeNode: Copy {
940 1069
    ty: Type,
941 1070
    next: ?*TypeNode,
942 1071
}
943 1072
1073 +
/// Look up a region name in a lexical environment.
1074 +
unsafe fn findRegion(scope: ?*RegionScope, name: *[u8]) -> ?*unsafe mut types::Region {
1075 +
    let mut current = scope;
1076 +
    while let env = current {
1077 +
        for region in env.entries {
1078 +
            if mem::eq(region.name, name) {
1079 +
                return region;
1080 +
            }
1081 +
        }
1082 +
        set current = env.parent;
1083 +
    }
1084 +
    return nil;
1085 +
}
1086 +
1087 +
/// Resolve a source region name without using its spelling as an identity.
1088 +
unsafe fn resolveRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe types::Region
1089 +
    throws (ResolveError)
1090 +
{
1091 +
    let case ast::NodeValue::Region { name, .. } = node.value
1092 +
        else panic "resolveRegion: invalid region node";
1093 +
    let region = findRegion(self.regionScope, name)
1094 +
        else throw emitError(self, node, ErrorKind::UnknownRegion(name));
1095 +
    return region;
1096 +
}
1097 +
1098 +
/// Bind all region parameters before resolving their parent relations.
1099 +
unsafe fn bindRegions 'arena (self: &mut Resolver 'arena, owner: *ast::Node, nodes: *[*ast::Node]) -> ?*RegionScope
1100 +
    throws (ResolveError)
1101 +
{
1102 +
    if let case NodeExtra::Regions(scope) = self.nodeData.entries[owner.id].extra {
1103 +
        return scope;
1104 +
    }
1105 +
    let mut count: u32 = 0;
1106 +
    for node in nodes {
1107 +
        if let case ast::NodeValue::Region { .. } = node.value {
1108 +
            set count += 1;
1109 +
        }
1110 +
    }
1111 +
    if count == 0 {
1112 +
        return nil;
1113 +
    }
1114 +
    let entries = try! alloc::allocRawSlice(
1115 +
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), count
1116 +
    ) as *unsafe mut [*unsafe mut types::Region];
1117 +
    let mut index: u32 = 0;
1118 +
    for node in nodes {
1119 +
        let case ast::NodeValue::Region { name, .. } = node.value else continue;
1120 +
        for i in 0..index {
1121 +
            if mem::eq(entries[i].name, name) {
1122 +
                throw emitError(self, node, ErrorKind::DuplicateBinding(name));
1123 +
            }
1124 +
        }
1125 +
        let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
1126 +
            as *unsafe mut types::Region;
1127 +
        set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil };
1128 +
        set entries[index] = region;
1129 +
        set index += 1;
1130 +
    }
1131 +
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
1132 +
    set *scope = RegionScope { entries, parent: nil };
1133 +
    let frozen: *RegionScope = scope;
1134 +
    set index = 0;
1135 +
    for node in nodes {
1136 +
        let case ast::NodeValue::Region { parent, .. } = node.value else continue;
1137 +
        if let parentNode = parent {
1138 +
            let case ast::NodeValue::Region { name, .. } = parentNode.value
1139 +
                else panic "bindRegions: invalid parent node";
1140 +
            let target = findRegion(frozen, name)
1141 +
                else throw emitError(self, parentNode, ErrorKind::UnknownRegion(name));
1142 +
            if types::regionContains(entries[index], target) {
1143 +
                throw emitError(self, parentNode, ErrorKind::RegionCycle(entries[index].name));
1144 +
            }
1145 +
            set entries[index].parent = target;
1146 +
        }
1147 +
        set index += 1;
1148 +
    }
1149 +
    set self.nodeData.entries[owner.id].extra = NodeExtra::Regions(frozen);
1150 +
    return frozen;
1151 +
}
1152 +
944 1153
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
945 -
export unsafe fn allocType(self: &mut Resolver, ty: Type) -> *Type {
1154 +
export unsafe fn allocType 'arena (self: &mut Resolver 'arena, ty: Type) -> *Type {
946 1155
    // Search existing types for a match.
947 1156
    let mut cursor = self.types;
948 1157
    while let node = cursor {
949 1158
        if node.ty == ty {
950 1159
            return &node.ty;
951 1160
        }
952 1161
        set cursor = node.next;
953 1162
    }
954 1163
    // Allocate a new type node from the arena.
955 1164
    let node = try! alloc::alloc(
956 -
        &mut self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
1165 +
        &mut *self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
957 1166
    ) as *mut TypeNode;
958 1167
959 1168
    set *node = TypeNode { ty, next: self.types };
960 1169
    let frozen: *TypeNode = node;
961 1170
    set self.types = frozen;
962 1171
963 1172
    return &frozen.ty;
964 1173
}
965 1174
966 1175
/// Allocate a nominal type descriptor and return a pointer to it.
967 -
unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *unsafe mut NominalType {
1176 +
unsafe fn allocNominalType 'arena (self: &mut Resolver 'arena, info: NominalType) -> *unsafe mut NominalType {
968 1177
    // Nb. We don't attempt to de-duplicate nominal type entries,
969 1178
    // since they don't carry node information and we create
970 1179
    // placeholder entries when binding symbols.
971 1180
    let entry = try! alloc::allocRaw(
972 -
        &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType)
1181 +
        self.arena, @sizeOf(NominalType), @alignOf(NominalType)
973 1182
    ) as *unsafe mut NominalType;
974 1183
975 1184
    set *entry = info;
976 1185
977 1186
    return entry;
978 1187
}
979 1188
1189 +
/// Allocate the single runtime layout for a nominal declaration.
1190 +
unsafe fn allocLayout 'arena (self: &mut Resolver 'arena, value: Layout) -> *Layout {
1191 +
    let layout = try! alloc::alloc(&mut *self.arena, @sizeOf(Layout), @alignOf(Layout)) as *mut Layout;
1192 +
    set *layout = value;
1193 +
    return layout;
1194 +
}
1195 +
1196 +
/// Get the exact arguments of an applied nominal descriptor.
1197 +
export unsafe fn nominalApplication(info: *unsafe NominalType) -> ?*unsafe NominalApplication {
1198 +
    match *info {
1199 +
        case NominalType::Application(applied) => return applied,
1200 +
        case NominalType::Record(body) => return body.application,
1201 +
        case NominalType::Union(body) => return body.application,
1202 +
        else => return nil,
1203 +
    }
1204 +
}
1205 +
1206 +
/// Get source region parameters without forcing a recursive type's layout.
1207 +
unsafe fn nominalParameters 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType) -> ?*RegionScope
1208 +
    throws (ResolveError)
1209 +
{
1210 +
    match *info {
1211 +
        case NominalType::Placeholder(node) => return try declarationRegions(self, node),
1212 +
        case NominalType::Resolving(node) => return try declarationRegions(self, node),
1213 +
        case NominalType::Application(applied) => return applied.parameters,
1214 +
        case NominalType::Record(body) => return body.regions,
1215 +
        case NominalType::Union(body) => return body.regions,
1216 +
    }
1217 +
}
1218 +
1219 +
/// Bind the regions declared by a nominal source node.
1220 +
unsafe fn declarationRegions 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*RegionScope
1221 +
    throws (ResolveError)
1222 +
{
1223 +
    match node.value {
1224 +
        case ast::NodeValue::RecordDecl(decl) => return try bindRegions(self, node, decl.regions),
1225 +
        case ast::NodeValue::UnionDecl(decl) => return try bindRegions(self, node, decl.regions),
1226 +
        else => panic "declarationRegions: expected nominal declaration",
1227 +
    }
1228 +
}
1229 +
1230 +
/// Use a hinted application only for the same unapplied nominal declaration.
1231 +
unsafe fn hintedNominal(info: *unsafe NominalType, hint: Type) -> *unsafe NominalType {
1232 +
    if nominalApplication(info) <> nil {
1233 +
        return info;
1234 +
    }
1235 +
    let mut target = hint;
1236 +
    if let case Type::Optional(inner) = target {
1237 +
        set target = *inner;
1238 +
    }
1239 +
    if let case Type::Nominal(other) = target {
1240 +
        if let applied = nominalApplication(other); applied.base == info {
1241 +
            return other;
1242 +
        }
1243 +
    }
1244 +
    return info;
1245 +
}
1246 +
1247 +
/// Require explicit arguments for a parameterized nominal type.
1248 +
unsafe fn requireNominalArguments 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType, site: *ast::Node)
1249 +
    throws (ResolveError)
1250 +
{
1251 +
    if nominalApplication(info) <> nil {
1252 +
        return;
1253 +
    }
1254 +
    if let parameters = try nominalParameters(self, info) {
1255 +
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
1256 +
            expected: parameters.entries.len, actual: 0,
1257 +
        }));
1258 +
    }
1259 +
}
1260 +
1261 +
/// Intern an exact nominal application before resolving its recursive members.
1262 +
unsafe fn internNominalApplication 'arena (
1263 +
    self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution
1264 +
) -> *unsafe mut NominalType {
1265 +
    let mut cursor = self.applications;
1266 +
    while let applied = cursor {
1267 +
        if applied.base == base {
1268 +
            let mut same = true;
1269 +
            for argument, i in applied.arguments {
1270 +
                let other = map.arguments[i] else panic "internNominalApplication: missing argument";
1271 +
                if argument.id <> other.id {
1272 +
                    set same = false;
1273 +
                    break;
1274 +
                }
1275 +
            }
1276 +
            if same {
1277 +
                return applied.view;
1278 +
            }
1279 +
        }
1280 +
        set cursor = applied.next;
1281 +
    }
1282 +
    let arguments = try! alloc::allocRawSlice(
1283 +
        self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len
1284 +
    ) as *unsafe mut [*unsafe types::Region];
1285 +
    for argument, i in map.arguments {
1286 +
        let region = argument else panic "internNominalApplication: incomplete map";
1287 +
        set arguments[i] = region;
1288 +
    }
1289 +
    let entry = try! alloc::allocRaw(
1290 +
        self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication)
1291 +
    ) as *unsafe mut NominalApplication;
1292 +
    let view = allocNominalType(self, NominalType::Application(entry));
1293 +
    set *entry = NominalApplication { base, parameters: map.parameters, arguments, view, next: self.applications };
1294 +
    set self.applications = entry;
1295 +
    return view;
1296 +
}
1297 +
1298 +
/// Check explicit region arguments and intern the applied nominal type.
1299 +
unsafe fn applyNominalRegions 'arena (
1300 +
    self: &mut Resolver 'arena, base: *unsafe NominalType, regions: *[*ast::Node], site: *ast::Node
1301 +
) -> *unsafe mut NominalType throws (ResolveError) {
1302 +
    if nominalApplication(base) <> nil {
1303 +
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: 0, actual: regions.len }));
1304 +
    }
1305 +
    let parameters = try nominalParameters(self, base);
1306 +
    let mut count: u32 = 0;
1307 +
    if let scope = parameters {
1308 +
        set count = scope.entries.len;
1309 +
    }
1310 +
    if count <> regions.len {
1311 +
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
1312 +
    }
1313 +
    let scope = parameters else panic "applyNominalRegions: empty application";
1314 +
    let map = regionSubstitution(self, scope);
1315 +
    for region, i in regions {
1316 +
        set map.arguments[i] = try resolveRegion(self, region);
1317 +
    }
1318 +
    try validateRegionArguments(self, &map, site);
1319 +
    return internNominalApplication(self, base, &map);
1320 +
}
1321 +
1322 +
/// Complete nominal views stored inline within an applied type.
1323 +
/// Pointer, slice, and cell targets have independent storage layouts.
1324 +
unsafe fn resolveInlineTypeViews 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node)
1325 +
    throws (ResolveError)
1326 +
{
1327 +
    match ty {
1328 +
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1329 +
        case Type::Array(array) => try resolveInlineTypeViews(self, *array.item, site),
1330 +
        case Type::Optional(inner) => try resolveInlineTypeViews(self, *inner, site),
1331 +
        else => {
1332 +
        },
1333 +
    }
1334 +
}
1335 +
1336 +
/// Resolve a substituted member view with the source declaration's shared layout.
1337 +
unsafe fn resolveNominalApplication 'arena (self: &mut Resolver 'arena, applied: *unsafe NominalApplication, site: *ast::Node)
1338 +
    throws (ResolveError)
1339 +
{
1340 +
    try ensureNominalResolved(self, applied.base, site);
1341 +
    let map = regionSubstitution(self, applied.parameters);
1342 +
    for argument, i in applied.arguments {
1343 +
        set map.arguments[i] = argument;
1344 +
    }
1345 +
    let allocator = alloc::arenaAllocator(self.arena);
1346 +
    match *applied.base {
1347 +
        case NominalType::Record(body) => {
1348 +
            let mut fields: *unsafe mut [RecordField] = &mut [];
1349 +
            for field in body.fields {
1350 +
                let fieldType = substituteRegions(self, &map, field.fieldType);
1351 +
                try resolveInlineTypeViews(self, fieldType, site);
1352 +
                fields.append(RecordField {
1353 +
                    name: field.name,
1354 +
                    fieldType,
1355 +
                    offset: field.offset,
1356 +
                }, allocator);
1357 +
            }
1358 +
            set *applied.view = NominalType::Record(RecordType {
1359 +
                regions: body.regions,
1360 +
                application: applied,
1361 +
                fields,
1362 +
                labeled: body.labeled,
1363 +
                layout: body.layout,
1364 +
                declaredLinear: body.declaredLinear,
1365 +
                declaredCopy: body.declaredCopy,
1366 +
            });
1367 +
        }
1368 +
        case NominalType::Union(body) => {
1369 +
            let mut variants: *unsafe mut [UnionVariant] = &mut [];
1370 +
            for variant in body.variants {
1371 +
                let valueType = substituteRegions(self, &map, variant.valueType);
1372 +
                try resolveInlineTypeViews(self, valueType, site);
1373 +
                variants.append(UnionVariant {
1374 +
                    name: variant.name,
1375 +
                    valueType,
1376 +
                    symbol: variant.symbol,
1377 +
                }, allocator);
1378 +
            }
1379 +
            set *applied.view = NominalType::Union(UnionType {
1380 +
                regions: body.regions,
1381 +
                application: applied,
1382 +
                variants,
1383 +
                layout: body.layout,
1384 +
                valOffset: body.valOffset,
1385 +
                isAllVoid: body.isAllVoid,
1386 +
                declaredLinear: body.declaredLinear,
1387 +
                declaredCopy: body.declaredCopy,
1388 +
            });
1389 +
        }
1390 +
        else => panic "resolveNominalApplication: unresolved base",
1391 +
    }
1392 +
}
1393 +
1394 +
/// Complete all applied member views before semantic metadata reaches lowering.
1395 +
unsafe fn resolveNominalApplications 'arena (self: &mut Resolver 'arena, site: *ast::Node) throws (ResolveError) {
1396 +
    let mut end: ?*unsafe NominalApplication = nil;
1397 +
    loop {
1398 +
        let first = self.applications;
1399 +
        let mut cursor = first;
1400 +
        while cursor <> end {
1401 +
            let applied = cursor else panic "resolveNominalApplications: invalid frontier";
1402 +
            try ensureNominalResolved(self, applied.view, site);
1403 +
            set cursor = applied.next;
1404 +
        }
1405 +
        if self.applications == first {
1406 +
            return;
1407 +
        }
1408 +
        set end = first;
1409 +
    }
1410 +
}
1411 +
980 1412
/// Allocate a function type descriptor and return a pointer to it.
981 -
unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType {
1413 +
unsafe fn allocFnType 'arena (self: &mut Resolver 'arena, info: FnType) -> *FnType {
982 1414
    let entry = try! alloc::alloc(
983 -
        &mut self.arena, @sizeOf(FnType), @alignOf(FnType)
1415 +
        &mut *self.arena, @sizeOf(FnType), @alignOf(FnType)
984 1416
    ) as *mut FnType;
985 1417
986 1418
    set *entry = info;
987 1419
988 1420
    return entry;
989 1421
}
990 1422
991 1423
/// Returns an error, if any, associated with the given node.
992 -
fn errorForNode(self: &Resolver, node: *ast::Node) -> ?Error {
1424 +
fn errorForNode 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Error {
993 1425
    for i in 0..self.errors.len {
994 1426
        let err = self.errors.entries[i];
995 1427
        if err.node == node {
996 1428
            return err;
997 1429
        }
999 1431
    return nil;
1000 1432
}
1001 1433
1002 1434
/// Storage buffers used by the analyzer.
1003 1435
export record ResolverStorage {
1004 -
    /// Unified arena for symbols, scopes, and nominal type.
1005 -
    arena: alloc::Arena,
1006 1436
    /// Node semantic metadata indexed by node ID.
1007 1437
    nodeData: *mut [NodeData],
1008 1438
    /// Package scope.
1009 1439
    pkgScope: *unsafe mut Scope,
1010 1440
    /// Error storage.
1018 1448
    /// Root AST node.
1019 1449
    rootAst: *ast::Node,
1020 1450
}
1021 1451
1022 1452
/// Construct a resolver with module context and backing storage.
1023 -
export unsafe fn resolver(
1453 +
/// The arena owner and backing bytes must retain stable addresses during use.
1454 +
/// Arena reclamation can occur only after all metadata uses end.
1455 +
export unsafe fn resolver 'arena (
1456 +
    arena: &'arena mut alloc::Arena,
1024 1457
    storage: ResolverStorage,
1025 1458
    config: Config
1026 -
) -> Resolver {
1027 -
    let case ResolverStorage { arena: initialArena, nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
1028 -
    let mut arena = initialArena;
1459 +
) -> Resolver 'arena {
1460 +
    let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
1029 1461
    let symbols = try! alloc::allocRawSlice(
1030 -
        &mut arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS
1462 +
        arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS
1031 1463
    ) as *unsafe mut [*unsafe mut Symbol];
1032 1464
1033 1465
    // Initialize the root scope.
1034 1466
    // TODO: Set this up when declaring `PKG_SCOPE`, not here.
1035 1467
    set *pkgScope = Scope {
1057 1489
    let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined;
1058 1490
    // TODO: Simplify.
1059 1491
    for i in 0..moduleScopes.len {
1060 1492
        set moduleScopes[i] = nil;
1061 1493
    }
1062 -
    return Resolver {
1494 +
    return Resolver 'arena {
1495 +
        regionScope: nil,
1496 +
        applications: nil,
1063 1497
        scope: pkgScope,
1064 1498
        pkgScope: pkgScope,
1065 1499
        loopStack: undefined,
1066 1500
        loopDepth: 0,
1067 1501
        currentFn: nil,
1083 1517
    };
1084 1518
}
1085 1519
1086 1520
/// Capture the current errors in an immutable arena allocation.
1087 1521
/// The allocation must remain valid while the diagnostics are used.
1088 -
export unsafe fn diagnostics(self: &mut Resolver) -> Diagnostics {
1522 +
export unsafe fn diagnostics 'arena (self: &mut Resolver 'arena) -> Diagnostics {
1089 1523
    let count = self.errors.len;
1090 1524
    let entries = try! alloc::allocSlice(
1091 -
        &mut self.arena, @sizeOf(Error), @alignOf(Error), count
1525 +
        self.arena, @sizeOf(Error), @alignOf(Error), count
1092 1526
    ) as *mut [Error];
1093 1527
    for i in 0..self.errors.len {
1094 1528
        set entries[i] = self.errors.entries[i];
1095 1529
    }
1096 1530
    return Diagnostics { errors: entries };
1108 1542
    }
1109 1543
    return errs[index];
1110 1544
}
1111 1545
1112 1546
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1113 -
fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1547 +
fn emitError 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1114 1548
    // If our error list is full, just return an error without recording it.
1115 1549
    if self.errors.len >= self.errors.entries.len {
1116 1550
        return ResolveError::Failure;
1117 1551
    }
1118 1552
    // Don't record more than one error per node.
1125 1559
1126 1560
    return ResolveError::Failure;
1127 1561
}
1128 1562
1129 1563
/// Like [`emitError`], but for type mismatches specifically.
1130 -
unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1564 +
unsafe fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1131 1565
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1132 1566
}
1133 1567
1134 1568
/// Allocate a scope object with the given symbol capacity.
1135 -
unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1569 +
unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1136 1570
    // Check for an existing scope for this node, and don't allocate a new
1137 1571
    // one in that case.
1138 1572
    if let scope = scopeFor(self, owner) {
1139 1573
        return scope;
1140 1574
    }
1141 1575
    assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds";
1142 -
    let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(Scope), @alignOf(Scope));
1576 +
    let p = try! alloc::allocRaw(self.arena, @sizeOf(Scope), @alignOf(Scope));
1143 1577
    let entry = p as *unsafe mut Scope;
1144 1578
1145 1579
    // Allocate symbols from the arena.
1146 1580
    let symbols = try! alloc::allocRawSlice(
1147 -
        &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity
1581 +
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity
1148 1582
    ) as *unsafe mut [*unsafe mut Symbol];
1149 1583
1150 1584
    set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 };
1151 1585
    set self.nodeData.entries[owner.id].scope = entry;
1152 1586
1154 1588
}
1155 1589
1156 1590
/// Enter a new local scope that is the child of the current scope.
1157 1591
/// This creates a parent/child relationship that means that lookups in the
1158 1592
/// child scope can recurse upwards.
1159 -
export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *unsafe Scope {
1593 +
export unsafe fn enterScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node) -> *unsafe Scope {
1160 1594
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1161 1595
    set scope.parent = self.scope;
1162 1596
    set self.scope = scope;
1163 1597
    return scope;
1164 1598
}
1165 1599
1166 1600
/// Enter a module scope. Returns an object that can be used to exit the scope.
1167 -
export unsafe fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1601 +
export unsafe fn enterModuleScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1168 1602
    let prevScope = self.scope;
1169 1603
    let prevMod = self.currentMod;
1170 1604
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1171 1605
1172 1606
    set self.scope = scope;
1177 1611
1178 1612
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1179 1613
}
1180 1614
1181 1615
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1182 -
unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1616 +
unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1183 1617
    let modEntry = module::findChild(self.moduleGraph, name, self.currentMod)
1184 1618
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1185 -
    let modRoot = modEntry.ast
1619 +
    let modRoot = module::astFor(modEntry)
1186 1620
        else panic "enterSubModule: analyzing module that wasn't parsed";
1187 1621
1188 1622
    return enterModuleScope(self, modRoot, modEntry);
1189 1623
}
1190 1624
1191 1625
/// Exit a module scope, given the object returned by `enterModuleScope`.
1192 -
export fn exitModuleScope(self: &mut Resolver, entry: ModuleScope) {
1626 +
export fn exitModuleScope 'arena (self: &mut Resolver 'arena, entry: ModuleScope) {
1193 1627
    set self.scope = entry.prevScope;
1194 1628
    set self.currentMod = entry.prevMod;
1195 1629
}
1196 1630
1197 1631
/// Exit the most recent scope.
1198 -
export unsafe fn exitScope(self: &mut Resolver) {
1632 +
export unsafe fn exitScope 'arena (self: &mut Resolver 'arena) {
1199 1633
    let parent = self.scope.parent else {
1200 1634
        // TODO: This should be a panic, but one of the tests hits this
1201 1635
        // clause, which might be a bug in the generator.
1202 1636
        return;
1203 1637
    };
1204 1638
    set self.scope = parent;
1205 1639
}
1206 1640
1207 1641
/// Visit the body of a loop while tracking nesting depth.
1208 -
unsafe fn visitLoop(self: &mut Resolver, body: *ast::Node) -> Type
1642 +
unsafe fn visitLoop 'arena (self: &mut Resolver 'arena, body: *ast::Node) -> Type
1209 1643
    throws (ResolveError)
1210 1644
{
1211 1645
    assert self.loopDepth < MAX_LOOP_DEPTH, "visitLoop: loop nesting depth exceeded";
1212 1646
    set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false };
1213 1647
    set self.loopDepth += 1;
1225 1659
    }
1226 1660
    return Type::Never;
1227 1661
}
1228 1662
1229 1663
/// Require that loop control statements appear inside a loop.
1230 -
unsafe fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
1664 +
unsafe fn ensureInsideLoop 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
1231 1665
    if self.loopDepth == 0 {
1232 1666
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1233 1667
    }
1234 1668
}
1235 1669
1236 1670
/// Bind a loop pattern to the provided type.
1237 -
unsafe fn bindForLoopPattern(self: &mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool)
1671 +
unsafe fn bindForLoopPattern 'arena (self: &mut Resolver 'arena, pattern: *ast::Node, ty: Type, mutable: bool)
1238 1672
    throws (ResolveError)
1239 1673
{
1240 1674
    match pattern.value {
1241 1675
        case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => {
1242 1676
            let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0);
1247 1681
        }
1248 1682
    }
1249 1683
}
1250 1684
1251 1685
/// Set the expected return type for a new function body.
1252 -
unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) {
1686 +
unsafe fn enterFn 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: &FnType) {
1253 1687
    assert self.currentFn == nil, "enterFn: already in a function";
1254 1688
    set self.currentFn = *ty;
1255 1689
    set self.currentFnNode = node;
1256 1690
    enterScope(self, node);
1257 1691
}
1258 1692
1259 1693
/// Clear the expected return type when leaving a function body.
1260 -
unsafe fn exitFn(self: &mut Resolver) {
1694 +
unsafe fn exitFn 'arena (self: &mut Resolver 'arena) {
1261 1695
    if self.currentFn == nil {
1262 1696
        // TODO: This should be a panic, but one of the tests hits this
1263 1697
        // clause, which might be a bug in the generator.
1264 1698
        return;
1265 1699
    }
1267 1701
    set self.currentFnNode = nil;
1268 1702
    exitScope(self);
1269 1703
}
1270 1704
1271 1705
/// Extract the identifier text from a node.
1272 -
unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8]
1706 +
unsafe fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8]
1273 1707
    throws (ResolveError)
1274 1708
{
1275 1709
    let case ast::NodeValue::Ident(name) = node.value
1276 1710
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1277 1711
    return name;
1278 1712
}
1279 1713
1280 1714
/// Associate a resolved symbol with an AST node.
1281 -
fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *unsafe mut Symbol) {
1715 +
fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) {
1282 1716
    if let existingSym = self.nodeData.entries[node.id].sym {
1283 1717
        panic "setNodeSymbol: a symbol is already associated with this node";
1284 1718
    }
1285 1719
    set self.nodeData.entries[node.id].sym = symbol;
1286 1720
}
1287 1721
1288 1722
/// Associate a resolved type with an AST node and return it.
1289 -
fn setNodeType(self: &mut Resolver, node: *ast::Node, ty: Type) -> Type {
1723 +
fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type {
1290 1724
    if ty == Type::Unknown {
1291 1725
        // In this case, we simply don't associate a type.
1292 1726
        return ty;
1293 1727
    }
1294 1728
    set self.nodeData.entries[node.id].ty = ty;
1307 1741
    }
1308 1742
    return Type::Void;
1309 1743
}
1310 1744
1311 1745
/// Associate a coercion plan with an AST node.
1312 -
fn setNodeCoercion(self: &mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion {
1746 +
fn setNodeCoercion 'arena (self: &mut Resolver 'arena, node: *ast::Node, coercion: Coercion) -> Coercion {
1313 1747
    if coercion == Coercion::Identity {
1314 1748
        return coercion;
1315 1749
    }
1316 1750
    set self.nodeData.entries[node.id].coercion = coercion;
1317 1751
1318 1752
    return coercion;
1319 1753
}
1320 1754
1321 1755
/// Associate a constant value with an AST node.
1322 -
fn setNodeConstValue(self: &mut Resolver, node: *ast::Node, value: ConstValue) {
1756 +
fn setNodeConstValue 'arena (self: &mut Resolver 'arena, node: *ast::Node, value: ConstValue) {
1323 1757
    set self.nodeData.entries[node.id].constValue = value;
1324 1758
}
1325 1759
1326 1760
/// Associate a record field index with a record literal field node.
1327 -
fn setRecordFieldIndex(self: &mut Resolver, node: *ast::Node, index: u32) {
1761 +
fn setRecordFieldIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node, index: u32) {
1328 1762
    set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index };
1329 1763
}
1330 1764
1331 1765
/// Associate slice range metadata with a subscript expression.
1332 -
fn setSliceRangeInfo(self: &mut Resolver, node: *ast::Node, info: SliceRangeInfo) {
1766 +
fn setSliceRangeInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: SliceRangeInfo) {
1333 1767
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
1334 1768
}
1335 1769
1336 1770
/// Associate union variant metadata with a pattern or constructor node.
1337 -
fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
1771 +
fn setVariantInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, ordinal: u32, tag: u32) {
1338 1772
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
1339 1773
}
1340 1774
1341 1775
/// Associate trait method call metadata with a call node.
1342 -
fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) {
1776 +
fn setTraitMethodCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) {
1343 1777
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
1344 1778
}
1345 1779
1346 1780
/// Associate for-loop metadata with a for-loop node.
1347 -
fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) {
1781 +
fn setForLoopInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: ForLoopInfo) {
1348 1782
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
1349 1783
}
1350 1784
1351 1785
/// Retrieve the constant value associated with a node, if any.
1352 -
export fn constValueEntry(self: &Resolver, node: *ast::Node) -> ?ConstValue {
1786 +
export fn constValueEntry 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ConstValue {
1353 1787
    return self.nodeData.entries[node.id].constValue;
1354 1788
}
1355 1789
1356 1790
/// Get the resolved record field index for a record literal field node.
1357 -
export fn recordFieldIndexFor(self: &Resolver, node: *ast::Node) -> ?u32 {
1791 +
export fn recordFieldIndexFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?u32 {
1358 1792
    if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra {
1359 1793
        return index;
1360 1794
    }
1361 1795
    return nil;
1362 1796
}
1363 1797
1364 1798
/// Get the slice range metadata for a subscript expression with a range index.
1365 -
export fn sliceRangeInfoFor(self: &Resolver, node: *ast::Node) -> ?SliceRangeInfo {
1799 +
export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo {
1366 1800
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
1367 1801
        return info;
1368 1802
    }
1369 1803
    return nil;
1370 1804
}
1371 1805
1372 1806
/// Get the for-loop metadata for a for-loop node.
1373 -
export fn forLoopInfoFor(self: &Resolver, node: *ast::Node) -> ?ForLoopInfo {
1807 +
export fn forLoopInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ForLoopInfo {
1374 1808
    if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra {
1375 1809
        return info;
1376 1810
    }
1377 1811
    return nil;
1378 1812
}
1379 1813
1380 1814
/// Associate match prong metadata with a match prong node.
1381 -
fn setProngCatchAll(self: &mut Resolver, node: *ast::Node, catchAll: bool) {
1815 +
fn setProngCatchAll 'arena (self: &mut Resolver 'arena, node: *ast::Node, catchAll: bool) {
1382 1816
    set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll };
1383 1817
}
1384 1818
1385 1819
/// Check if a prong is catch-all.
1386 -
export fn isProngCatchAll(self: &Resolver, node: *ast::Node) -> bool {
1820 +
export fn isProngCatchAll 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
1387 1821
    if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra {
1388 1822
        return catchAll;
1389 1823
    }
1390 1824
    return false;
1391 1825
}
1392 1826
1393 1827
/// Set match metadata.
1394 -
fn setMatchConst(self: &mut Resolver, node: *ast::Node, isConst: bool) {
1828 +
fn setMatchConst 'arena (self: &mut Resolver 'arena, node: *ast::Node, isConst: bool) {
1395 1829
    set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst };
1396 1830
}
1397 1831
1398 1832
/// Check if a match has all constant patterns.
1399 -
export fn isMatchConst(self: &Resolver, node: *ast::Node) -> bool {
1833 +
export fn isMatchConst 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
1400 1834
    if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra {
1401 1835
        return isConst;
1402 1836
    }
1403 1837
    return false;
1404 1838
}
1405 1839
1406 1840
/// Get the resolver metadata for a node.
1407 -
export fn nodeData(self: &Resolver, node: *ast::Node) -> NodeData {
1841 +
export fn nodeData 'arena (self: &Resolver 'arena, node: *ast::Node) -> NodeData {
1408 1842
    return self.nodeData.entries[node.id];
1409 1843
}
1410 1844
1411 1845
/// Get the type for a node, or `nil` if unknown.
1412 -
export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type {
1846 +
export fn typeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Type {
1413 1847
    let ty = self.nodeData.entries[node.id].ty;
1414 1848
    if ty == Type::Unknown {
1415 1849
        return nil;
1416 1850
    }
1417 1851
    return ty;
1418 1852
}
1419 1853
1420 1854
/// Get the scope associated with a node.
1421 -
export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Scope {
1855 +
export fn scopeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope {
1422 1856
    return self.nodeData.entries[node.id].scope;
1423 1857
}
1424 1858
1425 1859
/// Get the symbol bound to a node.
1426 -
export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Symbol {
1860 +
export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
1427 1861
    return self.nodeData.entries[node.id].sym;
1428 1862
}
1429 1863
1430 1864
/// Get the coercion plan associated with a node, if any.
1431 -
export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion {
1865 +
export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion {
1432 1866
    let c = self.nodeData.entries[node.id].coercion;
1433 1867
    if c == Coercion::Identity {
1434 1868
        return nil;
1435 1869
    }
1436 1870
    return c;
1437 1871
}
1438 1872
1439 1873
/// Get the module ID for a symbol by walking up its scope chain.
1440 -
export unsafe fn moduleIdForSymbol(self: &Resolver, sym: *unsafe Symbol) -> ?u16 {
1874 +
export unsafe fn moduleIdForSymbol 'arena (self: &Resolver 'arena, sym: *unsafe Symbol) -> ?u16 {
1441 1875
    // For module-level symbols, return the cached module ID.
1442 1876
    if let id = sym.moduleId {
1443 1877
        return id;
1444 1878
    }
1445 1879
    // For module symbols, return the module ID directly.
1453 1887
    return nil;
1454 1888
}
1455 1889
1456 1890
/// Get the binding node for a variant pattern.
1457 1891
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
1458 -
export unsafe fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node {
1892 +
export unsafe fn variantPatternBinding 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> ?*ast::Node {
1459 1893
    let case ast::NodeValue::Call(call) = pattern.value
1460 1894
        else return nil;
1461 1895
    let sym = symbolFor(self, call.callee)
1462 1896
        else return nil;
1463 1897
    let case SymbolData::Variant { .. } = sym.data
1473 1907
    }
1474 1908
    return arg;
1475 1909
}
1476 1910
1477 1911
/// Allocate a new symbol, and return a reference to it.
1478 -
unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol {
1479 -
    let sym = try! alloc::allocRaw(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol;
1912 +
unsafe fn allocSymbol 'arena (self: &mut Resolver 'arena, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol {
1913 +
    let sym = try! alloc::allocRaw(self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol;
1480 1914
    set *sym = Symbol { name, data, attrs, node, moduleId: nil };
1481 1915
1482 1916
    return sym;
1483 1917
}
1484 1918
1485 1919
/// Check that a type is boolean, otherwise throw an error.
1486 -
unsafe fn checkBoolean(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1920 +
unsafe fn checkBoolean 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
1487 1921
    return try checkEqual(self, node, Type::Bool);
1488 1922
}
1489 1923
1490 1924
/// Check that a type is numeric, otherwise throw an error.
1491 -
unsafe fn checkNumeric(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1925 +
unsafe fn checkNumeric 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
1492 1926
    let ty = try infer(self, node);
1493 1927
    if not isNumericType(ty) {
1494 1928
        throw emitError(self, node, ErrorKind::ExpectedNumeric);
1495 1929
    }
1496 1930
    return ty;
1523 1957
}
1524 1958
1525 1959
/// Get the layout of a type.
1526 1960
export unsafe fn getTypeLayout(ty: Type) -> Layout {
1527 1961
    match ty {
1528 -
        case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1529 -
        case Type::Slice { .. }, Type::TraitObject { .. } =>
1962 +
        case Type::Pointer { .. } => return Layout {
1963 +
            size: PTR_SIZE, alignment: PTR_SIZE
1964 +
        },
1965 +
        case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) =>
1530 1966
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1531 1967
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
1532 1968
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
1533 1969
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
1534 1970
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
1535 1971
        case Type::Int => return Layout { size: 8, alignment: 8 },
1536 1972
        case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 },
1537 1973
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1974 +
        case Type::Cell { .. } => return Layout {
1975 +
            size: PTR_SIZE, alignment: PTR_SIZE
1976 +
        },
1538 1977
        case Type::Array(arr) => return getArrayLayout(arr),
1539 1978
        case Type::Optional(inner) => return getOptionalLayout(*inner),
1540 1979
        case Type::Nominal(info) => return getNominalLayout(*info),
1541 1980
        else => {
1542 1981
            panic "getTypeLayout: the given type cannot be layed out";
1543 1982
        }
1544 1983
    }
1545 1984
}
1546 1985
1547 1986
/// Get the layout of a type or value.
1548 -
export unsafe fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout {
1987 +
export unsafe fn getLayout 'arena (self: &Resolver 'arena, node: *ast::Node, ty: Type) -> Layout {
1549 1988
    let mut layout = getTypeLayout(ty);
1550 1989
    // Check for symbol-specific alignment override.
1551 1990
    if let sym = symbolFor(self, node) {
1552 1991
        if let case SymbolData::Value { alignment, .. } = sym.data {
1553 1992
            if alignment > 0 {
1626 2065
}
1627 2066
1628 2067
/// Get the layout of a nominal type.
1629 2068
export fn getNominalLayout(info: NominalType) -> Layout {
1630 2069
    match info {
1631 -
        case NominalType::Placeholder(_) => {
1632 -
            panic "getNominalLayout: placeholder type";
2070 +
        case NominalType::Placeholder(_), NominalType::Resolving(_), NominalType::Application(_) => {
2071 +
            panic "getNominalLayout: unresolved type";
1633 2072
        }
1634 2073
        case NominalType::Record(recordType) => {
1635 -
            return recordType.layout;
2074 +
            return *recordType.layout;
1636 2075
        }
1637 2076
        case NominalType::Union(unionType) => {
1638 -
            return unionType.layout;
2077 +
            return *unionType.layout;
1639 2078
        }
1640 2079
    }
1641 2080
}
1642 2081
1643 2082
/// Get the layout of a result aggregate with a tag and the larger payload.
1775 2214
        }
1776 2215
    }
1777 2216
}
1778 2217
1779 2218
/// Ensure all nested nominal types in a type are resolved.
1780 -
unsafe fn ensureTypeResolved(self: &mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
2219 +
unsafe fn ensureTypeResolved 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) throws (ResolveError) {
1781 2220
    match ty {
1782 2221
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1783 -
        case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site),
1784 -
        case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse.
2222 +
        // Pointer and slice layouts do not depend on their element layout.
2223 +
        case Type::Pointer { .. }, Type::Slice { .. } => {
2224 +
        },
2225 +
        case Type::Cell { payload, .. } => try validateCellPayload(self, site, *payload),
1785 2226
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1786 2227
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
1787 2228
        else => {},
1788 2229
    }
1789 2230
}
1790 2231
1791 2232
/// Ensure a nominal type has its body resolved.
1792 -
unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *unsafe NominalType, site: *ast::Node)
2233 +
unsafe fn ensureNominalResolved 'arena (self: &mut Resolver 'arena, tyInfo: *unsafe NominalType, site: *ast::Node)
1793 2234
    throws (ResolveError)
1794 2235
{
2236 +
    if let case NominalType::Application(applied) = *tyInfo {
2237 +
        try resolveNominalApplication(self, applied, site);
2238 +
        return;
2239 +
    }
2240 +
    if let case NominalType::Resolving(_) = *tyInfo {
2241 +
        throw emitError(self, site, ErrorKind::RecursiveType);
2242 +
    }
1795 2243
    if let case NominalType::Placeholder(declNode) = *tyInfo {
1796 2244
        // When resolving on-demand (e.g. from a child module), switch to the
1797 2245
        // declaring module's scope so field type lookups find the right symbols.
1798 2246
        let prevScope = self.scope;
1799 2247
        let prevMod = self.currentMod;
1809 2257
            }
1810 2258
        }
1811 2259
1812 2260
        match declNode.value {
1813 2261
            case ast::NodeValue::RecordDecl(decl) => {
1814 -
                try resolveRecordBody(self, declNode, decl);
2262 +
                try resolveRecordBody(self, declNode, decl) catch error {
2263 +
                    set self.scope = prevScope;
2264 +
                    set self.currentMod = prevMod;
2265 +
                    throw error;
2266 +
                };
1815 2267
            }
1816 2268
            case ast::NodeValue::UnionDecl(decl) => {
1817 -
                try resolveUnionBody(self, declNode, decl);
2269 +
                try resolveUnionBody(self, declNode, decl) catch error {
2270 +
                    set self.scope = prevScope;
2271 +
                    set self.currentMod = prevMod;
2272 +
                    throw error;
2273 +
                };
1818 2274
            }
1819 2275
            else => {},
1820 2276
        }
1821 2277
        set self.scope = prevScope;
1822 2278
        set self.currentMod = prevMod;
1823 2279
    }
1824 2280
}
1825 2281
1826 2282
/// Check if all elements in a node list are assignable to the target type.
1827 -
unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *[*ast::Node]) -> bool {
2283 +
unsafe fn isListAssignable 'arena (self: &mut Resolver 'arena, targetType: Type, items: *[*ast::Node]) -> bool {
1828 2284
    for itemNode in items {
1829 2285
        let elemTy = typeFor(self, itemNode)
1830 2286
            else return false;
1831 2287
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
1832 2288
            // Do nothing.
1843 2299
    from: types::PointerClass,
1844 2300
    inUnsafeContext: bool,
1845 2301
) -> bool {
1846 2302
    return to == from or (
1847 2303
        to == types::PointerClass::Ref
1848 -
        and (from == types::PointerClass::Owned
2304 +
        and (types::isReference(from) or from == types::PointerClass::Owned
1849 2305
            or (from == types::PointerClass::Unsafe and inUnsafeContext))
1850 2306
    );
1851 2307
}
1852 2308
2309 +
/// Limit an exclusive value's implicit borrow to its owner's borrow.
2310 +
unsafe fn assignableValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: Type) -> Type {
2311 +
    match source {
2312 +
        case Type::Pointer { class, target, mutable: true } => {
2313 +
            let usable = pointerAddressClass(self, node, class, true);
2314 +
            return Type::Pointer { class: usable, target, mutable: true };
2315 +
        }
2316 +
        case Type::Slice { class, item, mutable: true } => {
2317 +
            let usable = pointerAddressClass(self, node, class, true);
2318 +
            return Type::Slice { class: usable, item, mutable: true };
2319 +
        }
2320 +
        case Type::TraitObject { class, traitInfo, mutable: true } => {
2321 +
            let usable = pointerAddressClass(self, node, class, true);
2322 +
            return Type::TraitObject { class: usable, traitInfo, mutable: true };
2323 +
        }
2324 +
        case Type::Optional(inner) => {
2325 +
            let value = assignableValueType(self, node, *inner);
2326 +
            if typesEqual(value, *inner) {
2327 +
                return source;
2328 +
            }
2329 +
            return Type::Optional(allocType(self, value));
2330 +
        }
2331 +
        else => return source,
2332 +
    }
2333 +
}
2334 +
1853 2335
/// Check if the `from` type is assignable to the `to` type, and return a
1854 2336
/// coercion plan if so.
1855 2337
/// Referenced storage requires equal element types. Function values may gain
1856 2338
/// an unsafe call requirement.
1857 -
unsafe fn isAssignable(self: &mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
2339 +
unsafe fn isAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, rval: *ast::Node) -> ?Coercion {
2340 +
    let from = assignableValueType(self, rval, source);
1858 2341
    if to == Type::Unknown or from == Type::Unknown {
1859 2342
        return nil;
1860 2343
    }
1861 2344
    if from == Type::Undefined {
2345 +
        if containsRegion(to) {
2346 +
            return nil;
2347 +
        }
1862 2348
        if to == Type::Never {
1863 2349
            return nil;
1864 2350
        }
1865 2351
        // TODO: Don't let `undefined` be used in place of functions and other
1866 2352
        // non-data types.
1872 2358
        return Coercion::Identity;
1873 2359
    }
1874 2360
    if to == from {
1875 2361
        return Coercion::Identity;
1876 2362
    }
2363 +
    if let case Type::Cell { class, payload } = to {
2364 +
        let case Type::Cell { class: sourceClass, payload: sourcePayload } = from else return nil;
2365 +
        if pointerClassesAssignable(class, sourceClass, self.inUnsafeContext) and typesEqual(*payload, *sourcePayload) {
2366 +
            return Coercion::Identity;
2367 +
        }
2368 +
        return nil;
2369 +
    }
1877 2370
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
1878 2371
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
1879 2372
            else return nil;
1880 2373
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) {
1881 2374
            return nil;
2037 2530
    return fnSignatureEqual(a, b);
2038 2531
}
2039 2532
2040 2533
/// Compare parameter, return, and error types of functions.
2041 2534
fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool {
2535 +
    if a.regions <> b.regions {
2536 +
        return false;
2537 +
    }
2042 2538
    if a.paramTypes.len <> b.paramTypes.len {
2043 2539
        return false;
2044 2540
    }
2045 2541
    if a.throwList.len <> b.throwList.len {
2046 2542
        return false;
2078 2574
            else return false;
2079 2575
        return aClass == bClass and aMutable == bMutable
2080 2576
            and typesEqual(*aItem, *bItem);
2081 2577
    }
2082 2578
    match a {
2579 +
        case Type::Cell { class, payload } => {
2580 +
            let case Type::Cell { class: otherClass, payload: other } = b else return false;
2581 +
            return class == otherClass and typesEqual(*payload, *other);
2582 +
        }
2083 2583
        case Type::Array(aa) => {
2084 2584
            let case Type::Array(ab) = b else return false;
2085 2585
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
2086 2586
        }
2087 2587
        case Type::Optional(oa) => {
2094 2594
        }
2095 2595
        else => return false,
2096 2596
    }
2097 2597
}
2098 2598
2599 +
/// Compare types after lexical region arguments are erased.
2600 +
/// Nominal types retain their source declaration identity.
2601 +
export unsafe fn erasedTypesEqual(a: Type, b: Type) -> bool {
2602 +
    if typesEqual(a, b) {
2603 +
        return true;
2604 +
    }
2605 +
    match a {
2606 +
        case Type::Cell { class, payload } => {
2607 +
            let case Type::Cell { class: otherClass, payload: other } = b else return false;
2608 +
            return erasedClassesEqual(class, otherClass) and erasedTypesEqual(*payload, *other);
2609 +
        }
2610 +
        case Type::Session(_) => {
2611 +
            let case Type::Session(_) = b else return false;
2612 +
            return true;
2613 +
        }
2614 +
        case Type::Nominal(left) => {
2615 +
            let case Type::Nominal(right) = b else return false;
2616 +
            let mut leftBase = left;
2617 +
            let mut rightBase = right;
2618 +
            if let app = nominalApplication(left) {
2619 +
                set leftBase = app.base;
2620 +
            }
2621 +
            if let app = nominalApplication(right) {
2622 +
                set rightBase = app.base;
2623 +
            }
2624 +
            return leftBase == rightBase;
2625 +
        }
2626 +
        case Type::Pointer { class, target, mutable } => {
2627 +
            let case Type::Pointer { class: otherClass, target: other, mutable: otherMutable } = b
2628 +
                else return false;
2629 +
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2630 +
                and erasedTypesEqual(*target, *other);
2631 +
        }
2632 +
        case Type::Slice { class, item, mutable } => {
2633 +
            let case Type::Slice { class: otherClass, item: other, mutable: otherMutable } = b
2634 +
                else return false;
2635 +
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2636 +
                and erasedTypesEqual(*item, *other);
2637 +
        }
2638 +
        case Type::TraitObject { class, traitInfo, mutable } => {
2639 +
            let case Type::TraitObject { class: otherClass, traitInfo: other, mutable: otherMutable } = b
2640 +
                else return false;
2641 +
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable and traitInfo == other;
2642 +
        }
2643 +
        case Type::Array(left) => {
2644 +
            let case Type::Array(right) = b else return false;
2645 +
            return left.length == right.length and erasedTypesEqual(*left.item, *right.item);
2646 +
        }
2647 +
        case Type::Optional(left) => {
2648 +
            let case Type::Optional(right) = b else return false;
2649 +
            return erasedTypesEqual(*left, *right);
2650 +
        }
2651 +
        case Type::Fn(left) => {
2652 +
            let case Type::Fn(right) = b else return false;
2653 +
            if left.isUnsafe <> right.isUnsafe or left.paramTypes.len <> right.paramTypes.len
2654 +
                or left.throwList.len <> right.throwList.len {
2655 +
                return false;
2656 +
            }
2657 +
            for ty, i in left.paramTypes {
2658 +
                if not erasedTypesEqual(*ty, *right.paramTypes[i]) {
2659 +
                    return false;
2660 +
                }
2661 +
            }
2662 +
            for ty, i in left.throwList {
2663 +
                if not erasedTypesEqual(*ty, *right.throwList[i]) {
2664 +
                    return false;
2665 +
                }
2666 +
            }
2667 +
            return erasedTypesEqual(*left.returnType, *right.returnType);
2668 +
        }
2669 +
        else => return false,
2670 +
    }
2671 +
}
2672 +
2673 +
/// Compare pointer classes without lexical region identities.
2674 +
fn erasedClassesEqual(a: types::PointerClass, b: types::PointerClass) -> bool {
2675 +
    return a == b or (types::isReference(a) and types::isReference(b));
2676 +
}
2677 +
2678 +
/// Require distinct runtime tags for errors with different source types.
2679 +
unsafe fn validateErrorTag 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type, errors: *[*Type]) throws (ResolveError) {
2680 +
    for other in errors {
2681 +
        if not typesEqual(ty, *other) and erasedTypesEqual(ty, *other) {
2682 +
            throw emitError(self, node, ErrorKind::AmbiguousRegionalError);
2683 +
        }
2684 +
    }
2685 +
}
2686 +
2099 2687
/// Return whether `ty` is a direct reference.
2100 2688
export fn isRefType(ty: Type) -> bool {
2101 2689
    match ty {
2102 -
        case Type::Pointer { class: types::PointerClass::Ref, .. },
2103 -
             Type::Slice { class: types::PointerClass::Ref, .. },
2104 -
             Type::TraitObject { class: types::PointerClass::Ref, .. } => return true,
2690 +
        case Type::Cell { class, .. } => return types::isReference(class),
2691 +
        case Type::Pointer { class, .. } => return types::isReference(class),
2692 +
        case Type::Slice { class, .. } => return types::isReference(class),
2693 +
        case Type::TraitObject { class, .. } => return types::isReference(class),
2105 2694
        else => return false,
2106 2695
    }
2107 2696
}
2108 2697
2109 -
/// Return whether a type contains a reference.
2110 -
fn containsRef(ty: Type) -> bool {
2111 -
    if isRefType(ty) {
2698 +
/// Get the region of a direct named reference.
2699 +
fn referenceRegion(ty: Type) -> ?*unsafe types::Region {
2700 +
    let mut class = types::PointerClass::Ref;
2701 +
    match ty {
2702 +
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
2703 +
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
2704 +
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
2705 +
        case Type::TraitObject { class: objectClass, .. } => set class = objectClass,
2706 +
        else => return nil,
2707 +
    }
2708 +
    if let case types::PointerClass::Region(region) = class {
2709 +
        return region;
2710 +
    }
2711 +
    return nil;
2712 +
}
2713 +
2714 +
/// Require every free region in a value type to remain in lexical scope.
2715 +
unsafe fn validateRegionDependencies 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
2716 +
    throws (ResolveError)
2717 +
{
2718 +
    try validateRegionStorage(self, node, ty, nil);
2719 +
}
2720 +
2721 +
/// Check a dependency against storage lifetime or current lexical visibility.
2722 +
unsafe fn regionCoversStorage(
2723 +
    scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region
2724 +
) -> bool {
2725 +
    if let region = destination {
2726 +
        return types::regionContains(dependency, region);
2727 +
    }
2728 +
    return regionInScope(scope, dependency);
2729 +
}
2730 +
2731 +
/// Require stored references to cover the lifetime of checked destination storage.
2732 +
unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type)
2733 +
    throws (ResolveError)
2734 +
{
2735 +
    if let case types::PointerClass::Region(region) = addressStorageClass(self, place) {
2736 +
        try validateRegionStorage(self, value, ty, region);
2737 +
    }
2738 +
}
2739 +
2740 +
/// Require all type dependencies to cover the destination or active lexical scope.
2741 +
unsafe fn validateRegionStorage 'arena (
2742 +
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, destination: ?*unsafe types::Region
2743 +
)
2744 +
    throws (ResolveError)
2745 +
{
2746 +
    if let case Type::Session(region) = ty {
2747 +
        if not regionCoversStorage(self.regionScope, region, destination) {
2748 +
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2749 +
        }
2750 +
    }
2751 +
    if let region = referenceRegion(ty) {
2752 +
        if not regionCoversStorage(self.regionScope, region, destination) {
2753 +
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2754 +
        }
2755 +
    }
2756 +
    match ty {
2757 +
        case Type::Pointer { target, .. } => try validateRegionStorage(self, node, *target, destination),
2758 +
        case Type::Slice { item, .. } => try validateRegionStorage(self, node, *item, destination),
2759 +
        case Type::Cell { payload, .. } => try validateRegionStorage(self, node, *payload, destination),
2760 +
        case Type::Array(array) => try validateRegionStorage(self, node, *array.item, destination),
2761 +
        case Type::Optional(inner) => try validateRegionStorage(self, node, *inner, destination),
2762 +
        case Type::Nominal(info) => {
2763 +
            if let applied = nominalApplication(info) {
2764 +
                for region in applied.arguments {
2765 +
                    if not regionCoversStorage(self.regionScope, region, destination) {
2766 +
                        throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2767 +
                    }
2768 +
                }
2769 +
            }
2770 +
        }
2771 +
        case Type::Fn(info) => {
2772 +
            if info.regions <> nil {
2773 +
                return;
2774 +
            }
2775 +
            for parameter in info.paramTypes {
2776 +
                try validateRegionStorage(self, node, *parameter, destination);
2777 +
            }
2778 +
            for error in info.throwList {
2779 +
                try validateRegionStorage(self, node, *error, destination);
2780 +
            }
2781 +
            try validateRegionStorage(self, node, *info.returnType, destination);
2782 +
        }
2783 +
        else => {
2784 +
        },
2785 +
    }
2786 +
}
2787 +
2788 +
/// Return whether a type has an explicit region dependency.
2789 +
unsafe fn containsRegion(ty: Type) -> bool {
2790 +
    match ty {
2791 +
        case Type::Cell { class, payload } => {
2792 +
            if let case types::PointerClass::Region(_) = class {
2793 +
                return true;
2794 +
            }
2795 +
            return containsRegion(*payload);
2796 +
        }
2797 +
        case Type::Session(_) => return true,
2798 +
        case Type::Pointer { class, target, .. } => {
2799 +
            if let case types::PointerClass::Region(_) = class {
2800 +
                return true;
2801 +
            }
2802 +
            return containsRegion(*target);
2803 +
        }
2804 +
        case Type::Slice { class, item, .. } => {
2805 +
            if let case types::PointerClass::Region(_) = class {
2806 +
                return true;
2807 +
            }
2808 +
            return containsRegion(*item);
2809 +
        }
2810 +
        case Type::TraitObject { class, .. } => {
2811 +
            if let case types::PointerClass::Region(_) = class {
2812 +
                return true;
2813 +
            }
2814 +
            return false;
2815 +
        }
2816 +
        case Type::Array(array) => return containsRegion(*array.item),
2817 +
        case Type::Optional(inner) => return containsRegion(*inner),
2818 +
        case Type::Fn(info) => {
2819 +
            if info.regions <> nil or containsRegion(*info.returnType) {
2820 +
                return true;
2821 +
            }
2822 +
            for param in info.paramTypes {
2823 +
                if containsRegion(*param) {
2824 +
                    return true;
2825 +
                }
2826 +
            }
2827 +
            for error in info.throwList {
2828 +
                if containsRegion(*error) {
2829 +
                    return true;
2830 +
                }
2831 +
            }
2832 +
            return false;
2833 +
        }
2834 +
        case Type::Nominal(info) => return nominalApplication(info) <> nil,
2835 +
        else => return false,
2836 +
    }
2837 +
}
2838 +
2839 +
/// Return whether a stored type contains a reference without a named region.
2840 +
unsafe fn containsUnscopedRef(ty: Type) -> bool {
2841 +
    if isRefType(ty) and referenceRegion(ty) == nil {
2112 2842
        return true;
2113 2843
    }
2114 2844
    if let case Type::Pointer { target, .. } = ty {
2115 -
        return containsRef(*target);
2845 +
        return containsUnscopedRef(*target);
2116 2846
    }
2117 2847
    if let case Type::Slice { item, .. } = ty {
2118 -
        return containsRef(*item);
2848 +
        return containsUnscopedRef(*item);
2119 2849
    }
2120 2850
    match ty {
2121 -
        case Type::Array(array) => return containsRef(*array.item),
2122 -
        case Type::Optional(inner) => return containsRef(*inner),
2851 +
        case Type::Cell { payload, .. } => return containsUnscopedRef(*payload),
2852 +
        case Type::Array(array) => return containsUnscopedRef(*array.item),
2853 +
        case Type::Optional(inner) => return containsUnscopedRef(*inner),
2854 +
        case Type::Fn(info) => return info.regions <> nil,
2123 2855
        // Nominal declarations validate their own fields and variants.
2124 2856
        // Treating them as leaves also terminates recursive pointer types.
2125 2857
        case Type::Nominal(_) => return false,
2126 2858
        else => return false,
2127 2859
    }
2128 2860
}
2129 2861
2130 2862
/// Return whether `ty` may be duplicated implicitly.
2131 2863
export unsafe fn isCopy(ty: Type) -> bool {
2132 2864
    match ty {
2865 +
        case Type::Session(_) => return false,
2133 2866
        case Type::Pointer { class, mutable, .. } =>
2134 2867
            return class == types::PointerClass::Unsafe or not mutable,
2135 2868
        case Type::Slice { class, mutable, .. } =>
2136 2869
            return class == types::PointerClass::Unsafe or not mutable,
2137 2870
        case Type::TraitObject { class, mutable, .. } =>
2138 2871
            return class == types::PointerClass::Unsafe or not mutable,
2139 2872
        case Type::Array(array) => return isCopy(*array.item),
2140 2873
        case Type::Optional(inner) => return isCopy(*inner),
2141 2874
        case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy,
2142 2875
        case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy,
2143 -
        case Type::Nominal(NominalType::Placeholder(_)) => return false,
2876 +
        case Type::Nominal(NominalType::Application(applied)) => return isCopy(Type::Nominal(applied.base)),
2877 +
        case Type::Nominal(NominalType::Placeholder(_)), Type::Nominal(NominalType::Resolving(_)) => return false,
2144 2878
        else => return true,
2145 2879
    }
2146 2880
}
2147 2881
2148 2882
/// Return whether a type must be consumed exactly once.
2149 2883
export unsafe fn isLinear(ty: Type) -> bool {
2150 2884
    match ty {
2885 +
        case Type::Nominal(NominalType::Application(applied)) => return isLinear(Type::Nominal(applied.base)),
2151 2886
        case Type::Array(array) => return isLinear(*array.item),
2152 2887
        case Type::Optional(inner) => return isLinear(*inner),
2153 2888
        case Type::Nominal(NominalType::Record(recInfo)) => {
2154 2889
            if recInfo.declaredLinear {
2155 2890
                return true;
2182 2917
}
2183 2918
2184 2919
/// Return whether `ty` is a direct unsafe pointer-like value.
2185 2920
fn isUnsafePointerType(ty: Type) -> bool {
2186 2921
    match ty {
2187 -
        case Type::Pointer { class: types::PointerClass::Unsafe, .. },
2922 +
        case Type::Cell { class: types::PointerClass::Unsafe, .. },
2923 +
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
2188 2924
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2189 2925
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
2190 2926
        else => return false,
2191 2927
    }
2192 2928
}
2267 3003
    return false;
2268 3004
}
2269 3005
2270 3006
/// Check if the `from` type is assignable to the `to` type, and return a
2271 3007
/// coercion plan if so, or throw an error if not.
2272 -
unsafe fn expectAssignable(self: &mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
3008 +
unsafe fn expectAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
3009 +
    let from = assignableValueType(self, site, source);
2273 3010
    if isRefType(to) and isUnsafePointerType(from) {
2274 3011
        try requireUnsafe(self, site);
2275 3012
    }
2276 3013
    // Ensure any nested nominal types are resolved before checking assignability.
2277 3014
    try ensureTypeResolved(self, to, site);
2283 3020
        actual: from,
2284 3021
    });
2285 3022
}
2286 3023
2287 3024
/// Check that a type is optional, otherwise throw an error.
2288 -
unsafe fn checkOptional(self: &mut Resolver, node: *ast::Node) -> *Type
3025 +
unsafe fn checkOptional 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *Type
2289 3026
    throws (ResolveError)
2290 3027
{
2291 3028
    if let case Type::Optional(inner) = try infer(self, node) {
2292 3029
        return inner;
2293 3030
    }
2294 3031
    throw emitError(self, node, ErrorKind::ExpectedOptional);
2295 3032
}
2296 3033
2297 3034
/// Check that a node's type is equal to the expected type.
2298 -
unsafe fn checkEqual(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type
3035 +
unsafe fn checkEqual 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type
2299 3036
    throws (ResolveError)
2300 3037
{
2301 3038
    let actualTy = try visit(self, node, expected);
2302 3039
    if actualTy <> expected {
2303 3040
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy });
2304 3041
    }
2305 3042
    return actualTy;
2306 3043
}
2307 3044
2308 3045
/// Bind an identifier in the given scope.
2309 -
unsafe fn bindIdent(
2310 -
    self: &mut Resolver,
3046 +
unsafe fn bindIdent 'arena (
3047 +
    self: &mut Resolver 'arena,
2311 3048
    name: *[u8],
2312 3049
    owner: *ast::Node,
2313 3050
    data: SymbolData,
2314 3051
    attrs: u32,
2315 3052
    scope: *unsafe mut Scope
2320 3057
2321 3058
    return sym;
2322 3059
}
2323 3060
2324 3061
/// Add a symbol to the given scope.
2325 -
unsafe fn addSymbolToScope(self: &mut Resolver, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) {
3062 +
unsafe fn addSymbolToScope 'arena (self: &mut Resolver 'arena, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) {
2326 3063
    for i in 0..scope.symbolsLen {
2327 3064
        if scope.symbols[i].name == sym.name {
2328 3065
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
2329 3066
        }
2330 3067
    }
2342 3079
    set scope.symbolsLen += 1;
2343 3080
}
2344 3081
2345 3082
/// Bind a value identifier in the current scope.
2346 3083
/// Returns `nil` if the identifier is a placeholder (`_`).
2347 -
unsafe fn bindValueIdent(
2348 -
    self: &mut Resolver,
3084 +
unsafe fn bindValueIdent 'arena (
3085 +
    self: &mut Resolver 'arena,
2349 3086
    ident: *ast::Node,
2350 3087
    owner: *ast::Node,
2351 3088
    type: Type,
2352 3089
    mutable: bool,
2353 3090
    alignment: u32,
2370 3107
    }
2371 3108
    return sym;
2372 3109
}
2373 3110
2374 3111
/// Bind a constant identifier in the current scope.
2375 -
unsafe fn bindConstIdent(
2376 -
    self: &mut Resolver,
3112 +
unsafe fn bindConstIdent 'arena (
3113 +
    self: &mut Resolver 'arena,
2377 3114
    ident: *ast::Node,
2378 3115
    owner: *ast::Node,
2379 3116
    type: Type,
2380 3117
    val: ?ConstValue,
2381 3118
    attrs: u32
2391 3128
}
2392 3129
2393 3130
/// Bind a module identifier in the given scope.
2394 3131
/// This is used when declaring modules with `mod` or
2395 3132
/// importing modules with `use`.
2396 -
unsafe fn bindModuleIdent(
2397 -
    self: &mut Resolver,
3133 +
unsafe fn bindModuleIdent 'arena (
3134 +
    self: &mut Resolver 'arena,
2398 3135
    entry: *module::ModuleEntry,
2399 3136
    scope: *unsafe mut Scope,
2400 3137
    owner: *ast::Node,
2401 3138
    attrs: u32,
2402 3139
    bindingScope: *unsafe mut Scope
2406 3143
2407 3144
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
2408 3145
}
2409 3146
2410 3147
/// Bind a type identifier in the current scope.
2411 -
unsafe fn bindTypeIdent(
2412 -
    self: &mut Resolver,
3148 +
unsafe fn bindTypeIdent 'arena (
3149 +
    self: &mut Resolver 'arena,
2413 3150
    ident: *ast::Node,
2414 3151
    owner: *ast::Node,
2415 3152
    type: *unsafe mut NominalType,
2416 3153
    attrs: u32
2417 3154
) -> *unsafe mut Symbol throws (ResolveError) {
2493 3230
}
2494 3231
2495 3232
/// Flatten an identifier or scope access chain into an array of name segments.
2496 3233
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
2497 3234
/// Return the number of segments written to the buffer.
2498 -
unsafe fn flattenPath(
2499 -
    self: &mut Resolver,
3235 +
unsafe fn flattenPath 'arena (
3236 +
    self: &mut Resolver 'arena,
2500 3237
    node: *ast::Node,
2501 3238
    buf: &mut [*[u8]]
2502 3239
) -> u32 throws (ResolveError) {
2503 3240
    let mut out: u32 = 0;
2504 3241
2548 3285
    }
2549 3286
}
2550 3287
2551 3288
/// Get the parent module scope for the current module.
2552 3289
/// Returns the scope of the parent module, or `nil` if this is a root module.
2553 -
unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) {
3290 +
unsafe fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) {
2554 3291
    let currentMod = module::get(self.moduleGraph, self.currentMod)
2555 3292
        else throw emitError(self, node, ErrorKind::Internal);
2556 3293
    let parentId = currentMod.parent
2557 3294
        else return nil; // No parent module.
2558 3295
2559 3296
    return self.moduleScopes[parentId as u32];
2560 3297
}
2561 3298
2562 3299
/// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`).
2563 3300
/// Returns the parent scope and the original node so `flattenPath` can strip `super`.
2564 -
unsafe fn checkSuperAccess(
2565 -
    self: &mut Resolver,
3301 +
unsafe fn checkSuperAccess 'arena (
3302 +
    self: &mut Resolver 'arena,
2566 3303
    node: *ast::Node
2567 3304
) -> ?SuperAccessResult throws (ResolveError) {
2568 3305
    // TODO: Maybe we should deal with `super` after the path is flattened.
2569 3306
    if let case ast::NodeValue::ScopeAccess(access) = node.value {
2570 3307
        // Direct super access: `super::x`.
2604 3341
    return symModuleId == currentModuleId;
2605 3342
}
2606 3343
2607 3344
/// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol,
2608 3345
/// starting from the given scope.
2609 -
unsafe fn resolveAccess(
2610 -
    self: &mut Resolver,
3346 +
unsafe fn resolveAccess 'arena (
3347 +
    self: &mut Resolver 'arena,
2611 3348
    node: *ast::Node,
2612 3349
    access: ast::Access,
2613 3350
    scope: *unsafe Scope
2614 3351
) -> *unsafe mut Symbol throws (ResolveError) {
3352 +
    if let case ast::NodeValue::RegionApply { .. } = access.parent.value {
3353 +
        let ty = try infer(self, access.parent);
3354 +
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3355 +
        try ensureNominalResolved(self, info, access.parent);
3356 +
        let case NominalType::Union(body) = *info else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3357 +
        let name = try nodeName(self, access.child);
3358 +
        let symbol = try resolveUnionVariantAccess(self, node, access, body, name);
3359 +
        setNodeType(self, node, ty);
3360 +
        return symbol;
3361 +
    }
2615 3362
    // Handle `super` access by adjusting scope and node.
2616 3363
    let mut startScope = scope;
2617 3364
    let mut pathNode = node;
2618 3365
    if let superAccess = try checkSuperAccess(self, node) {
2619 3366
        set startScope = superAccess.scope;
2627 3374
    return try resolvePath(self, node, access, &buffer[..pathLen], startScope);
2628 3375
}
2629 3376
2630 3377
/// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol,
2631 3378
/// starting from the given scope.
2632 -
unsafe fn resolvePath(
2633 -
    self: &mut Resolver,
3379 +
unsafe fn resolvePath 'arena (
3380 +
    self: &mut Resolver 'arena,
2634 3381
    node: *ast::Node,
2635 3382
    access: ast::Access,
2636 3383
    path: &[*[u8]],
2637 3384
    scope: *unsafe Scope
2638 3385
) -> *unsafe mut Symbol throws (ResolveError) {
2678 3425
    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
2679 3426
}
2680 3427
2681 3428
/// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope.
2682 3429
/// This traverses the module hierarchy, checking visibility at each step.
2683 -
unsafe fn resolveModulePath(
2684 -
    self: &mut Resolver,
3430 +
unsafe fn resolveModulePath 'arena (
3431 +
    self: &mut Resolver 'arena,
2685 3432
    module: *ast::Node
2686 3433
) -> ResolvedModule throws (ResolveError) {
2687 3434
    let mut startScope = self.scope;
2688 3435
    let mut pathNode = module;
2689 3436
2709 3456
2710 3457
    return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
2711 3458
}
2712 3459
2713 3460
/// Recursively resolve the remaining path segments by traversing child modules.
2714 -
unsafe fn resolveModulePathRecursive(
2715 -
    self: &mut Resolver,
3461 +
unsafe fn resolveModulePathRecursive 'arena (
3462 +
    self: &mut Resolver 'arena,
2716 3463
    node: *ast::Node,
2717 3464
    path: &[*[u8]],
2718 3465
    sym: *unsafe Symbol
2719 3466
) -> ResolvedModule throws (ResolveError) {
2720 3467
    let case SymbolData::Module { entry, scope } = sym.data
2737 3484
        childSym
2738 3485
    );
2739 3486
}
2740 3487
2741 3488
/// Resolve a type name, which could be an identifier or scoped path.
2742 -
unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) {
3489 +
unsafe fn resolveTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) {
2743 3490
    match node.value {
2744 3491
        case ast::NodeValue::Ident(name) => {
2745 3492
            let sym = findTypeSymbol(self.scope, name)
2746 3493
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2747 3494
            let case SymbolData::Type(ty) = sym.data
2768 3515
/// Visit a top-level declaration in the declaration phase.
2769 3516
/// This binds all names and analyzes signatures, types, and initializers.
2770 3517
/// Function bodies are deferred to the definition phase.
2771 3518
///
2772 3519
/// Nb. User-defined types are already handled by this point.
2773 -
unsafe fn visitDecl(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
3520 +
unsafe fn visitDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
2774 3521
    match node.value {
2775 3522
        case ast::NodeValue::FnDecl(_),
2776 3523
             ast::NodeValue::ConstDecl(_),
2777 3524
             ast::NodeValue::Mod(_),
2778 3525
             ast::NodeValue::Use(_) => {
2779 3526
            // Handled in previous passes.
2780 3527
        }
2781 3528
        case ast::NodeValue::StaticDecl(_) => {
2782 3529
            try infer(self, node);
2783 3530
        }
2784 -
        case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => {
2785 -
            try resolveInstanceDecl(self, node, traitName, targetType, methods);
3531 +
        case ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => {
3532 +
            try resolveInstanceDecl(self, node, traitName, targetType, regions, methods);
2786 3533
        }
2787 -
        case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, attrs } => {
2788 -
            try resolveMethodDecl(self, node, name, receiverName, receiverType, sig, attrs);
3534 +
        case ast::NodeValue::MethodDecl {
3535 +
            ..
3536 +
        } => {
3537 +
            try resolveMethodDecl(self, node);
2789 3538
        }
2790 3539
        else => {
2791 3540
            // Ignore non-declaration nodes.
2792 3541
        }
2793 3542
    }
2794 3543
}
2795 3544
2796 3545
/// Require an unsafe function or block.
2797 -
unsafe fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
3546 +
unsafe fn requireUnsafe 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
2798 3547
    if not self.inUnsafeContext {
2799 3548
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2800 3549
    }
2801 3550
}
2802 3551
2803 3552
/// Require an unsafe context for any access to an unsafe static.
2804 -
unsafe fn checkStaticAccess(self: &mut Resolver, node: *ast::Node, sym: &Symbol)
3553 +
unsafe fn checkStaticAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, sym: &Symbol)
2805 3554
    throws (ResolveError)
2806 3555
{
2807 3556
    if let case ast::NodeValue::StaticDecl(_) = sym.node.value {
2808 3557
        if ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) {
2809 3558
            try requireUnsafe(self, node);
2810 3559
        }
2811 3560
    }
2812 3561
}
2813 3562
2814 3563
/// Reject calls from safe code through unsafe function types.
2815 -
unsafe fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType)
3564 +
unsafe fn checkUnsafeCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: *FnType)
2816 3565
    throws (ResolveError)
2817 3566
{
2818 3567
    if info.isUnsafe and not self.inUnsafeContext {
2819 3568
        throw emitError(self, node, ErrorKind::UnsafeCall);
2820 3569
    }
2821 3570
}
2822 3571
2823 3572
/// Visit a top-level definition, recursing into sub-modules.
2824 -
unsafe fn visitDef(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
3573 +
unsafe fn visitDef 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
2825 3574
    match node.value {
2826 3575
        case ast::NodeValue::FnDecl(decl) => {
2827 3576
            try resolveFnDeclBody(self, node, decl) catch {
2828 3577
                return;
2829 3578
            };
2830 3579
        }
2831 3580
        case ast::NodeValue::Mod(decl) => {
2832 -
            if not shouldAnalyzeModule(self, decl.attrs) {
3581 +
            let modName = try nodeName(self, decl.name);
3582 +
            if not shouldAnalyzeModule(self, decl.attrs, modName) {
2833 3583
                return;
2834 3584
            }
2835 -
            let modName = try nodeName(self, decl.name);
2836 3585
            let submod = try enterSubModule(self, modName, node);
2837 3586
            let case ast::NodeValue::Block(block) = submod.root.value
2838 3587
                else panic "visitDef: expected block for module root";
2839 3588
            try resolveModuleDefs(self, &block) catch e {
2840 3589
                exitModuleScope(self, submod);
2849 3598
            // Skip: already analyzed in declaration phase.
2850 3599
        }
2851 3600
        case ast::NodeValue::InstanceDecl { methods, .. } => {
2852 3601
            try resolveInstanceMethodBodies(self, methods);
2853 3602
        }
2854 -
        case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } => {
2855 -
            try resolveMethodBody(self, node, receiverName, sig, body);
3603 +
        case ast::NodeValue::MethodDecl {
3604 +
            ..
3605 +
        } => {
3606 +
            try resolveMethodBody(self, node);
2856 3607
        }
2857 3608
        else => {
2858 3609
            // FIXME: This allows module-level statements that should
2859 3610
            // normally only be valid inside function bodies. We currently
2860 3611
            // need this because of how tests are written, but it should
2865 3616
        }
2866 3617
    }
2867 3618
}
2868 3619
2869 3620
/// Try to infer a node's type.
2870 -
unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
3621 +
unsafe fn infer 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2871 3622
    return try visit(self, node, Type::Unknown);
2872 3623
}
2873 3624
2874 -
/// Reject nested references while allowing a direct parameter or local reference.
2875 -
unsafe fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type)
3625 +
/// Permit named reference dependencies and direct call-scoped or local references.
3626 +
unsafe fn validateValueTypeReferences 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
2876 3627
    throws (ResolveError)
2877 3628
{
3629 +
    try validateRegionDependencies(self, node, ty);
3630 +
    if let case Type::Fn(info) = ty; info.regions == nil {
3631 +
        return;
3632 +
    }
2878 3633
    if isRefType(ty) {
2879 3634
        if let case Type::Pointer { target, .. } = ty {
2880 -
            if containsRef(*target) {
3635 +
            if containsUnscopedRef(*target) {
2881 3636
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2882 3637
            }
2883 3638
        } else if let case Type::Slice { item, .. } = ty {
2884 -
            if containsRef(*item) {
3639 +
            if containsUnscopedRef(*item) {
2885 3640
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2886 3641
            }
2887 3642
        }
2888 -
    } else if containsRef(ty) {
3643 +
    } else if containsUnscopedRef(ty) {
2889 3644
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2890 3645
    }
2891 3646
}
2892 3647
2893 3648
/// Require a type that may be stored or escape a call.
2894 -
unsafe fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type)
3649 +
unsafe fn ensureStorableType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
2895 3650
    throws (ResolveError)
2896 3651
{
2897 -
    if containsRef(ty) {
3652 +
    try validateRegionDependencies(self, node, ty);
3653 +
    if containsUnscopedRef(ty) {
2898 3654
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2899 3655
    }
2900 3656
}
2901 3657
2902 3658
/// Resolve a type signature node.
2903 -
unsafe fn resolveValueType(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
3659 +
unsafe fn resolveValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2904 3660
    let ty = try visit(self, node, Type::Unknown);
3661 +
    if let case Type::Nominal(info) = ty {
3662 +
        try requireNominalArguments(self, info, node);
3663 +
    }
2905 3664
    // Opaque value types are not allowed.
2906 3665
    if ty == Type::Opaque {
2907 3666
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
2908 3667
    }
2909 3668
    try validateValueTypeReferences(self, node, ty);
2910 3669
    return ty;
2911 3670
}
2912 3671
2913 3672
/// Analyze a node's type and check that it can be assigned to the expected type.
2914 -
unsafe fn checkAssignable(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
3673 +
unsafe fn checkAssignable 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2915 3674
    let actual = try visit(self, node, expected);
2916 3675
    let _ = try expectAssignable(self, expected, actual, node);
3676 +
    if isRefType(expected) and isMutablePointerLike(expected) and isMutablePointerLike(actual) {
3677 +
        if not try canMutateThrough(self, node) {
3678 +
            throw emitError(self, node, ErrorKind::ImmutableBinding);
3679 +
        }
3680 +
    }
2917 3681
    return actual;
2918 3682
}
2919 3683
2920 3684
/// Analyze a node and propagate the resolved type.
2921 3685
/// The `hint` parameter provides type context for inference and validation.
2922 3686
/// When `nil`, the type must be inferred from the expression itself.
2923 -
unsafe fn visit(self: &mut Resolver, node: *ast::Node, hint: Type) -> Type
3687 +
unsafe fn visit 'arena (self: &mut Resolver 'arena, node: *ast::Node, hint: Type) -> Type
2924 3688
    throws (ResolveError)
2925 3689
{
2926 3690
    if let ty = typeFor(self, node) {
2927 -
        return ty;
3691 +
        // An optional context completes a nil expression's storage type.
3692 +
        if ty <> Type::Nil or not isOptionalType(hint) {
3693 +
            return ty;
3694 +
        }
2928 3695
    }
2929 3696
    match node.value {
2930 3697
        case ast::NodeValue::Ident(name) => {
2931 3698
            let sym = findAnySymbol(self.scope, name)
2932 3699
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2940 3707
                        setNodeConstValue(self, node, val);
2941 3708
                    }
2942 3709
                    return setNodeType(self, node, type);
2943 3710
                },
2944 3711
                case SymbolData::Type(t) =>
2945 -
                    return setNodeType(self, node, Type::Nominal(t)),
3712 +
                    return setNodeType(self, node, Type::Nominal(hintedNominal(t, hint))),
2946 3713
                case SymbolData::Variant { .. } =>
2947 3714
                    return Type::Void,
2948 3715
                case SymbolData::Module { .. } =>
2949 3716
                    throw emitError(self, node, ErrorKind::UnexpectedModuleName),
2950 3717
                case SymbolData::Trait(_) =>
2951 3718
                    throw emitError(self, node, ErrorKind::UnexpectedTraitName),
2952 3719
            }
2953 3720
        },
2954 -
        case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal),
3721 +
        case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal, hint),
2955 3722
        case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access),
2956 3723
        case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop),
3724 +
        case ast::NodeValue::RegionBlock { region, bindings, body, isSession } => {
3725 +
            if isSession {
3726 +
                return try resolveSessionBlock(self, node, region, bindings, body);
3727 +
            }
3728 +
            return try resolveBorrowBlock(self, node, region, bindings, body);
3729 +
        }
2957 3730
        case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block),
2958 3731
        case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl),
2959 3732
        case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic(
2960 3733
            self, node, decl.ident, decl.type, decl.value, decl.attrs, true
2961 3734
        ),
2962 3735
        case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic(
2963 3736
            self, node, decl.ident, decl.type, decl.value, decl.attrs, false
2964 3737
        ),
2965 3738
        case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param),
2966 3739
        case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond),
2967 -
        case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond),
3740 +
        case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond, hint),
2968 3741
        case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond),
2969 3742
        case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode),
2970 3743
        case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode),
2971 3744
        case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode),
2972 3745
        case ast::NodeValue::Loop { body } => {
2991 3764
        case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign),
2992 3765
        case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint),
2993 3766
        case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint),
2994 3767
        case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint),
2995 3768
        case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index),
2996 -
        case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access),
3769 +
        case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access, hint),
2997 3770
        case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint),
2998 3771
        case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint),
2999 3772
        case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr),
3000 3773
        case ast::NodeValue::Range(range) => return try resolveRange(self, node, range),
3001 3774
        case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint),
3022 3795
        case ast::NodeValue::ExprStmt(expr) => {
3023 3796
            // Pass `Void` as expected type to indicate value is discarded.
3024 3797
            let exprTy = try visit(self, expr, Type::Void);
3025 3798
            return setNodeType(self, node, Type::Never if exprTy == Type::Never else Type::Void);
3026 3799
        },
3800 +
        case ast::NodeValue::RegionApply { value, regions } =>
3801 +
            return try resolveRegionApply(self, node, value, regions),
3027 3802
        case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig),
3028 3803
        case ast::NodeValue::Super => {
3029 3804
            // `super` by itself is invalid, must be used in scope access.
3030 3805
            throw emitError(self, node, ErrorKind::InvalidModulePath);
3031 3806
        },
3075 3850
        }
3076 3851
    }
3077 3852
}
3078 3853
3079 3854
/// Visit an optional node when present.
3080 -
unsafe fn visitOptional(self: &mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type
3855 +
unsafe fn visitOptional 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, hint: Type) -> ?Type
3081 3856
    throws (ResolveError)
3082 3857
{
3083 3858
    if let n = node {
3084 3859
        return try visit(self, n, hint);
3085 3860
    }
3086 3861
    return nil;
3087 3862
}
3088 3863
3089 3864
/// Visit every node contained in a list, returning the last resolved type.
3090 -
unsafe fn visitList(self: &mut Resolver, list: *[*ast::Node]) -> Type
3865 +
unsafe fn visitList 'arena (self: &mut Resolver 'arena, list: *[*ast::Node]) -> Type
3091 3866
    throws (ResolveError)
3092 3867
{
3093 3868
    let mut diverges = false;
3094 3869
    for item in list {
3095 3870
        if try infer(self, item) == Type::Never {
3101 3876
    }
3102 3877
    return Type::Void;
3103 3878
}
3104 3879
3105 3880
/// Collect attribute flags applied to a declaration.
3106 -
fn resolveAttributes(self: &mut Resolver, attrs: ?ast::Attributes) -> u32 {
3881 +
fn resolveAttributes(attrs: ?ast::Attributes) -> u32 {
3107 3882
    let list = attrs else return 0;
3108 -
    let attrNodes = list.list;
3109 3883
    let mut mask: u32 = 0;
3110 3884
3111 -
    for node in attrNodes {
3885 +
    for node in list.list {
3112 3886
        let case ast::NodeValue::Attribute(attr) = node.value
3113 3887
            else panic "resolveAttributes: invalid attribute node";
3114 3888
        set mask |= (attr as u32);
3115 3889
    }
3116 3890
    return mask;
3117 3891
}
3118 3892
3119 3893
/// Ensure the `default` attribute is only applied to functions.
3120 -
unsafe fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32)
3894 +
unsafe fn ensureDefaultAttrNotAllowed 'arena (self: &mut Resolver 'arena, node: *ast::Node, attrs: u32)
3121 3895
    throws (ResolveError)
3122 3896
{
3123 3897
    let defaultBit = ast::Attribute::Default as u32;
3124 3898
    if (attrs & defaultBit) <> 0 {
3125 3899
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
3126 3900
    }
3127 3901
}
3128 3902
3129 3903
/// Analyze a block node, allocating a nested lexical scope.
3130 -
unsafe fn resolveBlock(self: &mut Resolver, node: *ast::Node, block: ast::Block) -> Type
3904 +
unsafe fn resolveBlock 'arena (self: &mut Resolver 'arena, node: *ast::Node, block: ast::Block) -> Type
3131 3905
    throws (ResolveError)
3132 3906
{
3133 3907
    enterScope(self, node);
3134 3908
    let wasUnsafe = self.inUnsafeContext;
3135 3909
    set self.inUnsafeContext = wasUnsafe or block.isUnsafe;
3145 3919
    set self.inUnsafeContext = wasUnsafe;
3146 3920
3147 3921
    return setNodeType(self, node, blockTy);
3148 3922
}
3149 3923
3150 -
/// Analyze a `let` declaration and bind its identifier.
3151 -
unsafe fn resolveLet(self: &mut Resolver, node: *ast::Node, decl: ast::Let) -> Type
3924 +
/// Introduce a concrete region under an explicit parent or enclosing region block.
3925 +
unsafe fn borrowRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *RegionScope throws (ResolveError) {
3926 +
    let case ast::NodeValue::Region { name, parent } = node.value
3927 +
        else panic "borrowRegion: invalid region node";
3928 +
    if findRegion(self.regionScope, name) <> nil {
3929 +
        throw emitError(self, node, ErrorKind::DuplicateBinding(name));
3930 +
    }
3931 +
    let mut enclosing: ?*unsafe types::Region = nil;
3932 +
    let mut current = self.regionScope;
3933 +
    while let scope = current {
3934 +
        for region in scope.entries {
3935 +
            if region.origin == types::RegionOrigin::Block {
3936 +
                set enclosing = region;
3937 +
                break;
3938 +
            }
3939 +
        }
3940 +
        if enclosing <> nil {
3941 +
            break;
3942 +
        }
3943 +
        set current = scope.parent;
3944 +
    }
3945 +
    if let parentNode = parent {
3946 +
        set enclosing = try resolveRegion(self, parentNode);
3947 +
    }
3948 +
    let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
3949 +
        as *unsafe mut types::Region;
3950 +
    set *region = types::Region { id: node.id, origin: types::RegionOrigin::Block, name, parent: enclosing };
3951 +
    let entries = try! alloc::allocRawSlice(
3952 +
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1
3953 +
    ) as *unsafe mut [*unsafe mut types::Region];
3954 +
    set entries[0] = region;
3955 +
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
3956 +
    set *scope = RegionScope { entries, parent: self.regionScope };
3957 +
    return scope;
3958 +
}
3959 +
3960 +
/// Qualify an existing-place borrow with its block's region.
3961 +
unsafe fn qualifyBlockBorrow 'arena (
3962 +
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, region: *unsafe types::Region
3963 +
) -> Type throws (ResolveError) {
3964 +
    let mut class = types::PointerClass::Ref;
3965 +
    match ty {
3966 +
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
3967 +
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
3968 +
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
3969 +
        else => throw emitError(self, node, ErrorKind::RefBinding),
3970 +
    }
3971 +
    if let case types::PointerClass::Region(source) = class {
3972 +
        if not types::regionContains(source, region) {
3973 +
            throw emitError(self, node, ErrorKind::RegionParent(region.name));
3974 +
        }
3975 +
    }
3976 +
    match ty {
3977 +
        case Type::Pointer { target, mutable, .. } =>
3978 +
            return Type::Pointer { class: types::PointerClass::Region(region), target, mutable },
3979 +
        case Type::Slice { item, mutable, .. } =>
3980 +
            return Type::Slice { class: types::PointerClass::Region(region), item, mutable },
3981 +
        else => throw emitError(self, node, ErrorKind::RefBinding),
3982 +
    }
3983 +
}
3984 +
3985 +
/// Check source places before publishing the region's bindings to its body.
3986 +
unsafe fn resolveBorrowBlock 'arena (
3987 +
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
3988 +
    bindings: *[*ast::Node], body: *ast::Node
3989 +
) -> Type throws (ResolveError) {
3990 +
    if self.currentFn == nil {
3991 +
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
3992 +
    }
3993 +
    let scope = try borrowRegion(self, regionNode);
3994 +
    let region = scope.entries[0];
3995 +
    for bindingNode in bindings {
3996 +
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
3997 +
            else panic "resolveBorrowBlock: invalid binding node";
3998 +
        let case ast::NodeValue::AddressOf(address) = binding.value.value
3999 +
            else panic "resolveBorrowBlock: invalid source node";
4000 +
        let ty = try infer(self, binding.value);
4001 +
        if not ast::isPlaceExpr(address.target) or borrowPlace(self, address.target).root == nil {
4002 +
            throw emitError(self, binding.value, ErrorKind::RefBinding);
4003 +
        }
4004 +
        let qualified = try qualifyBlockBorrow(self, binding.value, ty, region);
4005 +
        setNodeType(self, binding.value, qualified);
4006 +
    }
4007 +
    let previous = self.regionScope;
4008 +
    set self.regionScope = scope;
4009 +
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4010 +
    enterScope(self, node);
4011 +
    let result = try resolveBorrowBody(self, bindings, body) catch error {
4012 +
        exitScope(self);
4013 +
        set self.regionScope = previous;
4014 +
        throw error;
4015 +
    };
4016 +
    exitScope(self);
4017 +
    set self.regionScope = previous;
4018 +
    return setNodeType(self, node, result);
4019 +
}
4020 +
4021 +
/// Bind a checked region's source references and resolve its statement body.
4022 +
unsafe fn resolveBorrowBody 'arena (self: &mut Resolver 'arena, bindings: *[*ast::Node], body: *ast::Node) -> Type
4023 +
    throws (ResolveError)
4024 +
{
4025 +
    for bindingNode in bindings {
4026 +
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4027 +
            else panic "resolveBorrowBody: invalid binding node";
4028 +
        try resolveLet(self, bindingNode, ast::borrowBinding(binding));
4029 +
    }
4030 +
    return try infer(self, body);
4031 +
}
4032 +
4033 +
/// Find a declaration by spelling when the name is not interned.
4034 +
unsafe fn findSpelledSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
4035 +
    for i in 0..scope.symbolsLen {
4036 +
        let symbol = scope.symbols[i];
4037 +
        if mem::eq(symbol.name, name) {
4038 +
            return symbol;
4039 +
        }
4040 +
    }
4041 +
    return nil;
4042 +
}
4043 +
4044 +
/// Look up a compiler-known declaration in the standard allocation module.
4045 +
unsafe fn allocationSymbol 'arena (self: &Resolver 'arena, name: *[u8]) -> ?*unsafe mut Symbol {
4046 +
    let mut scope = self.pkgScope;
4047 +
    for segment in ["std", "lang", "alloc"] {
4048 +
        let symbol = findSpelledSymbol(scope, segment) else return nil;
4049 +
        let case SymbolData::Module { scope: child, .. } = symbol.data else return nil;
4050 +
        set scope = child;
4051 +
    }
4052 +
    return findSpelledSymbol(scope, name);
4053 +
}
4054 +
4055 +
/// Bind an allocation interface while retaining the source arena until region exit.
4056 +
unsafe fn resolveSessionBlock 'arena (
4057 +
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
4058 +
    bindings: *[*ast::Node], body: *ast::Node
4059 +
) -> Type throws (ResolveError) {
4060 +
    if self.currentFn == nil or bindings.len <> 1 {
4061 +
        throw emitError(self, node, ErrorKind::InvalidSessionSource);
4062 +
    }
4063 +
    let bindingNode = bindings[0];
4064 +
    let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4065 +
        else panic "resolveSessionBlock: invalid binding";
4066 +
    let case ast::NodeValue::AddressOf(address) = binding.value.value
4067 +
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4068 +
    let ty = try infer(self, binding.value);
4069 +
    let case Type::Pointer { target, .. } = ty
4070 +
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4071 +
    let allocTrait = allocationSymbol(self, "Alloc")
4072 +
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4073 +
    let case SymbolData::Trait(allocInfo) = allocTrait.data
4074 +
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4075 +
    let allocModule = moduleIdForSymbol(self, allocTrait)
4076 +
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4077 +
    let mut allocInstance: ?*unsafe InstanceEntry = nil;
4078 +
    for i in 0..self.instancesLen {
4079 +
        let candidate: *unsafe InstanceEntry = &self.instances[i];
4080 +
        if candidate.traitType.moduleId == allocModule and mem::eq(candidate.traitType.name, allocInfo.name)
4081 +
            and erasedTypesEqual(candidate.concreteType, *target)
4082 +
        {
4083 +
            set allocInstance = candidate;
4084 +
            break;
4085 +
        }
4086 +
    }
4087 +
    let selected = allocInstance
4088 +
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4089 +
    if not address.mutable or not ast::isPlaceExpr(address.target)
4090 +
        or borrowPlace(self, address.target).root == nil
4091 +
    {
4092 +
        throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4093 +
    }
4094 +
    let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject {
4095 +
        traitInfo: allocInfo, inst: selected,
4096 +
    });
4097 +
    let scope = try borrowRegion(self, regionNode);
4098 +
    let region = scope.entries[0];
4099 +
    if let sourceRegion = referenceRegion(ty) {
4100 +
        set region.parent = sourceRegion;
4101 +
    }
4102 +
    setNodeType(self, binding.value, try qualifyBlockBorrow(self, binding.value, ty, region));
4103 +
    let previous = self.regionScope;
4104 +
    set self.regionScope = scope;
4105 +
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4106 +
    enterScope(self, node);
4107 +
    let result = try resolveSessionBody(self, bindingNode, binding, region, body) catch error {
4108 +
        exitScope(self);
4109 +
        set self.regionScope = previous;
4110 +
        throw error;
4111 +
    };
4112 +
    exitScope(self);
4113 +
    set self.regionScope = previous;
4114 +
    return setNodeType(self, node, result);
4115 +
}
4116 +
4117 +
/// Introduce the opaque session value and check its body.
4118 +
unsafe fn resolveSessionBody 'arena (
4119 +
    self: &mut Resolver 'arena, node: *ast::Node, binding: ast::Arg,
4120 +
    region: *unsafe types::Region, body: *ast::Node
4121 +
) -> Type throws (ResolveError) {
4122 +
    let ident = binding.label else panic "resolveSessionBody: missing binding name";
4123 +
    let _ = try bindValueIdent(self, ident, node, Type::Session(region), false, 0, 0);
4124 +
    return try infer(self, body);
4125 +
}
4126 +
4127 +
/// Analyze a `let` declaration and bind its identifier.
4128 +
unsafe fn resolveLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Let) -> Type
3152 4129
    throws (ResolveError)
3153 4130
{
3154 4131
    let mut alignment: u32 = 0; // Zero is default.
3155 4132
    let mut bindingTy = Type::Unknown;
3156 4133
    let mut valueTy = Type::Unknown;
3170 4147
    try validateValueTypeReferences(self, node, bindingTy);
3171 4148
    if isRefType(bindingTy) {
3172 4149
        if self.currentFn == nil {
3173 4150
            throw emitError(self, node, ErrorKind::InvalidRefPosition);
3174 4151
        }
3175 -
        if decl.mutable {
4152 +
        if decl.mutable and (referenceRegion(bindingTy) == nil or not isCopy(bindingTy)) {
3176 4153
            throw emitError(self, node, ErrorKind::RefBinding);
3177 4154
        }
3178 4155
    }
3179 4156
    // Variables cannot have void type.
3180 4157
    if bindingTy == Type::Void {
3195 4172
    // Alignment must be zero or a power of two.
3196 4173
    if alignment <> 0 and (alignment & (alignment - 1)) <> 0 {
3197 4174
        throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment));
3198 4175
    }
3199 4176
    let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0);
3200 -
    setNodeType(self, decl.value, bindingTy);
4177 +
4178 +
    // Untyped initializers use the declared storage type.
4179 +
    if not isTypeInferrable(valueTy) {
4180 +
        setNodeType(self, decl.value, bindingTy);
4181 +
    }
3201 4182
3202 4183
    return Type::Never if valueTy == Type::Never else Type::Void;
3203 4184
}
3204 4185
3205 4186
/// Check whether a node is an integer literal, optionally under unary negation.
3215 4196
        else => return false,
3216 4197
    }
3217 4198
}
3218 4199
3219 4200
/// Determine whether a node represents a compile-time constant expression.
3220 -
export unsafe fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool {
4201 +
export unsafe fn isConstExpr 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
3221 4202
    match node.value {
3222 4203
        case ast::NodeValue::Bool(_),
3223 4204
             ast::NodeValue::Char(_),
3224 4205
             ast::NodeValue::Number(_),
3225 4206
             ast::NodeValue::String(_),
3335 4316
            return ConstValue::Int(constIntFromBits(raw, bits, true)),
3336 4317
    }
3337 4318
}
3338 4319
3339 4320
/// Return the constant `u32` value for a slice bound when known.
3340 -
fn constSliceIndex(self: &mut Resolver, node: *ast::Node) -> ?u32 {
4321 +
fn constSliceIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?u32 {
3341 4322
    let value = constValueEntry(self, node)
3342 4323
        else return nil;
3343 4324
    let case ConstValue::Int(int) = value
3344 4325
        else return nil;
3345 4326
    if int.negative {
3353 4334
/// This function ensures that a node represents a valid, non-negative integer constant
3354 4335
/// that fits within a machine word. It is used for contexts requiring compile-time
3355 4336
/// non-negative integers, such as array sizes and alignment specifications.
3356 4337
///
3357 4338
/// Returns the unsigned magnitude of the constant as `u32`.
3358 -
unsafe fn checkSizeInt(self: &mut Resolver, node: *ast::Node) -> u32
4339 +
unsafe fn checkSizeInt 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> u32
3359 4340
    throws (ResolveError)
3360 4341
{
3361 4342
    // First traverse the node expect a numeric type.
3362 4343
    let _ = try checkNumeric(self, node);
3363 4344
3380 4361
3381 4362
/// Check that constructor arguments match record fields.
3382 4363
///
3383 4364
/// Verifies argument count matches field count, and that each argument is
3384 4365
/// assignable to its corresponding field type.
3385 -
unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType)
4366 +
unsafe fn checkRecordConstructorArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType)
3386 4367
    throws (ResolveError)
3387 4368
{
3388 4369
    try checkRecordArity(self, args, recInfo, node);
3389 4370
    for arg, i in args {
3390 4371
        let fieldType = recInfo.fields[i].fieldType;
3391 4372
        try checkAssignable(self, arg, fieldType);
3392 4373
    }
3393 4374
}
3394 4375
3395 4376
/// Check that the argument count of a constructor pattern or call matches the record field count.
3396 -
unsafe fn checkRecordArity(self: &mut Resolver, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
4377 +
unsafe fn checkRecordArity 'arena (self: &mut Resolver 'arena, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3397 4378
    if args.len <> recInfo.fields.len {
3398 4379
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch {
3399 4380
            expected: recInfo.fields.len as u32,
3400 4381
            actual: args.len,
3401 4382
        }));
3402 4383
    }
3403 4384
}
3404 4385
3405 4386
/// Helper for analyzing `constant` and `static` declarations.
3406 -
unsafe fn resolveConstOrStatic(
3407 -
    self: &mut Resolver,
4387 +
unsafe fn resolveConstOrStatic 'arena (
4388 +
    self: &mut Resolver 'arena,
3408 4389
    node: *ast::Node,
3409 4390
    ident: *ast::Node,
3410 4391
    typeNode: *ast::Node,
3411 4392
    valueNode: *ast::Node,
3412 4393
    attrList: ?ast::Attributes,
3413 4394
    isConst: bool
3414 4395
) -> Type throws (ResolveError) {
3415 -
    let attrs = resolveAttributes(self, attrList);
4396 +
    let attrs = resolveAttributes(attrList);
3416 4397
    let bindingTy = try infer(self, typeNode);
4398 +
    if containsRegion(bindingTy) {
4399 +
        throw emitError(self, typeNode, ErrorKind::InvalidRefPosition);
4400 +
    }
3417 4401
    try ensureStorableType(self, typeNode, bindingTy);
3418 4402
    let wasUnsafe = self.inUnsafeContext;
3419 4403
    set self.inUnsafeContext = wasUnsafe or (
3420 4404
        not isConst and ast::hasAttribute(attrs, ast::Attribute::Unsafe)
3421 4405
    );
3446 4430
3447 4431
    return Type::Void;
3448 4432
}
3449 4433
3450 4434
/// Analyze a function declaration signature and bind the function name.
3451 -
unsafe fn resolveFnDecl(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type
4435 +
unsafe fn resolveFnDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
4436 +
    throws (ResolveError)
4437 +
{
4438 +
    let previous = self.regionScope;
4439 +
    set self.regionScope = try bindRegions(self, node, decl.regions);
4440 +
    let result = try resolveFnSignature(self, node, decl) catch error {
4441 +
        set self.regionScope = previous;
4442 +
        throw error;
4443 +
    };
4444 +
    set self.regionScope = previous;
4445 +
    return result;
4446 +
}
4447 +
4448 +
/// Resolve a function signature in its declared region environment.
4449 +
unsafe fn resolveFnSignature 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
3452 4450
    throws (ResolveError)
3453 4451
{
3454 -
    let attrMask = resolveAttributes(self, decl.attrs);
4452 +
    let attrMask = resolveAttributes(decl.attrs);
3455 4453
    let mut retTy = Type::Void;
3456 4454
    if let retNode = decl.sig.returnType {
3457 4455
        set retTy = try infer(self, retNode);
3458 4456
        try ensureStorableType(self, retNode, retTy);
3459 4457
    }
3460 -
    let a = alloc::arenaAllocator(&mut self.arena);
4458 +
    let a = alloc::arenaAllocator(self.arena);
3461 4459
    let mut paramTypes: *mut [*Type] = &mut [];
3462 4460
    let mut throwList: *mut [*Type] = &mut [];
3463 4461
    let mut fnType = FnType {
4462 +
        regions: self.regionScope,
3464 4463
        paramTypes: &[],
3465 4464
        returnType: allocType(self, retTy),
3466 4465
        throwList: &[],
3467 4466
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3468 4467
    };
3494 4493
    for throwNode in decl.sig.throwList {
3495 4494
        let throwTy = try infer(self, throwNode) catch e {
3496 4495
            exitFn(self);
3497 4496
            throw e;
3498 4497
        };
4498 +
        try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
3499 4499
        throwList.append(allocType(self, throwTy), a);
3500 4500
        try ensureStorableType(self, throwNode, throwTy);
3501 4501
    }
3502 4502
    exitFn(self);
3503 4503
    set fnType.paramTypes = &paramTypes[..];
3510 4510
3511 4511
    return ty;
3512 4512
}
3513 4513
3514 4514
/// Analyze a function body.
3515 -
unsafe fn resolveFnDeclBody(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
4515 +
unsafe fn resolveFnDeclBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
3516 4516
    let sym = symbolFor(self, node) else {
3517 4517
        // The function declaration failed to type check, therefore
3518 4518
        // no symbol was associated with it.
3519 4519
        return;
3520 4520
    };
3529 4529
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
3530 4530
        }
3531 4531
        if isExtern {
3532 4532
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
3533 4533
        }
3534 -
        try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body);
4534 +
        let previous = self.regionScope;
4535 +
        set self.regionScope = try bindRegions(self, node, decl.regions);
4536 +
        try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body) catch error {
4537 +
            set self.regionScope = previous;
4538 +
            throw error;
4539 +
        };
4540 +
        set self.regionScope = previous;
3535 4541
    } else if not isExtern {
3536 4542
        throw emitError(self, node, ErrorKind::FnMissingBody);
3537 4543
    }
3538 4544
}
3539 4545
3540 4546
/// Resolve a function or method body and restore the enclosing context.
3541 -
unsafe fn resolveExecutableBody(
3542 -
    self: &mut Resolver,
4547 +
unsafe fn resolveExecutableBody 'arena (
4548 +
    self: &mut Resolver 'arena,
3543 4549
    node: *ast::Node,
3544 4550
    fnType: *FnType,
3545 4551
    receiverName: ?*ast::Node,
3546 4552
    params: *[*ast::Node],
3547 4553
    body: *ast::Node,
3563 4569
    }
3564 4570
}
3565 4571
3566 4572
/// Check parameters, body types, and ownership.
3567 4573
/// Return whether a required return is missing.
3568 -
unsafe fn checkExecutableBody(
3569 -
    self: &mut Resolver,
4574 +
unsafe fn checkExecutableBody 'arena (
4575 +
    self: &mut Resolver 'arena,
3570 4576
    fnType: *FnType,
3571 4577
    receiverName: ?*ast::Node,
3572 4578
    params: *[*ast::Node],
3573 4579
    body: *ast::Node,
3574 4580
) -> bool throws (ResolveError) {
3590 4596
    try checkLinearFn(self, receiverName, params, body);
3591 4597
    return false;
3592 4598
}
3593 4599
3594 4600
/// Analyze a function parameter and bind its identifier.
3595 -
unsafe fn resolveFnParam(self: &mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
4601 +
unsafe fn resolveFnParam 'arena (self: &mut Resolver 'arena, node: *ast::Node, param: ast::FnParam) -> Type
3596 4602
    throws (ResolveError)
3597 4603
{
3598 4604
    let ty = try resolveValueType(self, param.type);
3599 4605
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3600 4606
3608 4614
    /// The declaration permits implicit copies.
3609 4615
    copy: bool,
3610 4616
}
3611 4617
3612 4618
/// Resolve compiler-known ownership markers from a derive list.
3613 -
unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *[*ast::Node]) -> OwnershipMarkers
4619 +
unsafe fn resolveOwnershipMarkers 'arena (self: &mut Resolver 'arena, derives: *[*ast::Node]) -> OwnershipMarkers
3614 4620
    throws (ResolveError)
3615 4621
{
3616 4622
    let mut result = OwnershipMarkers { linear: false, copy: false };
3617 4623
    for derive in derives {
4624 +
        if let case ast::NodeValue::Region { .. } = derive.value {
4625 +
            continue;
4626 +
        }
3618 4627
        let name = try nodeName(self, derive);
3619 4628
        if mem::eq(name, "Once") {
3620 4629
            if result.linear {
3621 4630
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3622 4631
            }
3639 4648
    }
3640 4649
    return result;
3641 4650
}
3642 4651
3643 4652
/// Resolve record fields from a node list.
3644 -
unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
4653 +
unsafe fn resolveRecordFields 'arena (self: &mut Resolver 'arena, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
3645 4654
    throws (ResolveError)
3646 4655
{
3647 -
    let a = alloc::arenaAllocator(&mut self.arena);
4656 +
    let a = alloc::arenaAllocator(self.arena);
3648 4657
    let mut result: *unsafe mut [RecordField] = &mut [];
3649 4658
    let mut currentOffset: u32 = 0;
3650 4659
    let mut maxAlignment: u32 = 1;
3651 4660
3652 4661
    if fields.len > parser::MAX_RECORD_FIELDS {
3653 4662
        throw emitError(self, node, ErrorKind::Internal);
3654 4663
    }
3655 -
    // TODO: Add cycle detection to catch invalid recursive types like `record A { a: A }`.
3656 4664
    for field in fields {
3657 4665
        let case ast::NodeValue::RecordField {
3658 4666
            field: fieldNode,
3659 4667
            type: typeNode,
3660 4668
            value: valueNode
3694 4702
    let recordLayout = Layout {
3695 4703
        size: mem::alignUp(currentOffset, maxAlignment),
3696 4704
        alignment: maxAlignment
3697 4705
    };
3698 4706
    return RecordType {
4707 +
        regions: nil,
4708 +
        application: nil,
3699 4709
        fields: &result[..],
3700 4710
        labeled,
3701 -
        layout: recordLayout,
4711 +
        layout: allocLayout(self, recordLayout),
3702 4712
        declaredLinear: false,
3703 4713
        declaredCopy: false,
3704 4714
    };
3705 4715
}
3706 4716
3707 4717
/// Resolve record field types for a named record declaration.
3708 -
unsafe fn resolveRecordBody(self: &mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
4718 +
unsafe fn resolveRecordBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
4719 +
    throws (ResolveError)
4720 +
{
4721 +
    let previous = self.regionScope;
4722 +
    set self.regionScope = try bindRegions(self, node, decl.regions);
4723 +
    try resolveRecordContents(self, node, decl) catch error {
4724 +
        set self.regionScope = previous;
4725 +
        throw error;
4726 +
    };
4727 +
    set self.regionScope = previous;
4728 +
}
4729 +
4730 +
/// Resolve record contents in the declaration's region environment.
4731 +
unsafe fn resolveRecordContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
3709 4732
    throws (ResolveError)
3710 4733
{
3711 4734
    // Get the type symbol that was bound to this declaration node.
3712 4735
    // If there's no symbol, it's because an earlier phase failed.
3713 4736
    let sym = symbolFor(self, node)
3718 4741
    // Skip if already resolved.
3719 4742
    if let case NominalType::Record(_) = *nominalTy {
3720 4743
        return;
3721 4744
    }
3722 4745
    let markers = try resolveOwnershipMarkers(self, decl.derives);
3723 -
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
4746 +
    set *nominalTy = NominalType::Resolving(node);
4747 +
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled) catch error {
4748 +
        set *nominalTy = NominalType::Placeholder(node);
4749 +
        throw error;
4750 +
    };
4751 +
    set recordType.regions = self.regionScope;
3724 4752
    if markers.copy {
3725 4753
        for field in recordType.fields {
3726 4754
            if not isCopy(field.fieldType) {
3727 4755
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
3728 4756
            }
3733 4761
3734 4762
    set *nominalTy = NominalType::Record(recordType);
3735 4763
}
3736 4764
3737 4765
/// Bind a type name.
3738 -
unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
4766 +
unsafe fn bindTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3739 4767
    throws (ResolveError)
3740 4768
{
3741 -
    let attrMask = resolveAttributes(self, attrs);
4769 +
    let attrMask = resolveAttributes(attrs);
3742 4770
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3743 4771
3744 4772
    // Create a placeholder nominal type that will be replaced in
3745 4773
    // the next phase.
3746 4774
    let nominalTy = allocNominalType(self, NominalType::Placeholder(node));
3747 4775
3748 4776
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3749 4777
}
3750 4778
3751 4779
/// Allocate a trait type descriptor and return a pointer to it.
3752 -
unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *unsafe mut TraitType {
3753 -
    let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
4780 +
unsafe fn allocTraitType 'arena (self: &mut Resolver 'arena, name: *[u8]) -> *unsafe mut TraitType {
4781 +
    let p = try! alloc::allocRaw(self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3754 4782
    let entry = p as *unsafe mut TraitType;
3755 -
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
4783 +
    set *entry = TraitType { name, moduleId: self.currentMod, methods: &mut [], supertraits: &mut [] };
3756 4784
3757 4785
    return entry;
3758 4786
}
3759 4787
3760 4788
/// Bind a trait name in the current scope.
3761 -
unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
4789 +
unsafe fn bindTraitName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3762 4790
    throws (ResolveError)
3763 4791
{
3764 -
    let attrMask = resolveAttributes(self, attrs);
4792 +
    let attrMask = resolveAttributes(attrs);
3765 4793
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3766 4794
3767 4795
    let traitName = try nodeName(self, name);
3768 4796
    let traitType = allocTraitType(self, traitName);
3769 4797
    let data = SymbolData::Trait(traitType);
3777 4805
}
3778 4806
3779 4807
/// Find a trait method by name.
3780 4808
export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod {
3781 4809
    for i in 0..traitType.methods.len {
3782 -
        if traitType.methods[i].name == name {
4810 +
        if mem::eq(traitType.methods[i].name, name) {
3783 4811
            return &traitType.methods[i];
3784 4812
        }
3785 4813
    }
3786 4814
    return nil;
3787 4815
}
3788 4816
3789 4817
/// Resolve a trait declaration body: supertrait methods, then own methods.
3790 -
unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
4818 +
unsafe fn resolveTraitBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
3791 4819
    throws (ResolveError)
3792 4820
{
3793 4821
    let sym = symbolFor(self, node)
3794 4822
        else return;
3795 4823
    let case SymbolData::Trait(traitType) = sym.data
3812 4840
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
3813 4841
        }
3814 4842
3815 4843
        setNodeSymbol(self, superNode, superSym);
3816 4844
3817 -
        let a = alloc::arenaAllocator(&mut self.arena);
4845 +
        let a = alloc::arenaAllocator(self.arena);
3818 4846
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
3819 4847
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3820 4848
                expected: ast::MAX_TRAIT_METHODS,
3821 4849
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
3822 4850
            }));
3843 4871
            actual: traitType.methods.len as u32 + methods.len as u32,
3844 4872
        }));
3845 4873
    }
3846 4874
3847 4875
    for methodNode in methods {
3848 -
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value
4876 +
        let case ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } = methodNode.value
3849 4877
            else continue;
4878 +
        let attrs = modifiers.attrs;
3850 4879
        let methodName = try nodeName(self, name);
3851 -
        let attrMask = resolveAttributes(self, attrs);
4880 +
        let attrMask = resolveAttributes(attrs);
4881 +
        let previousRegions = self.regionScope;
4882 +
        set self.regionScope = try bindRegions(self, methodNode, modifiers.regions);
3852 4883
3853 4884
        // Reject duplicate method names.
3854 4885
        if let _ = findTraitMethod(traitType, methodName) {
3855 4886
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3856 4887
        }
3857 4888
        // Determine the receiver class and mutability, and validate that it
3858 4889
        // points to the declaring trait.
3859 4890
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3860 4891
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3861 4892
        let case ast::TypeSig::Pointer {
3862 -
            class: receiverClass, valueType: receiverValueType, mutable,
4893 +
            class: receiverSyntax, valueType: receiverValueType, mutable,
3863 4894
        } = typeSig
3864 4895
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
4896 +
        let receiverClass = resolvePointerClass(receiverSyntax);
3865 4897
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
3866 4898
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3867 4899
        let case ast::TypeSig::Nominal(nameNode) = innerSig
3868 4900
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3869 4901
        let receiverTargetName = try nodeName(self, nameNode);
3870 4902
3871 4903
        if receiverTargetName <> traitType.name {
3872 4904
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3873 4905
        }
3874 4906
        // Resolve parameter types and return type.
3875 -
        let a = alloc::arenaAllocator(&mut self.arena);
4907 +
        let a = alloc::arenaAllocator(self.arena);
3876 4908
        let mut paramTypes: *mut [*Type] = &mut [];
3877 4909
        let mut throwList: *mut [*Type] = &mut [];
3878 4910
        let mut retType = allocType(self, Type::Void);
3879 4911
3880 4912
        if sig.params.len > MAX_FN_PARAMS {
3882 4914
                expected: MAX_FN_PARAMS,
3883 4915
                actual: sig.params.len,
3884 4916
            }));
3885 4917
        }
3886 4918
        for paramNode in sig.params {
3887 -
            let paramTy = try infer(self, paramNode);
4919 +
            let case ast::NodeValue::FnParam(param) = paramNode.value
4920 +
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4921 +
            let paramTy = try resolveValueType(self, param.type);
3888 4922
            paramTypes.append(allocType(self, paramTy), a);
3889 4923
        }
3890 4924
        if let ret = sig.returnType {
3891 4925
            set retType = allocType(self, try infer(self, ret));
3892 4926
        }
3897 4931
                actual: sig.throwList.len,
3898 4932
            }));
3899 4933
        }
3900 4934
        for throwNode in sig.throwList {
3901 4935
            let throwTy = try infer(self, throwNode);
4936 +
            try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
3902 4937
            throwList.append(allocType(self, throwTy), a);
3903 4938
        }
3904 4939
        let fnType = FnType {
4940 +
            regions: self.regionScope,
3905 4941
            paramTypes: &paramTypes[..],
3906 4942
            returnType: retType,
3907 4943
            throwList: &throwList[..],
3908 4944
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3909 4945
        };
3914 4950
            receiverClass,
3915 4951
            index: traitType.methods.len as u32,
3916 4952
        }, a);
3917 4953
3918 4954
        setNodeType(self, methodNode, Type::Void);
4955 +
        set self.regionScope = previousRegions;
3919 4956
    }
3920 4957
}
3921 4958
3922 4959
/// Resolve a name path node to a symbol.
3923 4960
/// Used for trait and type references in instance declarations and trait objects.
3924 -
unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *unsafe mut Symbol
4961 +
unsafe fn resolveNamePath 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe mut Symbol
3925 4962
    throws (ResolveError)
3926 4963
{
3927 4964
    match node.value {
3928 4965
        case ast::NodeValue::Ident(name) => {
3929 4966
            let sym = findAnySymbol(self.scope, name)
3938 4975
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
3939 4976
        }
3940 4977
    }
3941 4978
}
3942 4979
4980 +
/// Join instance and method region parameters into one function binder.
4981 +
unsafe fn instanceMethodRegions 'arena (
4982 +
    self: &mut Resolver 'arena, instanceRegions: *[*ast::Node], methodRegions: *[*ast::Node]
4983 +
) -> *[*ast::Node] {
4984 +
    let count = instanceRegions.len + methodRegions.len;
4985 +
    if count == 0 {
4986 +
        return &[];
4987 +
    }
4988 +
    let allocator = alloc::arenaAllocator(self.arena);
4989 +
    let mut nodes: *mut [*ast::Node] = &mut [];
4990 +
    for region in instanceRegions {
4991 +
        nodes.append(region, allocator);
4992 +
    }
4993 +
    for region in methodRegions {
4994 +
        nodes.append(region, allocator);
4995 +
    }
4996 +
    return &nodes[..];
4997 +
}
4998 +
4999 +
/// Map one region binder to a contiguous part of another binder.
5000 +
unsafe fn mapRegionScopes 'arena (
5001 +
    self: &mut Resolver 'arena, source: ?*RegionScope, target: ?*RegionScope,
5002 +
    offset: u32, site: *ast::Node
5003 +
) -> ?RegionSubstitution throws (ResolveError) {
5004 +
    let sourceScope = source else return nil;
5005 +
    let targetScope = target else throw emitError(self, site, ErrorKind::Internal);
5006 +
    if offset + sourceScope.entries.len > targetScope.entries.len {
5007 +
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
5008 +
            expected: sourceScope.entries.len,
5009 +
            actual: targetScope.entries.len - offset,
5010 +
        }));
5011 +
    }
5012 +
    let map = regionSubstitution(self, sourceScope);
5013 +
    for _, i in sourceScope.entries {
5014 +
        set map.arguments[i] = targetScope.entries[offset + i];
5015 +
    }
5016 +
    try validateRegionArguments(self, &map, site);
5017 +
    return map;
5018 +
}
5019 +
5020 +
/// Resolved implementation of one trait method.
5021 +
record ResolvedInstanceMethod: Copy {
5022 +
    /// Canonical trait method.
5023 +
    method: *unsafe TraitMethod,
5024 +
    /// Concrete function symbol.
5025 +
    symbol: *unsafe mut Symbol,
5026 +
}
5027 +
5028 +
/// Shared declaration state for instance method resolution.
5029 +
record InstanceMethodContext: Copy {
5030 +
    /// Implemented trait.
5031 +
    traitInfo: *unsafe TraitType,
5032 +
    /// Instance target with declaration regions applied.
5033 +
    concreteType: Type,
5034 +
    /// Instance region nodes in declaration order.
5035 +
    regions: *[*ast::Node],
5036 +
    /// Bound instance region scope.
5037 +
    scope: ?*RegionScope,
5038 +
}
5039 +
5040 +
/// Resolve one instance method in its combined region environment.
5041 +
unsafe fn resolveInstanceMethod 'arena (
5042 +
    self: &mut Resolver 'arena, methodNode: *ast::Node,
5043 +
    context: &InstanceMethodContext
5044 +
) -> ResolvedInstanceMethod throws (ResolveError) {
5045 +
    let case ast::NodeValue::MethodDecl {
5046 +
        name, modifiers, receiverType, sig, ..
5047 +
    } = methodNode.value else panic "resolveInstanceMethod: invalid method";
5048 +
    let combinedRegions = instanceMethodRegions(self, context.regions, modifiers.regions);
5049 +
    let methodScope = try bindRegions(self, methodNode, combinedRegions);
5050 +
    set self.regionScope = methodScope;
5051 +
5052 +
    let methodName = try nodeName(self, name);
5053 +
    let attrMask = resolveAttributes(modifiers.attrs);
5054 +
    let tm = findTraitMethod(context.traitInfo, methodName)
5055 +
        else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
5056 +
    if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe {
5057 +
        throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
5058 +
    }
5059 +
5060 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer {
5061 +
        class: receiverSyntax, valueType, mutable: receiverMut,
5062 +
    }) = receiverType.value else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5063 +
    let receiverClass = resolvePointerClass(receiverSyntax);
5064 +
    if receiverClass <> tm.receiverClass {
5065 +
        throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5066 +
    }
5067 +
    let annotatedTy = try infer(self, valueType);
5068 +
    let mut expectedConcrete = context.concreteType;
5069 +
    if let map = try mapRegionScopes(self, context.scope, methodScope, 0, methodNode) {
5070 +
        set expectedConcrete = substituteRegions(self, &map, context.concreteType);
5071 +
    }
5072 +
    if not typesEqual(annotatedTy, expectedConcrete) {
5073 +
        throw emitTypeMismatch(self, receiverType, TypeMismatch { expected: expectedConcrete, actual: annotatedTy });
5074 +
    }
5075 +
    if tm.mutable and not receiverMut {
5076 +
        throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
5077 +
    }
5078 +
    if receiverMut and not tm.mutable {
5079 +
        throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
5080 +
    }
5081 +
5082 +
    let mut traitFn = tm.fnType;
5083 +
    let mut traitRegionCount: u32 = 0;
5084 +
    if let traitScope = tm.fnType.regions {
5085 +
        set traitRegionCount = traitScope.entries.len;
5086 +
    }
5087 +
    if traitRegionCount <> modifiers.regions.len {
5088 +
        throw emitError(self, methodNode, ErrorKind::RegionArgumentCount(CountMismatch {
5089 +
            expected: traitRegionCount, actual: modifiers.regions.len,
5090 +
        }));
5091 +
    }
5092 +
    if let map = try mapRegionScopes(self, tm.fnType.regions, methodScope, context.regions.len, methodNode) {
5093 +
        set traitFn = substituteFnRegions(self, &map, tm.fnType, nil);
5094 +
    }
5095 +
    if sig.params.len <> traitFn.paramTypes.len {
5096 +
        throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
5097 +
            expected: traitFn.paramTypes.len, actual: sig.params.len,
5098 +
        }));
5099 +
    }
5100 +
5101 +
    let allocator = alloc::arenaAllocator(self.arena);
5102 +
    let mut paramTypes: *mut [*Type] = &mut [];
5103 +
    let receiverPtrType = Type::Pointer {
5104 +
        class: receiverClass, target: allocType(self, annotatedTy), mutable: receiverMut,
5105 +
    };
5106 +
    paramTypes.append(allocType(self, receiverPtrType), allocator);
5107 +
    for paramNode, i in sig.params {
5108 +
        let case ast::NodeValue::FnParam(param) = paramNode.value
5109 +
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5110 +
        let instanceParamTy = try resolveValueType(self, param.type);
5111 +
        if not typesEqual(instanceParamTy, *traitFn.paramTypes[i]) {
5112 +
            throw emitTypeMismatch(self, paramNode, TypeMismatch {
5113 +
                expected: *traitFn.paramTypes[i], actual: instanceParamTy,
5114 +
            });
5115 +
        }
5116 +
        paramTypes.append(allocType(self, instanceParamTy), allocator);
5117 +
    }
5118 +
    let mut returnType = Type::Void;
5119 +
    if let returnNode = sig.returnType {
5120 +
        set returnType = try resolveValueType(self, returnNode);
5121 +
    }
5122 +
    if not typesEqual(returnType, *traitFn.returnType) {
5123 +
        throw emitTypeMismatch(self, methodNode, TypeMismatch {
5124 +
            expected: *traitFn.returnType, actual: returnType,
5125 +
        });
5126 +
    }
5127 +
    if sig.throwList.len <> traitFn.throwList.len {
5128 +
        throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
5129 +
            expected: traitFn.throwList.len, actual: sig.throwList.len,
5130 +
        }));
5131 +
    }
5132 +
    let mut throwList: *mut [*Type] = &mut [];
5133 +
    for throwNode, i in sig.throwList {
5134 +
        let throwType = try resolveValueType(self, throwNode);
5135 +
        if not typesEqual(throwType, *traitFn.throwList[i]) {
5136 +
            throw emitTypeMismatch(self, throwNode, TypeMismatch {
5137 +
                expected: *traitFn.throwList[i], actual: throwType,
5138 +
            });
5139 +
        }
5140 +
        throwList.append(allocType(self, throwType), allocator);
5141 +
    }
5142 +
5143 +
    let fnType = FnType {
5144 +
        regions: methodScope, paramTypes: &paramTypes[..],
5145 +
        returnType: allocType(self, returnType), throwList: &throwList[..],
5146 +
        isUnsafe: tm.fnType.isUnsafe,
5147 +
    };
5148 +
    let fnTy = Type::Fn(allocFnType(self, fnType));
5149 +
    let sym = allocSymbol(self, SymbolData::Value {
5150 +
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
5151 +
    }, methodName, methodNode, attrMask);
5152 +
    setNodeSymbol(self, methodNode, sym);
5153 +
    setNodeType(self, methodNode, fnTy);
5154 +
    setNodeType(self, name, fnTy);
5155 +
    set self.regionScope = context.scope;
5156 +
    return ResolvedInstanceMethod { method: tm, symbol: sym };
5157 +
}
5158 +
3943 5159
/// Resolve an instance declaration.
3944 5160
/// Validates that the trait exists, the target type exists, and all methods
3945 5161
/// match the trait's signatures.
3946 -
unsafe fn resolveInstanceDecl(
3947 -
    self: &mut Resolver,
5162 +
unsafe fn resolveInstanceDecl 'arena (
5163 +
    self: &mut Resolver 'arena,
5164 +
    node: *ast::Node,
5165 +
    traitName: *ast::Node,
5166 +
    targetType: *ast::Node,
5167 +
    regions: *[*ast::Node],
5168 +
    methods: *[*ast::Node]
5169 +
) throws (ResolveError) {
5170 +
    let previous = self.regionScope;
5171 +
    set self.regionScope = try bindRegions(self, node, regions);
5172 +
    try resolveInstanceContents(self, node, traitName, targetType, regions, methods) catch error {
5173 +
        set self.regionScope = previous;
5174 +
        throw error;
5175 +
    };
5176 +
    set self.regionScope = previous;
5177 +
}
5178 +
5179 +
/// Resolve an instance in its declaration region environment.
5180 +
unsafe fn resolveInstanceContents 'arena (
5181 +
    self: &mut Resolver 'arena,
3948 5182
    node: *ast::Node,
3949 5183
    traitName: *ast::Node,
3950 5184
    targetType: *ast::Node,
5185 +
    regions: *[*ast::Node],
3951 5186
    methods: *[*ast::Node]
3952 5187
) throws (ResolveError) {
5188 +
    let instanceScope = self.regionScope;
3953 5189
    // Look up the trait.
3954 5190
    let traitSym = try resolveNamePath(self, traitName);
3955 5191
    let case SymbolData::Trait(traitInfo) = traitSym.data
3956 5192
        else throw emitError(self, traitName, ErrorKind::Internal);
3957 5193
3964 5200
    setNodeSymbol(self, targetType, typeSym);
3965 5201
    // Ensure the concrete type body is resolved.
3966 5202
    try ensureNominalResolved(self, nominalTy, targetType);
3967 5203
3968 5204
    // Reject duplicate instance for the same (trait, type) pair.
3969 -
    let concreteType = Type::Nominal(nominalTy);
5205 +
    let mut concreteInfo = nominalTy;
5206 +
    if regions.len > 0 {
5207 +
        set concreteInfo = try applyNominalRegions(self, nominalTy, regions, targetType);
5208 +
    } else {
5209 +
        try requireNominalArguments(self, nominalTy, targetType);
5210 +
    }
5211 +
    let concreteType = Type::Nominal(concreteInfo);
3970 5212
    if let _ = findInstance(self, traitInfo, concreteType) {
3971 5213
        throw emitError(self, node, ErrorKind::DuplicateInstance);
3972 5214
    }
3973 5215
3974 5216
    // Build the instance entry.
3975 5217
    if self.instancesLen >= MAX_INSTANCES {
3976 5218
        throw emitError(self, node, ErrorKind::Internal);
3977 5219
    }
3978 5220
    let methodSlice = try! alloc::allocRawSlice(
3979 -
        &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
5221 +
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
3980 5222
    ) as *unsafe mut [*unsafe mut Symbol];
3981 5223
    let mut entry = InstanceEntry {
3982 5224
        traitType: traitInfo,
3983 5225
        concreteType,
3984 5226
        concreteTypeName: typeSym.name,
3985 5227
        moduleId: self.currentMod,
3986 5228
        methods: methodSlice,
3987 5229
    };
3988 5230
    // Track which trait methods are covered by the instance.
3989 5231
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
5232 +
    let methodContext = InstanceMethodContext {
5233 +
        traitInfo, concreteType, regions, scope: instanceScope,
5234 +
    };
3990 5235
3991 5236
    // Match each instance method to a trait method.
3992 5237
    for methodNode in methods {
3993 -
        let case ast::NodeValue::MethodDecl {
3994 -
            name, receiverName, receiverType, sig, body, attrs,
3995 -
        } = methodNode.value else continue;
3996 -
3997 -
        let methodName = try nodeName(self, name);
3998 -
        let attrMask = resolveAttributes(self, attrs);
3999 -
4000 -
        // Find the matching trait method.
4001 -
        let tm = findTraitMethod(traitInfo, methodName)
4002 -
            else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
4003 -
        let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
4004 -
        if instanceUnsafe <> tm.fnType.isUnsafe {
4005 -
            throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
4006 -
        }
4007 -
4008 -
        // Determine receiver mutability and validate receiver type.
4009 -
        // The receiver must be `*Type` or `*mut Type`.
4010 -
        let case ast::NodeValue::TypeSig(typeSig) = receiverType.value
4011 -
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4012 -
        let case ast::TypeSig::Pointer {
4013 -
            class: receiverClass, valueType, mutable: receiverMut,
4014 -
        } = typeSig
4015 -
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4016 -
        if receiverClass <> tm.receiverClass {
4017 -
            throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4018 -
        }
4019 -
4020 -
        // Validate that the receiver type annotation matches the
4021 -
        // concrete type from the instance declaration.
4022 -
        let annotatedTy = try infer(self, valueType);
4023 -
        if not typesEqual(annotatedTy, concreteType) {
4024 -
            throw emitTypeMismatch(self, receiverType, TypeMismatch {
4025 -
                expected: concreteType,
4026 -
                actual: annotatedTy,
4027 -
            });
4028 -
        }
4029 -
4030 -
        // Check receiver mutability matches in both directions.
4031 -
        if tm.mutable and not receiverMut {
4032 -
            throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
4033 -
        }
4034 -
        if receiverMut and not tm.mutable {
4035 -
            throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
4036 -
        }
4037 -
4038 -
        // Build the function type for the instance method.
4039 -
        // The receiver becomes the first parameter.
4040 -
        let receiverPtrType = Type::Pointer {
4041 -
            class: receiverClass,
4042 -
            target: allocType(self, concreteType),
4043 -
            mutable: receiverMut,
4044 -
        };
4045 -
4046 -
        // Validate that the instance method's signature matches the
4047 -
        // trait method's signature exactly (params, return type, throws).
4048 -
        if sig.params.len <> tm.fnType.paramTypes.len {
4049 -
            throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
4050 -
                expected: tm.fnType.paramTypes.len as u32,
4051 -
                actual: sig.params.len,
4052 -
            }));
4053 -
        }
4054 -
        for paramNode, j in sig.params {
4055 -
            let case ast::NodeValue::FnParam(param) = paramNode.value
4056 -
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4057 -
            let instanceParamTy = try resolveValueType(self, param.type);
4058 -
            if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) {
4059 -
                throw emitTypeMismatch(self, paramNode, TypeMismatch {
4060 -
                    expected: *tm.fnType.paramTypes[j],
4061 -
                    actual: instanceParamTy,
4062 -
                });
4063 -
            }
4064 -
        }
4065 -
        let mut instanceRetTy = Type::Void;
4066 -
        if let retNode = sig.returnType {
4067 -
            set instanceRetTy = try resolveValueType(self, retNode);
4068 -
        }
4069 -
        if not typesEqual(instanceRetTy, *tm.fnType.returnType) {
4070 -
            throw emitTypeMismatch(self, methodNode, TypeMismatch {
4071 -
                expected: *tm.fnType.returnType,
4072 -
                actual: instanceRetTy,
4073 -
            });
4074 -
        }
4075 -
        if sig.throwList.len <> tm.fnType.throwList.len {
4076 -
            throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
4077 -
                expected: tm.fnType.throwList.len as u32,
4078 -
                actual: sig.throwList.len,
4079 -
            }));
4080 -
        }
4081 -
        for throwNode, j in sig.throwList {
4082 -
            let instanceThrowTy = try resolveValueType(self, throwNode);
4083 -
            if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) {
4084 -
                throw emitTypeMismatch(self, throwNode, TypeMismatch {
4085 -
                    expected: *tm.fnType.throwList[j],
4086 -
                    actual: instanceThrowTy,
4087 -
                });
4088 -
            }
4089 -
        }
4090 -
4091 -
        // Build final function type: receiver plus trait's canonical types.
4092 -
        let a = alloc::arenaAllocator(&mut self.arena);
4093 -
        // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`?
4094 -
        let mut paramTypes: *mut [*Type] = &mut [];
4095 -
        paramTypes.append(allocType(self, receiverPtrType), a);
4096 -
4097 -
        for ty in tm.fnType.paramTypes {
4098 -
            paramTypes.append(ty, a);
4099 -
        }
4100 -
        let fnType = FnType {
4101 -
            paramTypes: &paramTypes[..],
4102 -
            returnType: tm.fnType.returnType,
4103 -
            throwList: tm.fnType.throwList,
4104 -
            isUnsafe: tm.fnType.isUnsafe,
4105 -
        };
4106 -
4107 -
        // Create a symbol for the instance method without binding it into the
4108 -
        // module scope. Instance methods are dispatched via v-table, so they
4109 -
        // must not pollute the enclosing scope.
4110 -
        let fnTy = Type::Fn(allocFnType(self, fnType));
4111 -
        let mName = try nodeName(self, name);
4112 -
        let sym = allocSymbol(self, SymbolData::Value {
4113 -
            mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4114 -
        }, mName, methodNode, attrMask);
4115 -
4116 -
        setNodeSymbol(self, methodNode, sym);
4117 -
        setNodeType(self, methodNode, fnTy);
4118 -
        setNodeType(self, name, fnTy);
4119 -
4120 -
        // Store in instance entry at the matching v-table slot.
4121 -
        set entry.methods[tm.index] = sym;
4122 -
        set covered[tm.index] = true;
5238 +
        let resolved = try resolveInstanceMethod(
5239 +
            self, methodNode, &methodContext
5240 +
        );
5241 +
        set entry.methods[resolved.method.index] = resolved.symbol;
5242 +
        set covered[resolved.method.index] = true;
4123 5243
    }
4124 5244
4125 5245
    // Fill inherited method slots from supertrait instances.
4126 5246
    for superTrait in traitInfo.supertraits {
4127 5247
        let superInst = findInstance(self, superTrait, concreteType)
4147 5267
4148 5268
    setNodeType(self, node, Type::Void);
4149 5269
}
4150 5270
4151 5271
/// Resolve instance method bodies.
4152 -
unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *[*ast::Node])
5272 +
unsafe fn resolveInstanceMethodBodies 'arena (self: &mut Resolver 'arena, methods: *[*ast::Node])
4153 5273
    throws (ResolveError)
4154 5274
{
4155 5275
    for methodNode in methods {
4156 -
        let case ast::NodeValue::MethodDecl {
4157 -
            name, receiverName, receiverType, sig, body, ..
4158 -
        } = methodNode.value else continue;
5276 +
        let case ast::NodeValue::MethodDecl { .. } = methodNode.value else continue;
4159 5277
4160 5278
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
4161 5279
        // for this method (eg. unknown method name). Skip gracefully.
4162 -
        let sym = symbolFor(self, methodNode)
4163 -
            else continue;
5280 +
        if symbolFor(self, methodNode) == nil {
5281 +
            continue;
5282 +
        }
4164 5283
4165 -
        try resolveMethodBody(self, methodNode, receiverName, sig, body);
5284 +
        try resolveMethodBody(self, methodNode);
4166 5285
    }
4167 5286
}
4168 5287
4169 5288
/// Resolve a method body shared by instance methods and standalone methods.
4170 5289
/// Binds the receiver and parameters, then type-checks the body.
4171 -
unsafe fn resolveMethodBody(
4172 -
    self: &mut Resolver,
5290 +
unsafe fn resolveMethodBody 'arena (
5291 +
    self: &mut Resolver 'arena,
4173 5292
    node: *ast::Node,
4174 -
    receiverName: *ast::Node,
4175 -
    sig: ast::FnSig,
4176 -
    body: *ast::Node,
4177 5293
) throws (ResolveError) {
5294 +
    let case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } = node.value
5295 +
        else panic "resolveMethodBody: invalid method";
4178 5296
    let sym = symbolFor(self, node)
4179 5297
        else throw emitError(self, node, ErrorKind::Internal);
4180 5298
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
4181 5299
        else panic "resolveMethodBody: expected value symbol";
4182 -
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body);
5300 +
    let previous = self.regionScope;
5301 +
    set self.regionScope = fnType.regions;
5302 +
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body) catch error {
5303 +
        set self.regionScope = previous;
5304 +
        throw error;
5305 +
    };
5306 +
    set self.regionScope = previous;
4183 5307
}
4184 5308
4185 5309
/// Resolve a standalone method declaration (signature only).
4186 5310
/// Validates the receiver type and registers the method in the method table.
4187 5311
4188 5312
/// Extract the type name from a resolved receiver type node.
4189 -
unsafe fn receiverTypeName(
4190 -
    self: &mut Resolver,
5313 +
unsafe fn receiverTypeName 'arena (
5314 +
    self: &mut Resolver 'arena,
4191 5315
    receiverType: *ast::Node,
4192 5316
) -> *[u8] throws (ResolveError) {
4193 5317
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4194 5318
        receiverType.value
4195 5319
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4196 -
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
4197 -
        else throw emitError(self, receiverType, ErrorKind::Internal);
5320 +
    let mut nameNode: *ast::Node = valueType;
5321 +
    match valueType.value {
5322 +
        case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(name)) => set nameNode = name,
5323 +
        case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name, .. }) => set nameNode = name,
5324 +
        else => throw emitError(self, receiverType, ErrorKind::Internal),
5325 +
    }
4198 5326
    let sym = symbolFor(self, nameNode)
4199 5327
        else throw emitError(self, receiverType, ErrorKind::Internal);
4200 5328
4201 5329
    return sym.name;
4202 5330
}
4203 5331
4204 5332
/// Resolve and register a standalone method declaration.
4205 -
unsafe fn resolveMethodDecl(
4206 -
    self: &mut Resolver,
5333 +
unsafe fn resolveMethodDecl 'arena (
5334 +
    self: &mut Resolver 'arena,
5335 +
    node: *ast::Node,
5336 +
) throws (ResolveError) {
5337 +
    let case ast::NodeValue::MethodDecl { modifiers, .. } = node.value
5338 +
        else panic "resolveMethodDecl: invalid method";
5339 +
    let previous = self.regionScope;
5340 +
    set self.regionScope = try bindRegions(self, node, modifiers.regions);
5341 +
    try resolveMethodSignature(self, node) catch error {
5342 +
        set self.regionScope = previous;
5343 +
        throw error;
5344 +
    };
5345 +
    set self.regionScope = previous;
5346 +
}
5347 +
5348 +
/// Resolve a standalone method signature in its region environment.
5349 +
unsafe fn resolveMethodSignature 'arena (
5350 +
    self: &mut Resolver 'arena,
4207 5351
    node: *ast::Node,
4208 -
    name: *ast::Node,
4209 -
    receiverName: *ast::Node,
4210 -
    receiverType: *ast::Node,
4211 -
    sig: ast::FnSig,
4212 -
    attrs: ?ast::Attributes,
4213 5352
) throws (ResolveError) {
5353 +
    let case ast::NodeValue::MethodDecl {
5354 +
        name, modifiers, receiverType, sig, ..
5355 +
    } = node.value else panic "resolveMethodSignature: invalid method";
4214 5356
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
4215 5357
    // nominal type.
4216 5358
    let fullReceiverTy = try infer(self, receiverType);
4217 5359
    let case Type::Pointer {
4218 5360
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
4224 5366
    try ensureNominalResolved(self, nominalTy, receiverType);
4225 5367
4226 5368
    // Get the type name from the inner type node's symbol.
4227 5369
    let typeName = try receiverTypeName(self, receiverType);
4228 5370
    let methodName = try nodeName(self, name);
4229 -
    let attrMask = resolveAttributes(self, attrs);
5371 +
    let attrMask = resolveAttributes(modifiers.attrs);
4230 5372
4231 5373
    // Reject duplicate method for the same (type, name).
4232 5374
    if let _ = findMethod(self, concreteType, methodName) {
4233 5375
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
4234 5376
    }
4235 5377
4236 5378
    // Resolve parameter types.
4237 -
    let a = alloc::arenaAllocator(&mut self.arena);
5379 +
    let a = alloc::arenaAllocator(self.arena);
4238 5380
    let mut paramTypes: *mut [*Type] = &mut [];
4239 5381
4240 5382
    // Receiver is the first parameter.
4241 5383
    let receiverPtrType = Type::Pointer {
4242 5384
        class: receiverClass,
4260 5402
4261 5403
    // Resolve throw list.
4262 5404
    let mut throwTypes: *mut [*Type] = &mut [];
4263 5405
    for throwNode in sig.throwList {
4264 5406
        let throwTy = try resolveValueType(self, throwNode);
5407 +
        try validateErrorTag(self, throwNode, throwTy, &throwTypes[..]);
4265 5408
        throwTypes.append(allocType(self, throwTy), a);
4266 5409
    }
4267 5410
4268 5411
    let retTypePtr = allocType(self, returnType);
4269 5412
    let throwList = &throwTypes[..];
4270 5413
4271 5414
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
4272 5415
    // Full function type (receiver + params) for lowering.
4273 5416
    let fullFnType = FnType {
5417 +
        regions: self.regionScope,
4274 5418
        paramTypes: &paramTypes[..],
4275 5419
        returnType: retTypePtr,
4276 5420
        throwList,
4277 5421
        isUnsafe,
4278 5422
    };
4279 5423
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
4280 5424
4281 5425
    // Function type excluding receiver, for call arg checking.
4282 5426
    let checkFnType = FnType {
5427 +
        regions: self.regionScope,
4283 5428
        paramTypes: &paramTypes[1..],
4284 5429
        returnType: retTypePtr,
4285 5430
        throwList,
4286 5431
        isUnsafe,
4287 5432
    };
4310 5455
    };
4311 5456
    set self.methodsLen += 1;
4312 5457
}
4313 5458
4314 5459
/// Look up an instance entry by trait and concrete type.
4315 -
unsafe fn findInstance(self: &Resolver, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
5460 +
unsafe fn findInstance 'arena (self: &Resolver 'arena, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
4316 5461
    for i in 0..self.instancesLen {
4317 5462
        let entry: *unsafe InstanceEntry = &self.instances[i];
4318 -
        if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) {
5463 +
        if entry.traitType == traitInfo and erasedTypesEqual(entry.concreteType, concreteType) {
4319 5464
            return entry;
4320 5465
        }
4321 5466
    }
4322 5467
    return nil;
4323 5468
}
4324 5469
4325 5470
/// Look up a standalone method by concrete type and name.
4326 -
export unsafe fn findMethod(self: &Resolver, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
5471 +
export unsafe fn findMethod 'arena (self: &Resolver 'arena, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
4327 5472
    for i in 0..self.methodsLen {
4328 5473
        let entry: *unsafe MethodEntry = &self.methods[i];
4329 -
        if typesEqual(entry.concreteType, concreteType) and entry.name == name {
5474 +
        if erasedTypesEqual(entry.concreteType, concreteType) and entry.name == name {
4330 5475
            return entry;
4331 5476
        }
4332 5477
    }
4333 5478
    return nil;
4334 5479
}
4335 5480
4336 5481
/// Look up a standalone method entry by its symbol.
4337 -
export unsafe fn findMethodBySymbol(self: &Resolver, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry {
5482 +
export unsafe fn findMethodBySymbol 'arena (self: &Resolver 'arena, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry {
4338 5483
    for i in 0..self.methodsLen {
4339 5484
        let entry: *unsafe MethodEntry = &self.methods[i];
4340 5485
        if entry.symbol == sym {
4341 5486
            return entry;
4342 5487
        }
4343 5488
    }
4344 5489
    return nil;
4345 5490
}
4346 5491
4347 5492
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
4348 -
unsafe fn resolveUnionBody(self: &mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
5493 +
unsafe fn resolveUnionBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
5494 +
    throws (ResolveError)
5495 +
{
5496 +
    let previous = self.regionScope;
5497 +
    set self.regionScope = try bindRegions(self, node, decl.regions);
5498 +
    try resolveUnionContents(self, node, decl) catch error {
5499 +
        set self.regionScope = previous;
5500 +
        throw error;
5501 +
    };
5502 +
    set self.regionScope = previous;
5503 +
}
5504 +
5505 +
/// Resolve union contents in the declaration's region environment.
5506 +
unsafe fn resolveUnionContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
4349 5507
    throws (ResolveError)
4350 5508
{
4351 5509
    // Get the type symbol that was bound to this declaration node.
4352 5510
    // If there's no symbol, it's because an earlier phase failed.
4353 5511
    let sym = symbolFor(self, node)
4358 5516
    // Check if already resolved, in which case there's no need to
4359 5517
    // do it again.
4360 5518
    if let case NominalType::Union(_) = *nominalTy {
4361 5519
        return;
4362 5520
    }
4363 -
    let a = alloc::arenaAllocator(&mut self.arena);
5521 +
    let a = alloc::arenaAllocator(self.arena);
4364 5522
    let mut variants: *unsafe mut [UnionVariant] = &mut [];
4365 5523
4366 -
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
4367 -
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
4368 -
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4369 5524
    let markers = try resolveOwnershipMarkers(self, decl.derives);
4370 -
    set *nominalTy = NominalType::Union(UnionType {
4371 -
        variants: &[],
4372 -
        layout: Layout { size: 0, alignment: 0 },
4373 -
        valOffset: 0,
4374 -
        isAllVoid: true,
4375 -
        declaredLinear: markers.linear,
4376 -
        declaredCopy: markers.copy,
4377 -
    });
5525 +
    set *nominalTy = NominalType::Resolving(node);
4378 5526
4379 5527
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
4380 5528
    let mut iota: u32 = 0;
4381 5529
    for variantNode, i in decl.variants {
4382 5530
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
4385 5533
        // Resolve the variant's payload type if present.
4386 5534
        let mut variantType = Type::Void;
4387 5535
        if let typeNode = variantDecl.type {
4388 5536
            set variantType = try infer(self, typeNode);
4389 5537
            try ensureStorableType(self, typeNode, variantType);
5538 +
            try ensureTypeResolved(self, variantType, typeNode);
4390 5539
        }
4391 5540
        // Process the variant's explicit discriminant value if present.
4392 5541
        try visitOptional(self, variantDecl.value, variantType);
4393 5542
        let tag = variantTag(variantDecl, &mut iota);
4394 5543
        // Create a symbol for this variant.
4410 5559
    }
4411 5560
    let info = computeUnionLayout(&variants[..]);
4412 5561
4413 5562
    // Update the nominal type with the resolved variants.
4414 5563
    set *nominalTy = NominalType::Union(UnionType {
5564 +
        regions: self.regionScope,
5565 +
        application: nil,
4415 5566
        variants: &variants[..],
4416 -
        layout: info.layout,
5567 +
        layout: allocLayout(self, info.layout),
4417 5568
        valOffset: info.valOffset,
4418 5569
        isAllVoid: info.isAllVoid,
4419 5570
        declaredLinear: markers.linear,
4420 5571
        declaredCopy: markers.copy,
4421 5572
    });
4422 5573
}
4423 5574
4424 -
/// Check if a module should be analyzed based on its attributes and build configuration.
4425 -
fn shouldAnalyzeModule(self: &Resolver, attrs: ?ast::Attributes) -> bool {
5575 +
/// Check whether an attributed module or import is active in this build.
5576 +
/// A test module is active only when its source module was registered.
5577 +
unsafe fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool {
4426 5578
    if let attributes = attrs {
4427 -
        // Skip test modules unless we're building in test mode.
4428 -
        if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest {
4429 -
            return false;
5579 +
        if ast::attributesContains(&attributes, ast::Attribute::Test) {
5580 +
            if not self.config.buildTest {
5581 +
                return false;
5582 +
            }
5583 +
            if let moduleName = name {
5584 +
                return module::findChild(self.moduleGraph, moduleName, self.currentMod) <> nil;
5585 +
            }
4430 5586
        }
4431 5587
    }
4432 5588
    return true;
4433 5589
}
4434 5590
4435 5591
/// Analyze a module during the graph analysis phase.
4436 -
unsafe fn resolveModGraph(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
5592 +
unsafe fn resolveModGraph 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
4437 5593
    throws (ResolveError)
4438 5594
{
4439 -
    if not shouldAnalyzeModule(self, decl.attrs) {
5595 +
    let modName = try nodeName(self, decl.name);
5596 +
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
4440 5597
        return;
4441 5598
    }
4442 -
    let modName = try nodeName(self, decl.name);
4443 -
    let attrMask = resolveAttributes(self, decl.attrs);
5599 +
    let attrMask = resolveAttributes(decl.attrs);
4444 5600
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
4445 5601
    let submod = try enterSubModule(self, modName, node);
4446 5602
4447 5603
    // Bind the module symbol in the outer scope, ie. where the `mod` statement is.
4448 5604
    try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope);
4452 5608
4453 5609
    exitModuleScope(self, submod);
4454 5610
}
4455 5611
4456 5612
/// Analyze a module in the declaration phase.
4457 -
unsafe fn resolveModDecl(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
5613 +
unsafe fn resolveModDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
4458 5614
    throws (ResolveError)
4459 5615
{
4460 -
    if not shouldAnalyzeModule(self, decl.attrs) {
4461 -
        return;
4462 -
    }
4463 5616
    // Find module under the current module.
4464 5617
    let modName = try nodeName(self, decl.name);
5618 +
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
5619 +
        return;
5620 +
    }
4465 5621
    let submod = try enterSubModule(self, modName, node);
4466 5622
    let case ast::NodeValue::Block(block) = submod.root.value
4467 5623
        else panic "resolveModDecl: expected block for module root";
4468 5624
    try resolveModuleDecls(self, &block);
4469 5625
4470 5626
    exitModuleScope(self, submod);
4471 5627
}
4472 5628
4473 5629
/// Analyze a `use` statement and create a symbol for the imported module.
4474 -
unsafe fn resolveUse(self: &mut Resolver, node: *ast::Node, decl: ast::Use) -> Type
5630 +
unsafe fn resolveUse 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Use) -> Type
4475 5631
    throws (ResolveError)
4476 5632
{
5633 +
    if not shouldAnalyzeModule(self, decl.attrs, nil) {
5634 +
        return Type::Void;
5635 +
    }
4477 5636
    let resolved = try resolveModulePath(self, decl.path);
4478 -
    let attrMask = resolveAttributes(self, decl.attrs);
5637 +
    let attrMask = resolveAttributes(decl.attrs);
4479 5638
4480 5639
    if decl.wildcard {
4481 5640
        // Import all public symbols from the target module.
4482 5641
        for i in 0..resolved.scope.symbolsLen {
4483 5642
            let sym = resolved.scope.symbols[i];
4498 5657
    }
4499 5658
    return Type::Void;
4500 5659
}
4501 5660
4502 5661
/// Analyze a standard `if` statement.
4503 -
unsafe fn resolveIf(self: &mut Resolver, node: *ast::Node, cond: ast::If) -> Type
5662 +
unsafe fn resolveIf 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::If) -> Type
4504 5663
    throws (ResolveError)
4505 5664
{
4506 5665
    try checkBoolean(self, cond.condition);
4507 5666
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4508 5667
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4509 5668
4510 5669
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4511 5670
}
4512 5671
4513 5672
/// Analyze a conditional expression.
4514 -
unsafe fn resolveCondExpr(self: &mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type
5673 +
unsafe fn resolveCondExpr 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::CondExpr, hint: Type) -> Type
4515 5674
    throws (ResolveError)
4516 5675
{
4517 5676
    try checkBoolean(self, cond.condition);
4518 -
    let thenTy = try infer(self, cond.thenExpr);
4519 -
    let elseTy = try infer(self, cond.elseExpr);
5677 +
    let thenValue = try visit(self, cond.thenExpr, hint);
5678 +
    let thenTy = assignableValueType(self, cond.thenExpr, thenValue);
5679 +
    let elseValue = try visit(self, cond.elseExpr, hint);
5680 +
    let elseTy = assignableValueType(self, cond.elseExpr, elseValue);
4520 5681
4521 5682
    // Either branch may supply the concrete type for an otherwise context-
4522 5683
    // dependent expression, such as an unsuffixed integer or `nil`.
4523 5684
    if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) {
4524 5685
        setNodeCoercion(self, cond.elseExpr, coercion);
4532 5693
4533 5694
    return setNodeType(self, node, thenTy);
4534 5695
}
4535 5696
4536 5697
/// Analyze a pattern match structure (used by if-let, while-let).
4537 -
unsafe fn resolvePatternMatch(self: &mut Resolver, node: *ast::Node, pat: &ast::PatternMatch)
5698 +
unsafe fn resolvePatternMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, pat: &ast::PatternMatch)
4538 5699
    throws (ResolveError)
4539 5700
{
4540 5701
    match pat.kind {
4541 5702
        case ast::PatternKind::Case => {
4542 5703
            // Analyze pattern against scrutinee type.
4560 5721
        try checkBoolean(self, guard);
4561 5722
    }
4562 5723
}
4563 5724
4564 5725
/// Analyze an `if let` or `if let case` pattern binding.
4565 -
unsafe fn resolveIfLet(self: &mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type
5726 +
unsafe fn resolveIfLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::IfLet) -> Type
4566 5727
    throws (ResolveError)
4567 5728
{
4568 5729
    enterScope(self, node);
4569 5730
    try resolvePatternMatch(self, node, &cond.pattern);
4570 5731
4598 5759
4599 5760
/// Analyze a case pattern for match, if-case, let-case, or while-case.
4600 5761
///
4601 5762
/// At the top level, bare identifiers are compared against existing values.
4602 5763
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
4603 -
unsafe fn resolveCasePattern(
4604 -
    self: &mut Resolver,
5764 +
unsafe fn resolveCasePattern 'arena (
5765 +
    self: &mut Resolver 'arena,
4605 5766
    pattern: *ast::Node,
4606 5767
    scrutineeTy: Type,
4607 5768
    mode: IdentMode,
4608 5769
    matchBy: MatchBy
4609 5770
) throws (ResolveError) {
4625 5786
                    return;
4626 5787
                }
4627 5788
                case NominalType::Record(recInfo) => {
4628 5789
                    match pattern.value {
4629 5790
                        case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => {
4630 -
                            try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5791 +
                            try resolveRecordPattern(self, pattern, scrutineeTy, recInfo, matchBy);
4631 5792
                            return;
4632 5793
                        } else => {}
4633 5794
                    }
4634 5795
                } else => {}
4635 5796
            }
4667 5828
        }
4668 5829
    }
4669 5830
}
4670 5831
4671 5832
/// Analyze a traditional `while` loop.
4672 -
unsafe fn resolveWhile(self: &mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type
5833 +
unsafe fn resolveWhile 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::While) -> Type
4673 5834
    throws (ResolveError)
4674 5835
{
4675 5836
    try checkBoolean(self, loopNode.condition);
4676 5837
    let loopTy = try visitLoop(self, loopNode.body);
4677 5838
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4681 5842
    }
4682 5843
    return setNodeType(self, node, Type::Void);
4683 5844
}
4684 5845
4685 5846
/// Analyze a `while let` loop with pattern binding.
4686 -
unsafe fn resolveWhileLet(self: &mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type
5847 +
unsafe fn resolveWhileLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::WhileLet) -> Type
4687 5848
    throws (ResolveError)
4688 5849
{
4689 5850
    enterScope(self, node);
4690 5851
    try resolvePatternMatch(self, node, &loopNode.pattern);
4691 5852
4696 5857
4697 5858
    return setNodeType(self, node, Type::Void);
4698 5859
}
4699 5860
4700 5861
/// Analyze a `for` loop, binding iteration variables.
4701 -
unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type
5862 +
unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type
4702 5863
    throws (ResolveError)
4703 5864
{
4704 5865
    let iterableTy = try infer(self, forStmt.iterable);
4705 5866
4706 5867
    // Extract binding names for the lowerer.
4813 5974
4814 5975
/// Check whether a pattern contains nested sub-patterns that further
4815 5976
/// refine the match beyond the outer variant (e.g. nested union variant
4816 5977
/// tests or literal comparisons). Used to allow the same outer variant
4817 5978
/// to appear in multiple match arms.
4818 -
fn hasNestedRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
5979 +
fn hasNestedRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
4819 5980
    for i in 0..patternSubCount(pattern) {
4820 5981
        if let sub = patternSubElement(pattern, i) {
4821 5982
            if isRefiningPattern(self, sub) {
4822 5983
                return true;
4823 5984
            }
4828 5989
4829 5990
/// Check whether a single pattern node is a refining pattern that tests
4830 5991
/// a value rather than just binding it. Union variants, literals, and
4831 5992
/// scope accesses are refining; identifiers, placeholders, and plain
4832 5993
/// record destructurings are not.
4833 -
fn isRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
5994 +
fn isRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
4834 5995
    match pattern.value {
4835 5996
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
4836 5997
            return false,
4837 5998
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
4838 5999
            if let keyNode = patternVariantKeyNode(pattern) {
4895 6056
    return true;
4896 6057
}
4897 6058
4898 6059
/// Analyze a match prong, checking for duplicate catch-alls. Returns the
4899 6060
/// unified match type.
4900 -
unsafe fn resolveMatchProng(
4901 -
    self: &mut Resolver,
6061 +
unsafe fn resolveMatchProng 'arena (
6062 +
    self: &mut Resolver 'arena,
4902 6063
    prongNode: *ast::Node,
4903 6064
    prong: ast::MatchProng,
4904 6065
    subjectTy: Type,
4905 6066
    state: &mut MatchState,
4906 6067
    matchType: Type,
4929 6090
    return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
4930 6091
}
4931 6092
4932 6093
/// Analyze a `match` expression. Dispatches to specialized functions based on
4933 6094
/// the subject type.
4934 -
unsafe fn resolveMatch(self: &mut Resolver, node: *ast::Node, sw: ast::Match) -> Type
6095 +
unsafe fn resolveMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match) -> Type
4935 6096
    throws (ResolveError)
4936 6097
{
4937 6098
    let subjectTy = try infer(self, sw.subject);
4938 6099
    if isUnsafePointerType(subjectTy) {
4939 6100
        try requireUnsafe(self, sw.subject);
4943 6104
    if let case Type::Optional(inner) = subject.effectiveTy {
4944 6105
        try resolveMatchOptional(self, node, sw, inner, subject.by);
4945 6106
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
4946 6107
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4947 6108
    } else {
4948 -
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
6109 +
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy, subject.by);
4949 6110
    }
4950 6111
4951 6112
    // Mark last non-guarded prong as exhaustive.
4952 6113
    let lastProng = sw.prongs[sw.prongs.len - 1];
4953 6114
    let case ast::NodeValue::MatchProng(p) = lastProng.value
4960 6121
    };
4961 6122
    return ty;
4962 6123
}
4963 6124
4964 6125
/// Analyze a `match` expression on an optional subject.
4965 -
unsafe fn resolveMatchOptional(
4966 -
    self: &mut Resolver,
6126 +
unsafe fn resolveMatchOptional 'arena (
6127 +
    self: &mut Resolver 'arena,
4967 6128
    node: *ast::Node,
4968 6129
    sw: ast::Match,
4969 6130
    innerTy: *Type,
4970 6131
    matchBy: MatchBy
4971 6132
) -> Type throws (ResolveError)
5033 6194
    }
5034 6195
    return setNodeType(self, node, matchType);
5035 6196
}
5036 6197
5037 6198
/// Analyze a `match` expression on a union subject.
5038 -
unsafe fn resolveMatchUnion(
5039 -
    self: &mut Resolver,
6199 +
unsafe fn resolveMatchUnion 'arena (
6200 +
    self: &mut Resolver 'arena,
5040 6201
    node: *ast::Node,
5041 6202
    sw: ast::Match,
5042 6203
    subjectTy: Type,
5043 6204
    info: UnionType,
5044 6205
    matchBy: MatchBy
5089 6250
    return setNodeType(self, node, matchType);
5090 6251
}
5091 6252
5092 6253
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
5093 6254
/// booleans must cover both `true` and `false`, other types require a catch-all.
5094 -
unsafe fn resolveMatchGeneric(self: &mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type
6255 +
unsafe fn resolveMatchGeneric 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match, subjectTy: Type, matchBy: MatchBy) -> Type
5095 6256
    throws (ResolveError)
5096 6257
{
5097 6258
    let prongs = sw.prongs;
5098 6259
    let mut state = MatchState { catchAll: false, isConst: true };
5099 6260
    let mut matchType = Type::Never;
5104 6265
    for prongNode in prongs {
5105 6266
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
5106 6267
            else panic "resolveMatchGeneric: expected match prong";
5107 6268
5108 6269
        set matchType = try resolveMatchProng(
5109 -
            self, prongNode, prong, subjectTy, &mut state, matchType, MatchBy::Value
6270 +
            self, prongNode, prong, subjectTy, &mut state, matchType, matchBy
5110 6271
        );
5111 6272
        // Track boolean coverage. Guarded prongs don't count as covering.
5112 6273
        if let case ast::ProngArm::Case(patterns) = prong.arm {
5113 6274
            for p in patterns {
5114 6275
                if prong.guard == nil {
5158 6319
5159 6320
    return setNodeType(self, node, matchType);
5160 6321
}
5161 6322
5162 6323
/// Analyze a single `match` prong branch. Returns the unified match type.
5163 -
unsafe fn visitMatchProng(
5164 -
    self: &mut Resolver,
6324 +
unsafe fn visitMatchProng 'arena (
6325 +
    self: &mut Resolver 'arena,
5165 6326
    node: *ast::Node,
5166 6327
    prongNode: ast::MatchProng,
5167 6328
    subjectTy: Type,
5168 6329
    matchType: Type,
5169 6330
    matchBy: MatchBy
5178 6339
5179 6340
    return unifyBranches(matchType, prongTy);
5180 6341
}
5181 6342
5182 6343
/// Analyze the contents of a `match` prong while inside the prong scope.
5183 -
unsafe fn resolveMatchProngBody(
5184 -
    self: &mut Resolver,
6344 +
unsafe fn resolveMatchProngBody 'arena (
6345 +
    self: &mut Resolver 'arena,
5185 6346
    prong: ast::MatchProng,
5186 6347
    subjectTy: Type,
5187 6348
    matchBy: MatchBy
5188 6349
) -> Type throws (ResolveError) {
5189 6350
    match prong.arm {
5207 6368
    }
5208 6369
    return try visit(self, prong.body, Type::Void);
5209 6370
}
5210 6371
5211 6372
/// Ensure a scope access pattern references a compatible union variant.
5212 -
unsafe fn resolveUnionScopePattern(
5213 -
    self: &mut Resolver,
6373 +
unsafe fn resolveUnionScopePattern 'arena (
6374 +
    self: &mut Resolver 'arena,
5214 6375
    pattern: *ast::Node,
5215 6376
    access: ast::Access,
5216 6377
    subjectTy: Type,
5217 6378
    unionType: UnionType
5218 6379
) throws (ResolveError) {
5233 6394
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
5234 6395
    }
5235 6396
}
5236 6397
5237 6398
/// Validate and bind a union constructor call used as a `match` pattern.
5238 -
unsafe fn resolveUnionCallPattern(
5239 -
    self: &mut Resolver,
6399 +
unsafe fn resolveUnionCallPattern 'arena (
6400 +
    self: &mut Resolver 'arena,
5240 6401
    pattern: *ast::Node,
5241 6402
    call: ast::Call,
5242 6403
    subjectTy: Type,
5243 6404
    unionType: UnionType,
5244 6405
    matchBy: MatchBy
5257 6418
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5258 6419
    }
5259 6420
}
5260 6421
5261 6422
/// Bind the payload introduced by a union constructor pattern.
5262 -
unsafe fn bindUnionPatternPayload(
5263 -
    self: &mut Resolver,
6423 +
unsafe fn bindUnionPatternPayload 'arena (
6424 +
    self: &mut Resolver 'arena,
5264 6425
    pattern: *ast::Node,
5265 6426
    call: ast::Call,
5266 6427
    variantName: *[u8],
5267 6428
    payloadTy: Type,
5268 6429
    matchBy: MatchBy
5271 6432
        throw emitError(
5272 6433
            self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName)
5273 6434
        );
5274 6435
    }
5275 6436
    // All variant payloads are records.
6437 +
    try ensureTypeResolved(self, payloadTy, pattern);
5276 6438
    let recInfo = getRecord(payloadTy)
5277 6439
        else panic "bindUnionPatternPayload: payload is not a record";
5278 6440
5279 6441
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5280 6442
}
5281 6443
5282 6444
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
5283 -
unsafe fn bindPatternVar(self: &mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy)
6445 +
unsafe fn bindPatternVar 'arena (self: &mut Resolver 'arena, binding: *ast::Node, ty: Type, matchBy: MatchBy)
5284 6446
    throws (ResolveError)
5285 6447
{
5286 6448
    let mut bindTy = ty;
5287 6449
    match matchBy {
5288 6450
        case MatchBy::Value => {}
5310 6472
            try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy);
5311 6473
        }
5312 6474
    }
5313 6475
}
5314 6476
6477 +
/// Check a record pattern's exact nominal type before binding its fields.
6478 +
unsafe fn resolveRecordPattern 'arena (
6479 +
    self: &mut Resolver 'arena, pattern: *ast::Node, subjectTy: Type, body: RecordType, matchBy: MatchBy
6480 +
) throws (ResolveError) {
6481 +
    let mut name: ?*ast::Node = nil;
6482 +
    match pattern.value {
6483 +
        case ast::NodeValue::Call(call) => set name = call.callee,
6484 +
        case ast::NodeValue::RecordLit(lit) => set name = lit.typeName,
6485 +
        else => panic "resolveRecordPattern: expected record pattern",
6486 +
    }
6487 +
    if let typeName = name {
6488 +
        let actual = try visit(self, typeName, subjectTy);
6489 +
        let symbol = symbolFor(self, typeName) else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
6490 +
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
6491 +
        if not typesEqual(actual, subjectTy) {
6492 +
            throw emitTypeMismatch(self, typeName, TypeMismatch { expected: subjectTy, actual });
6493 +
        }
6494 +
    }
6495 +
    setNodeType(self, pattern, subjectTy);
6496 +
    try bindRecordPatternFields(self, pattern, body, matchBy);
6497 +
}
6498 +
5315 6499
/// Bind record pattern fields to variables in the current scope.
5316 -
unsafe fn bindRecordPatternFields(
5317 -
    self: &mut Resolver,
6500 +
unsafe fn bindRecordPatternFields 'arena (
6501 +
    self: &mut Resolver 'arena,
5318 6502
    pattern: *ast::Node,
5319 6503
    recInfo: RecordType,
5320 6504
    matchBy: MatchBy
5321 6505
) throws (ResolveError) {
5322 6506
    match pattern.value {
5352 6536
        else => throw emitError(self, pattern, ErrorKind::Internal)
5353 6537
    }
5354 6538
}
5355 6539
5356 6540
/// Validate and bind a record literal pattern for matching labeled union variants.
5357 -
unsafe fn resolveUnionRecordPattern(
5358 -
    self: &mut Resolver,
6541 +
unsafe fn resolveUnionRecordPattern 'arena (
6542 +
    self: &mut Resolver 'arena,
5359 6543
    pattern: *ast::Node,
5360 6544
    lit: ast::RecordLit,
5361 6545
    subjectTy: Type,
5362 6546
    unionType: UnionType,
5363 6547
    matchBy: MatchBy
5382 6566
    setVariantInfo(self, pattern, index, tag);
5383 6567
5384 6568
    if variant.valueType == Type::Void {
5385 6569
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5386 6570
    }
6571 +
    try ensureTypeResolved(self, variant.valueType, pattern);
5387 6572
    let recInfo = getRecord(variant.valueType)
5388 6573
        else panic "resolveUnionRecordPattern: payload is not a record";
5389 6574
5390 6575
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5391 6576
}
5392 6577
5393 6578
/// Analyze a pattern appearing in a union case.
5394 -
unsafe fn resolveUnionPattern(
5395 -
    self: &mut Resolver,
6579 +
unsafe fn resolveUnionPattern 'arena (
6580 +
    self: &mut Resolver 'arena,
5396 6581
    pattern: *ast::Node,
5397 6582
    subjectTy: Type,
5398 6583
    unionType: UnionType,
5399 6584
    matchBy: MatchBy
5400 6585
) throws (ResolveError) {
5446 6631
    }
5447 6632
    return false;
5448 6633
}
5449 6634
5450 6635
/// Analyze a `let-else` guard.
5451 -
unsafe fn resolveLetElse(self: &mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
6636 +
unsafe fn resolveLetElse 'arena (self: &mut Resolver 'arena, node: *ast::Node, letElse: ast::LetElse) -> Type
5452 6637
    throws (ResolveError)
5453 6638
{
5454 6639
    let pat = letElse.pattern;
5455 6640
    let exprTy = try infer(self, pat.scrutinee);
5456 6641
5493 6678
    }
5494 6679
    return setNodeType(self, node, Type::Void);
5495 6680
}
5496 6681
5497 6682
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
5498 -
unsafe fn resolveBuiltinCall(
5499 -
    self: &mut Resolver,
6683 +
unsafe fn resolveBuiltinCall 'arena (
6684 +
    self: &mut Resolver 'arena,
5500 6685
    node: *ast::Node,
5501 6686
    kind: ast::Builtin,
5502 6687
    args: *[*ast::Node]
5503 6688
) -> Type throws (ResolveError) {
5504 6689
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
5557 6742
        negative: false,
5558 6743
    }));
5559 6744
    return setNodeType(self, node, Type::U32);
5560 6745
}
5561 6746
5562 -
/// Validate call arguments against a function type: check argument count,
5563 -
/// type-check each argument, and verify that throwing functions use `try`.
5564 -
unsafe fn checkCallArgs(self: &mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
5565 -
    throws (ResolveError)
5566 -
{
5567 -
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
5568 -
        throw emitError(self, node, ErrorKind::MissingTry);
6747 +
/// Allocate an initially empty argument map for a region-parameterized signature.
6748 +
unsafe fn regionSubstitution 'arena (self: &mut Resolver 'arena, parameters: *RegionScope) -> RegionSubstitution {
6749 +
    let count = parameters.entries.len;
6750 +
    let arguments = try! alloc::allocRawSlice(
6751 +
        self.arena, @sizeOf(?*unsafe types::Region), @alignOf(?*unsafe types::Region), count
6752 +
    ) as *unsafe mut [?*unsafe types::Region];
6753 +
    for i in 0..count {
6754 +
        set arguments[i] = nil;
5569 6755
    }
5570 -
    if call.args.len <> info.paramTypes.len as u32 {
5571 -
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5572 -
            expected: info.paramTypes.len as u32,
5573 -
            actual: call.args.len,
5574 -
        }));
6756 +
    return RegionSubstitution { parameters, arguments };
6757 +
}
6758 +
6759 +
/// Find the position of a region in a substitution's declared parameter list.
6760 +
unsafe fn regionParameter(map: &RegionSubstitution, region: *unsafe types::Region) -> ?u32 {
6761 +
    for parameter, i in map.parameters.entries {
6762 +
        if parameter.id == region.id {
6763 +
            return i;
6764 +
        }
5575 6765
    }
5576 -
    for argNode, i in call.args {
5577 -
        let expectedTy = *info.paramTypes[i];
6766 +
    return nil;
6767 +
}
5578 6768
5579 -
        try checkAssignable(self, argNode, expectedTy);
6769 +
/// Infer one region argument from a pair of reference classes.
6770 +
unsafe fn inferRegionClass 'arena (
6771 +
    self: &mut Resolver 'arena, map: &RegionSubstitution,
6772 +
    expected: types::PointerClass, actual: types::PointerClass, site: *ast::Node
6773 +
) throws (ResolveError) {
6774 +
    let case types::PointerClass::Region(parameter) = expected else return;
6775 +
    let index = regionParameter(map, parameter) else return;
6776 +
    let case types::PointerClass::Region(argument) = actual
6777 +
        else throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
6778 +
    if let previous = map.arguments[index]; previous.id <> argument.id {
6779 +
        throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
5580 6780
    }
6781 +
    set map.arguments[index] = argument;
5581 6782
}
5582 6783
5583 -
/// Analyze a function call expression.
5584 -
unsafe fn resolveCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type
5585 -
    throws (ResolveError)
6784 +
/// Infer regions through matching type structure without adding lifetime subtyping.
6785 +
unsafe fn inferRegionArguments 'arena (
6786 +
    self: &mut Resolver 'arena, map: &RegionSubstitution, expected: Type, actual: Type, site: *ast::Node
6787 +
) throws (ResolveError) {
6788 +
    match expected {
6789 +
        case Type::Cell { class, payload } => {
6790 +
            let case Type::Cell { class: otherClass, payload: other } = actual else return;
6791 +
            try inferRegionClass(self, map, class, otherClass, site);
6792 +
            try inferRegionArguments(self, map, *payload, *other, site);
6793 +
        }
6794 +
        case Type::Session(region) => {
6795 +
            let case Type::Session(other) = actual else return;
6796 +
            try inferRegionClass(self, map, types::PointerClass::Region(region),
6797 +
                types::PointerClass::Region(other), site);
6798 +
        }
6799 +
        case Type::Pointer { class, target, .. } => {
6800 +
            let case Type::Pointer { class: otherClass, target: otherTarget, .. } = actual else return;
6801 +
            try inferRegionClass(self, map, class, otherClass, site);
6802 +
            try inferRegionArguments(self, map, *target, *otherTarget, site);
6803 +
        }
6804 +
        case Type::Slice { class, item, .. } => {
6805 +
            let case Type::Slice { class: otherClass, item: otherItem, .. } = actual else return;
6806 +
            try inferRegionClass(self, map, class, otherClass, site);
6807 +
            try inferRegionArguments(self, map, *item, *otherItem, site);
6808 +
        }
6809 +
        case Type::TraitObject { class, .. } => {
6810 +
            if let case Type::TraitObject { class: otherClass, .. } = actual {
6811 +
                try inferRegionClass(self, map, class, otherClass, site);
6812 +
            }
6813 +
        }
6814 +
        case Type::Array(array) => {
6815 +
            if let case Type::Array(other) = actual {
6816 +
                try inferRegionArguments(self, map, *array.item, *other.item, site);
6817 +
            }
6818 +
        }
6819 +
        case Type::Optional(inner) => {
6820 +
            if let case Type::Optional(other) = actual {
6821 +
                try inferRegionArguments(self, map, *inner, *other, site);
6822 +
            } else {
6823 +
                try inferRegionArguments(self, map, *inner, actual, site);
6824 +
            }
6825 +
        }
6826 +
        case Type::Fn(info) => {
6827 +
            let case Type::Fn(other) = actual else return;
6828 +
            if info.paramTypes.len <> other.paramTypes.len or info.throwList.len <> other.throwList.len {
6829 +
                return;
6830 +
            }
6831 +
            for parameter, i in info.paramTypes {
6832 +
                try inferRegionArguments(self, map, *parameter, *other.paramTypes[i], site);
6833 +
            }
6834 +
            for error, i in info.throwList {
6835 +
                try inferRegionArguments(self, map, *error, *other.throwList[i], site);
6836 +
            }
6837 +
            try inferRegionArguments(self, map, *info.returnType, *other.returnType, site);
6838 +
        }
6839 +
        case Type::Nominal(info) => {
6840 +
            let applied = nominalApplication(info) else return;
6841 +
            let case Type::Nominal(otherInfo) = actual else return;
6842 +
            let other = nominalApplication(otherInfo) else return;
6843 +
            if applied.base <> other.base {
6844 +
                return;
6845 +
            }
6846 +
            for region, i in applied.arguments {
6847 +
                try inferRegionClass(self, map, types::PointerClass::Region(region),
6848 +
                    types::PointerClass::Region(other.arguments[i]), site);
6849 +
            }
6850 +
        }
6851 +
        else => {
6852 +
        }
6853 +
    }
6854 +
}
6855 +
6856 +
/// Require a total substitution whose arguments satisfy each parent relation.
6857 +
unsafe fn validateRegionArguments 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, site: *ast::Node)
6858 +
    throws (ResolveError)
6859 +
{
6860 +
    for parameter, i in map.parameters.entries {
6861 +
        if map.arguments[i] == nil {
6862 +
            throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
6863 +
        }
6864 +
    }
6865 +
    for parameter, i in map.parameters.entries {
6866 +
        let parent = parameter.parent else continue;
6867 +
        let index = regionParameter(map, parent) else panic "validateRegionArguments: unknown parent";
6868 +
        let parentArgument = map.arguments[index] else panic "validateRegionArguments: missing parent argument";
6869 +
        let argument = map.arguments[i] else panic "validateRegionArguments: missing argument";
6870 +
        if not types::regionContains(parentArgument, argument) {
6871 +
            throw emitError(self, site, ErrorKind::RegionParent(parameter.name));
6872 +
        }
6873 +
    }
6874 +
}
6875 +
6876 +
/// Substitute a reference's region while preserving its ownership class.
6877 +
unsafe fn substituteRegionClass(map: &RegionSubstitution, class: types::PointerClass) -> types::PointerClass {
6878 +
    let case types::PointerClass::Region(region) = class else return class;
6879 +
    let index = regionParameter(map, region) else return class;
6880 +
    let argument = map.arguments[index] else panic "substituteRegionClass: missing argument";
6881 +
    return types::PointerClass::Region(argument);
6882 +
}
6883 +
6884 +
/// Substitute free region arguments in a type without changing its runtime layout.
6885 +
unsafe fn substituteRegions 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, ty: Type) -> Type {
6886 +
    match ty {
6887 +
        case Type::Cell { class, payload } =>
6888 +
            return Type::Cell { class: substituteRegionClass(map, class), payload: allocType(self, substituteRegions(self, map, *payload)) },
6889 +
        case Type::Session(region) => {
6890 +
            let index = regionParameter(map, region) else return ty;
6891 +
            let argument = map.arguments[index] else panic "substituteRegions: missing session region";
6892 +
            return Type::Session(argument);
6893 +
        }
6894 +
        case Type::Pointer { class, target, mutable } => {
6895 +
            let targetType = substituteRegions(self, map, *target);
6896 +
            return Type::Pointer { class: substituteRegionClass(map, class), target: allocType(self, targetType), mutable };
6897 +
        }
6898 +
        case Type::Slice { class, item, mutable } => {
6899 +
            let itemType = substituteRegions(self, map, *item);
6900 +
            return Type::Slice { class: substituteRegionClass(map, class), item: allocType(self, itemType), mutable };
6901 +
        }
6902 +
        case Type::TraitObject { class, traitInfo, mutable } =>
6903 +
            return Type::TraitObject { class: substituteRegionClass(map, class), traitInfo, mutable },
6904 +
        case Type::Array(array) => {
6905 +
            let itemType = substituteRegions(self, map, *array.item);
6906 +
            return Type::Array(ArrayType { item: allocType(self, itemType), length: array.length });
6907 +
        }
6908 +
        case Type::Optional(inner) => {
6909 +
            let innerType = substituteRegions(self, map, *inner);
6910 +
            return Type::Optional(allocType(self, innerType));
6911 +
        }
6912 +
        case Type::Fn(info) => return Type::Fn(substituteFnRegions(self, map, info, info.regions)),
6913 +
        case Type::Nominal(info) => {
6914 +
            let applied = nominalApplication(info) else return ty;
6915 +
            let arguments = regionSubstitution(self, applied.parameters);
6916 +
            for region, i in applied.arguments {
6917 +
                let class = substituteRegionClass(map, types::PointerClass::Region(region));
6918 +
                let case types::PointerClass::Region(argument) = class else panic;
6919 +
                set arguments.arguments[i] = argument;
6920 +
            }
6921 +
            return Type::Nominal(internNominalApplication(self, applied.base, &arguments));
6922 +
        }
6923 +
        else => return ty,
6924 +
    }
6925 +
}
6926 +
6927 +
/// Create a substituted signature with the specified remaining region binder.
6928 +
unsafe fn substituteFnRegions 'arena (
6929 +
    self: &mut Resolver 'arena, map: &RegionSubstitution, info: *FnType, regions: ?*RegionScope
6930 +
) -> *FnType {
6931 +
    let a = alloc::arenaAllocator(self.arena);
6932 +
    let mut paramTypes: *mut [*Type] = &mut [];
6933 +
    let mut throwList: *mut [*Type] = &mut [];
6934 +
    for parameter in info.paramTypes {
6935 +
        let ty = substituteRegions(self, map, *parameter);
6936 +
        paramTypes.append(allocType(self, ty), a);
6937 +
    }
6938 +
    for error in info.throwList {
6939 +
        let ty = substituteRegions(self, map, *error);
6940 +
        throwList.append(allocType(self, ty), a);
6941 +
    }
6942 +
    let returnType = substituteRegions(self, map, *info.returnType);
6943 +
    return allocFnType(self, FnType {
6944 +
        regions,
6945 +
        paramTypes: &paramTypes[..],
6946 +
        returnType: allocType(self, returnType),
6947 +
        throwList: &throwList[..],
6948 +
        isUnsafe: info.isUnsafe,
6949 +
    });
6950 +
}
6951 +
6952 +
/// Preserve a call-scoped pointer class while inferring its region-bearing contents.
6953 +
/// Named reference regions are inferred from the source storage.
6954 +
unsafe fn regionInputHint 'arena (self: &mut Resolver 'arena, expected: Type) -> Type {
6955 +
    if let case Type::Optional(inner) = expected {
6956 +
        return regionInputHint(self, *inner);
6957 +
    }
6958 +
    match expected {
6959 +
        case Type::Pointer { class, mutable, .. } => {
6960 +
            if let case types::PointerClass::Region(_) = class {
6961 +
                return Type::Unknown;
6962 +
            }
6963 +
            return Type::Pointer { class, target: allocType(self, Type::Unknown), mutable };
6964 +
        }
6965 +
        case Type::Slice { class, mutable, .. } => {
6966 +
            if let case types::PointerClass::Region(_) = class {
6967 +
                return Type::Unknown;
6968 +
            }
6969 +
            return Type::Slice { class, item: allocType(self, Type::Unknown), mutable };
6970 +
        }
6971 +
        else => return Type::Unknown,
6972 +
    }
6973 +
}
6974 +
6975 +
/// Infer a source function's region arguments from its call inputs.
6976 +
unsafe fn instantiateCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType) -> *FnType
6977 +
    throws (ResolveError)
6978 +
{
6979 +
    let parameters = info.regions else return info;
6980 +
    if call.args.len <> info.paramTypes.len {
6981 +
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
6982 +
            expected: info.paramTypes.len, actual: call.args.len,
6983 +
        }));
6984 +
    }
6985 +
    let map = regionSubstitution(self, parameters);
6986 +
    for argument, i in call.args {
6987 +
        let expected = *info.paramTypes[i];
6988 +
        if containsRegion(expected) {
6989 +
            let actual = try visit(self, argument, regionInputHint(self, expected));
6990 +
            try inferRegionArguments(self, &map, expected, actual, argument);
6991 +
        }
6992 +
    }
6993 +
    try validateRegionArguments(self, &map, node);
6994 +
    return substituteFnRegions(self, &map, info, nil);
6995 +
}
6996 +
6997 +
/// Infer a method's region arguments from its receiver and call arguments.
6998 +
unsafe fn instantiateMethodCall 'arena (
6999 +
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call,
7000 +
    receiver: *ast::Node, receiverType: Type, method: *unsafe MethodEntry
7001 +
) -> *FnType throws (ResolveError) {
7002 +
    let parameters = method.fnType.regions else return method.fnType;
7003 +
    if call.args.len <> method.fnType.paramTypes.len {
7004 +
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7005 +
            expected: method.fnType.paramTypes.len, actual: call.args.len,
7006 +
        }));
7007 +
    }
7008 +
    let map = regionSubstitution(self, parameters);
7009 +
    try inferRegionArguments(self, &map, method.concreteType, receiverType, receiver);
7010 +
    for argument, i in call.args {
7011 +
        let expected = *method.fnType.paramTypes[i];
7012 +
        if containsRegion(expected) {
7013 +
            let actual = try visit(self, argument, regionInputHint(self, expected));
7014 +
            try inferRegionArguments(self, &map, expected, actual, argument);
7015 +
        }
7016 +
    }
7017 +
    try validateRegionArguments(self, &map, node);
7018 +
    return substituteFnRegions(self, &map, method.fnType, nil);
7019 +
}
7020 +
7021 +
/// Apply explicit current-scope regions to a function or nominal type.
7022 +
unsafe fn resolveRegionApply 'arena (
7023 +
    self: &mut Resolver 'arena, node: *ast::Node, value: *ast::Node, regions: *[*ast::Node]
7024 +
) -> Type throws (ResolveError) {
7025 +
    let ty = try infer(self, value);
7026 +
    if let case Type::Nominal(base) = ty {
7027 +
        let symbol = symbolFor(self, value) else throw emitError(self, node, ErrorKind::CannotInferType);
7028 +
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, node, ErrorKind::CannotInferType);
7029 +
        let applied = try applyNominalRegions(self, base, regions, node);
7030 +
        try ensureNominalResolved(self, applied, node);
7031 +
        setNodeSymbol(self, node, symbol);
7032 +
        return setNodeType(self, node, Type::Nominal(applied));
7033 +
    }
7034 +
    let case Type::Fn(info) = ty else throw emitError(self, node, ErrorKind::CannotInferType);
7035 +
    let mut count: u32 = 0;
7036 +
    if let scope = info.regions {
7037 +
        set count = scope.entries.len;
7038 +
    }
7039 +
    if count <> regions.len {
7040 +
        throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
7041 +
    }
7042 +
    let scope = info.regions else panic "resolveRegionApply: empty region application";
7043 +
    let map = regionSubstitution(self, scope);
7044 +
    for region, i in regions {
7045 +
        set map.arguments[i] = try resolveRegion(self, region);
7046 +
    }
7047 +
    try validateRegionArguments(self, &map, node);
7048 +
    let applied = substituteFnRegions(self, &map, info, nil);
7049 +
    if let symbol = symbolFor(self, value) {
7050 +
        setNodeSymbol(self, node, symbol);
7051 +
    }
7052 +
    return setNodeType(self, node, Type::Fn(applied));
7053 +
}
7054 +
7055 +
/// Validate call arguments against a function type: check argument count,
7056 +
/// type-check each argument, and verify that throwing functions use `try`.
7057 +
unsafe fn checkCallArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
7058 +
    throws (ResolveError)
7059 +
{
7060 +
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
7061 +
        throw emitError(self, node, ErrorKind::MissingTry);
7062 +
    }
7063 +
    if call.args.len <> info.paramTypes.len as u32 {
7064 +
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7065 +
            expected: info.paramTypes.len as u32,
7066 +
            actual: call.args.len,
7067 +
        }));
7068 +
    }
7069 +
    for argNode, i in call.args {
7070 +
        let expectedTy = *info.paramTypes[i];
7071 +
7072 +
        try checkAssignable(self, argNode, expectedTy);
7073 +
    }
7074 +
}
7075 +
7076 +
/// Return whether a value can be discarded by bulk arena reclamation.
7077 +
/// Pointer lifetimes are checked when their values are constructed.
7078 +
unsafe fn isBulkDiscardable(ty: Type) -> bool {
7079 +
    match ty {
7080 +
        case Type::Void, Type::Bool, Type::U8, Type::U16, Type::U32, Type::U64,
7081 +
             Type::I8, Type::I16, Type::I32, Type::I64, Type::Fn(_) => return true,
7082 +
        case Type::Pointer { .. }, Type::Slice { .. }, Type::TraitObject { .. } => return true,
7083 +
        case Type::Cell { .. } => return true,
7084 +
        case Type::Array(array) => return isBulkDiscardable(*array.item),
7085 +
        case Type::Optional(inner) => return isBulkDiscardable(*inner),
7086 +
        case Type::Nominal(NominalType::Record(recordType)) => {
7087 +
            if recordType.declaredLinear {
7088 +
                return false;
7089 +
            }
7090 +
            for field in recordType.fields {
7091 +
                if not isBulkDiscardable(field.fieldType) {
7092 +
                    return false;
7093 +
                }
7094 +
            }
7095 +
            return true;
7096 +
        }
7097 +
        case Type::Nominal(NominalType::Union(unionType)) => {
7098 +
            if unionType.declaredLinear {
7099 +
                return false;
7100 +
            }
7101 +
            for variant in unionType.variants {
7102 +
                if not isBulkDiscardable(variant.valueType) {
7103 +
                    return false;
7104 +
                }
7105 +
            }
7106 +
            return true;
7107 +
        }
7108 +
        else => return false,
7109 +
    }
7110 +
}
7111 +
7112 +
/// Compute the end of an aligned layout within the allocator's byte-count range.
7113 +
fn allocationLayoutEnd(offset: u64, layout: Layout) -> ?u64 {
7114 +
    let mut aligned = offset;
7115 +
    if layout.alignment > 0 {
7116 +
        let mask = (layout.alignment - 1) as u64;
7117 +
        set aligned = (offset + mask) & ~mask;
7118 +
    }
7119 +
    let end = aligned + layout.size as u64;
7120 +
    if end > 4294967295 {
7121 +
        return nil;
7122 +
    }
7123 +
    return end;
7124 +
}
7125 +
7126 +
/// Check allocation layout arithmetic independently of the stored narrow offsets.
7127 +
unsafe fn hasAllocationLayout(ty: Type) -> bool {
7128 +
    let layout = getTypeLayout(ty);
7129 +
    match ty {
7130 +
        case Type::Cell { .. } => return true,
7131 +
        case Type::Array(array) => {
7132 +
            if not hasAllocationLayout(*array.item) {
7133 +
                return false;
7134 +
            }
7135 +
            let item = getTypeLayout(*array.item);
7136 +
            return item.size as u64 * array.length as u64 == layout.size as u64;
7137 +
        }
7138 +
        case Type::Optional(inner) => {
7139 +
            if not hasAllocationLayout(*inner) {
7140 +
                return false;
7141 +
            }
7142 +
            if isNullableType(*inner) {
7143 +
                return true;
7144 +
            }
7145 +
            let end = allocationLayoutEnd(1, getTypeLayout(*inner)) else return false;
7146 +
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment: layout.alignment }) else return false;
7147 +
            return total == layout.size as u64;
7148 +
        }
7149 +
        case Type::Nominal(NominalType::Record(recordType)) => {
7150 +
            let mut offset: u64 = 0;
7151 +
            for field in recordType.fields {
7152 +
                if not hasAllocationLayout(field.fieldType) {
7153 +
                    return false;
7154 +
                }
7155 +
                let fieldLayout = getTypeLayout(field.fieldType);
7156 +
                let end = allocationLayoutEnd(offset, fieldLayout) else return false;
7157 +
                let start = end - fieldLayout.size as u64;
7158 +
                if start > 2147483647 or field.offset < 0 or start <> field.offset as u64 {
7159 +
                    return false;
7160 +
                }
7161 +
                set offset = end;
7162 +
            }
7163 +
            let total = allocationLayoutEnd(offset, Layout { size: 0, alignment: layout.alignment }) else return false;
7164 +
            return total == layout.size as u64;
7165 +
        }
7166 +
        case Type::Nominal(NominalType::Union(unionType)) => {
7167 +
            let mut payloadSize: u32 = 0;
7168 +
            let mut alignment: u32 = 1;
7169 +
            for variant in unionType.variants {
7170 +
                if not hasAllocationLayout(variant.valueType) {
7171 +
                    return false;
7172 +
                }
7173 +
                let item = getTypeLayout(variant.valueType);
7174 +
                set payloadSize = max(payloadSize, item.size);
7175 +
                set alignment = max(alignment, item.alignment);
7176 +
            }
7177 +
            let end = allocationLayoutEnd(1, Layout { size: payloadSize, alignment }) else return false;
7178 +
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment }) else return false;
7179 +
            return total == layout.size as u64 and end - payloadSize as u64 == unionType.valOffset as u64;
7180 +
        }
7181 +
        else => return true,
7182 +
    }
7183 +
}
7184 +
7185 +
/// Validate the reservation ABI used by typed session allocation.
7186 +
unsafe fn sessionRuntime 'arena (
7187 +
    self: &mut Resolver 'arena, node: *ast::Node, slice: bool
7188 +
) -> *unsafe TraitMethod
7189 +
    throws (ResolveError)
7190 +
{
7191 +
    let allocTrait = allocationSymbol(self, "Alloc")
7192 +
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7193 +
    let case SymbolData::Trait(allocInfo) = allocTrait.data
7194 +
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7195 +
    let name = "reserveSlice" if slice else "reserve";
7196 +
    let method = findTraitMethod(allocInfo, name)
7197 +
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7198 +
    let error = allocationSymbol(self, "AllocError")
7199 +
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7200 +
    let case SymbolData::Type(errorType) = error.data
7201 +
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7202 +
    let count: u32 = 3 if slice else 2;
7203 +
    let info = method.fnType;
7204 +
    if info.regions <> nil or not info.isUnsafe or info.paramTypes.len <> count or info.throwList.len <> 1 {
7205 +
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7206 +
    }
7207 +
    if not typesEqual(*info.throwList[0], Type::Nominal(errorType)) {
7208 +
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7209 +
    }
7210 +
    for i in 0..count {
7211 +
        if *info.paramTypes[i] <> Type::U32 {
7212 +
            throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7213 +
        }
7214 +
    }
7215 +
    let expected = Type::Slice { class: types::PointerClass::Unsafe, item: allocType(self, Type::Opaque), mutable: true }
7216 +
        if slice else Type::Pointer {
7217 +
            class: types::PointerClass::Unsafe, target: allocType(self, Type::Opaque), mutable: true
7218 +
        };
7219 +
    if not typesEqual(*info.returnType, expected) {
7220 +
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7221 +
    }
7222 +
    return method;
7223 +
}
7224 +
7225 +
/// Check initialized session allocation and retain its source region in the result.
7226 +
unsafe fn resolveSessionAllocation 'arena (
7227 +
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, access: ast::Access,
7228 +
    region: *unsafe types::Region, ctx: CallCtx, hint: Type
7229 +
) -> Type throws (ResolveError) {
7230 +
    let name = try nodeName(self, access.child);
7231 +
    let mut kind = SessionAllocationKind::New;
7232 +
    if mem::eq(name, "copy") {
7233 +
        set kind = SessionAllocationKind::Copy;
7234 +
    }
7235 +
    else if mem::eq(name, "fill") {
7236 +
        set kind = SessionAllocationKind::Fill;
7237 +
    }
7238 +
    else if not mem::eq(name, "new") {
7239 +
        throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(name));
7240 +
    }
7241 +
    let count: u32 = 2 if kind == SessionAllocationKind::Fill else 1;
7242 +
    if call.args.len <> count {
7243 +
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { expected: count, actual: call.args.len }));
7244 +
    }
7245 +
    let slice = kind <> SessionAllocationKind::New;
7246 +
    let mut itemHint = Type::Unknown;
7247 +
    if kind == SessionAllocationKind::New {
7248 +
        if let case Type::Pointer { target, .. } = hint {
7249 +
            set itemHint = *target;
7250 +
        }
7251 +
    } else if kind == SessionAllocationKind::Fill {
7252 +
        if let case Type::Slice { item, .. } = hint {
7253 +
            set itemHint = *item;
7254 +
        }
7255 +
    } else {
7256 +
        set itemHint = Type::Slice {
7257 +
            class: types::PointerClass::Ref, item: allocType(self, Type::Unknown), mutable: false,
7258 +
        };
7259 +
    }
7260 +
    let valueType = try visit(self, call.args[0], itemHint);
7261 +
    let mut itemType = valueType;
7262 +
    let mut parameter = valueType;
7263 +
    if kind == SessionAllocationKind::Copy {
7264 +
        let case Type::Slice { item, .. } = valueType
7265 +
            else throw emitError(self, call.args[0], ErrorKind::ExpectedIndexable);
7266 +
        set itemType = *item;
7267 +
        set parameter = Type::Slice { class: types::PointerClass::Ref, item, mutable: false };
7268 +
    }
7269 +
    if not isTypeInferrable(itemType) or itemType == Type::Void or itemType == Type::Opaque {
7270 +
        throw emitError(self, call.args[0], ErrorKind::CannotInferType);
7271 +
    }
7272 +
    try ensureStorableType(self, call.args[0], itemType);
7273 +
    try ensureTypeResolved(self, itemType, call.args[0]);
7274 +
    try validateRegionStorage(self, call.args[0], itemType, region);
7275 +
    if not isBulkDiscardable(itemType) or (slice and not isCopy(itemType)) {
7276 +
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationValue);
7277 +
    }
7278 +
    if not hasAllocationLayout(itemType) {
7279 +
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationLayout);
7280 +
    }
7281 +
    let runtime = try sessionRuntime(self, node, slice);
7282 +
    let runtimeType = runtime.fnType;
7283 +
    let item = allocType(self, itemType);
7284 +
    let result = Type::Slice { class: types::PointerClass::Region(region), item, mutable: true }
7285 +
        if slice else Type::Pointer {
7286 +
            class: types::PointerClass::Region(region), target: item, mutable: true
7287 +
        };
7288 +
    let a = alloc::arenaAllocator(self.arena);
7289 +
    let mut parameters: *mut [*Type] = &mut [];
7290 +
    parameters.append(allocType(self, parameter), a);
7291 +
    if kind == SessionAllocationKind::Fill {
7292 +
        parameters.append(allocType(self, Type::U32), a);
7293 +
    }
7294 +
    let info = allocFnType(self, FnType {
7295 +
        regions: nil, paramTypes: &parameters[..], returnType: allocType(self, result),
7296 +
        throwList: runtimeType.throwList, isUnsafe: false,
7297 +
    });
7298 +
    try checkCallArgs(self, node, call, info, ctx);
7299 +
    setNodeType(self, call.callee, Type::Fn(info));
7300 +
    let allocTrait = allocationSymbol(self, "Alloc") else panic;
7301 +
    let case SymbolData::Trait(traitInfo) = allocTrait.data else panic;
7302 +
    set self.nodeData.entries[node.id].extra = NodeExtra::SessionAllocation(SessionAllocation {
7303 +
        kind, item, traitInfo, methodIndex: runtime.index,
7304 +
    });
7305 +
    return setNodeType(self, node, result);
7306 +
}
7307 +
7308 +
/// Return whether a nominal declaration explicitly derives Copy.
7309 +
fn nominalDeclaresCopy(node: *ast::Node) -> bool {
7310 +
    let mut derives: *[*ast::Node] = &[];
7311 +
    match node.value {
7312 +
        case ast::NodeValue::RecordDecl(decl) => set derives = decl.derives,
7313 +
        case ast::NodeValue::UnionDecl(decl) => set derives = decl.derives,
7314 +
        else => return false,
7315 +
    }
7316 +
    for derive in derives {
7317 +
        if let case ast::NodeValue::Ident(name) = derive.value {
7318 +
            if mem::eq(name, "Copy") {
7319 +
                return true;
7320 +
            }
7321 +
        }
7322 +
    }
7323 +
    return false;
7324 +
}
7325 +
7326 +
/// Require a complete payload that can be copied and discarded by value.
7327 +
unsafe fn validateCellPayload 'arena (self: &mut Resolver 'arena, node: *ast::Node, payload: Type) throws (ResolveError) {
7328 +
    try ensureStorableType(self, node, payload);
7329 +
    if let case Type::Nominal(info) = payload {
7330 +
        let mut source = info;
7331 +
        if let case NominalType::Application(applied) = *source {
7332 +
            set source = applied.base;
7333 +
        }
7334 +
        if let case NominalType::Resolving(decl) = *source {
7335 +
            if nominalDeclaresCopy(decl) {
7336 +
                return;
7337 +
            }
7338 +
            throw emitError(self, node, ErrorKind::InvalidCellPayload);
7339 +
        }
7340 +
    }
7341 +
    try ensureTypeResolved(self, payload, node);
7342 +
    if not isTypeInferrable(payload) or payload == Type::Void or payload == Type::Opaque
7343 +
        or not isCopy(payload) or not isBulkDiscardable(payload)
7344 +
    {
7345 +
        throw emitError(self, node, ErrorKind::InvalidCellPayload);
7346 +
    }
7347 +
    if not hasAllocationLayout(payload) {
7348 +
        throw emitError(self, node, ErrorKind::InvalidAllocationLayout);
7349 +
    }
7350 +
}
7351 +
7352 +
/// Analyze a function call expression.
7353 +
unsafe fn resolveCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, ctx: CallCtx, hint: Type) -> Type
7354 +
    throws (ResolveError)
5586 7355
{
5587 7356
    // Intercept method calls on slices before inferring the callee.
5588 7357
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5589 7358
        let parentTy = try infer(self, access.parent);
5590 7359
        if isUnsafePointerType(parentTy) {
5591 7360
            try requireUnsafe(self, access.parent);
5592 7361
        }
5593 7362
        let subjectTy = autoDeref(parentTy);
7363 +
        if let case Type::Session(region) = subjectTy {
7364 +
            return try resolveSessionAllocation(self, node, call, access, region, ctx, hint);
7365 +
        }
5594 7366
5595 7367
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5596 7368
            let methodName = try nodeName(self, access.child);
5597 7369
            if methodName == "append" {
5598 7370
                return try resolveSliceAppend(
5604 7376
                    self, node, access.parent, call.args, item, mutable
5605 7377
                );
5606 7378
            }
5607 7379
        }
5608 7380
    }
5609 -
    let calleeTy = try infer(self, call.callee);
7381 +
    let calleeTy = try visit(self, call.callee, hint);
5610 7382
    if let case Type::Fn(info) = calleeTy {
5611 7383
        try checkUnsafeCall(self, call.callee, info);
5612 7384
    }
5613 7385
5614 7386
    // Check if callee is a union variant and dispatch to constructor handler.
5615 7387
    // TODO: Move this out. We should decide on this earlier, based on the callee.
5616 7388
    if let calleeSym = symbolFor(self, call.callee) {
5617 7389
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
5618 7390
            // TODO: Don't pass the callee type, pass the union type by getting it from
5619 7391
            // the symbol.
5620 -
            let declSym = symbolFor(self, decl) else panic;
5621 -
            let case SymbolData::Type(ty) = declSym.data else panic;
5622 -
7392 +
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid variant type";
5623 7393
            return try resolveUnionConstructorCall(self, node, call, ty);
5624 7394
        }
5625 7395
        // Check if callee is an unlabeled record type for constructor call syntax.
5626 -
        if let case SymbolData::Type(ty) = calleeSym.data {
7396 +
        if let case SymbolData::Type(_) = calleeSym.data {
7397 +
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid type callee";
7398 +
            try requireNominalArguments(self, ty, call.callee);
5627 7399
            // Ensure the record body is resolved before checking if labeled.
5628 7400
            try ensureNominalResolved(self, ty, call.callee);
5629 7401
            if let case NominalType::Record(recInfo) = *ty {
5630 7402
                if not recInfo.labeled {
5631 7403
                    return try resolveRecordConstructorCall(self, node, call, ty);
5646 7418
            let methodName = try nodeName(self, access.child);
5647 7419
            let method = findTraitMethod(traitInfo, methodName)
5648 7420
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
5649 7421
5650 7422
            // Reject mutable-receiver methods called on immutable trait objects.
5651 -
            if method.mutable and not objMutable {
5652 -
                throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
7423 +
            if method.mutable {
7424 +
                if not objMutable or not try canMutateThrough(self, access.parent) {
7425 +
                    throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
7426 +
                }
5653 7427
            }
5654 -
            try checkCallArgs(self, node, call, method.fnType, ctx);
7428 +
            let applied = try instantiateCall(self, node, call, method.fnType);
7429 +
            try checkCallArgs(self, node, call, applied, ctx);
5655 7430
            setTraitMethodCall(self, node, traitInfo, method.index);
5656 7431
5657 -
            return setNodeType(self, node, *method.fnType.returnType);
7432 +
            return setNodeType(self, node, *applied.returnType);
5658 7433
        }
5659 7434
5660 7435
        // Check for a standalone method call on a concrete type.
5661 7436
        if let case Type::Nominal(_) = subjectTy {
5662 7437
            let methodName = try nodeName(self, access.child);
5668 7443
                    if not try canMutateThrough(self, access.parent) {
5669 7444
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5670 7445
                    }
5671 7446
                }
5672 7447
                // Check arguments (excluding receiver).
5673 -
                try checkCallArgs(self, node, call, method.fnType, ctx);
7448 +
                let applied = try instantiateMethodCall(
7449 +
                    self, node, call, access.parent, subjectTy, method
7450 +
                );
7451 +
                try checkCallArgs(self, node, call, applied, ctx);
5674 7452
                set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
5675 7453
5676 -
                return setNodeType(self, node, *method.fnType.returnType);
7454 +
                return setNodeType(self, node, *applied.returnType);
5677 7455
            }
5678 7456
        }
5679 7457
    }
5680 7458
    let case Type::Fn(info) = calleeTy else {
5681 7459
        throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch {
5682 7460
            expected: Type::Unknown,
5683 7461
            actual: calleeTy,
5684 7462
        }));
5685 7463
    };
5686 -
    try checkCallArgs(self, node, call, info, ctx);
7464 +
    let applied = try instantiateCall(self, node, call, info);
7465 +
    try checkCallArgs(self, node, call, applied, ctx);
5687 7466
    // Associate function type to callee.
5688 -
    setNodeType(self, call.callee, calleeTy);
7467 +
    setNodeType(self, call.callee, Type::Fn(applied));
5689 7468
5690 7469
    // Associate return type to call.
5691 -
    return setNodeType(self, node, *info.returnType);
7470 +
    return setNodeType(self, node, *applied.returnType);
5692 7471
}
5693 7472
5694 7473
/// Check the allocator layout and the callback ABI used by slice append.
5695 7474
unsafe fn isSliceAllocator(ty: Type) -> bool {
5696 7475
    let case Type::Nominal(NominalType::Record(rec)) = ty else return false;
5720 7499
        else return false;
5721 7500
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
5722 7501
}
5723 7502
5724 7503
/// Resolve `slice.append(val, allocator)`.
5725 -
unsafe fn resolveSliceAppend(
5726 -
    self: &mut Resolver,
7504 +
unsafe fn resolveSliceAppend 'arena (
7505 +
    self: &mut Resolver 'arena,
5727 7506
    node: *ast::Node,
5728 7507
    parent: *ast::Node,
5729 7508
    parentType: Type,
5730 7509
    args: *[*ast::Node],
5731 7510
    elemType: *Type,
5755 7534
    // Return the parent's type so the caller can rebind:
5756 7535
    return setNodeType(self, node, parentType);
5757 7536
}
5758 7537
5759 7538
/// Resolve `slice.delete(index)`.
5760 -
unsafe fn resolveSliceDelete(
5761 -
    self: &mut Resolver,
7539 +
unsafe fn resolveSliceDelete 'arena (
7540 +
    self: &mut Resolver 'arena,
5762 7541
    node: *ast::Node,
5763 7542
    parent: *ast::Node,
5764 7543
    args: *[*ast::Node],
5765 7544
    elemType: *Type,
5766 7545
    mutable: bool
5779 7558
5780 7559
    return setNodeType(self, node, Type::Void);
5781 7560
}
5782 7561
5783 7562
/// Analyze an assignment expression.
5784 -
unsafe fn resolveAssign(self: &mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
7563 +
unsafe fn resolveAssign 'arena (self: &mut Resolver 'arena, node: *ast::Node, assign: ast::Assign) -> Type
5785 7564
    throws (ResolveError)
5786 7565
{
5787 7566
    // Slice assignment: `slice[range] = value`.
5788 7567
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
5789 7568
        if let case ast::NodeValue::Range(range) = index.value {
5824 7603
                    );
5825 7604
                }
5826 7605
            } else {
5827 7606
                try checkAssignable(self, assign.right, *item);
5828 7607
            }
7608 +
            try validateRegionalStore(self, assign.left, assign.right, *item);
5829 7609
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
5830 7610
            setNodeType(self, assign.left, *item);
5831 7611
5832 7612
            return setNodeType(self, node, Type::Void);
5833 7613
        }
5834 7614
    }
5835 7615
    let leftTy = try infer(self, assign.left);
5836 7616
7617 +
    if let case ast::NodeValue::Deref(target) = assign.left.value {
7618 +
        if let case Type::Cell { class, .. } = try infer(self, target) {
7619 +
            try checkAssignable(self, assign.right, leftTy);
7620 +
            if let case types::PointerClass::Region(region) = class {
7621 +
                try validateRegionStorage(self, assign.right, leftTy, region);
7622 +
            } else if class == types::PointerClass::Owned and containsRegion(leftTy) {
7623 +
                throw emitError(self, assign.right, ErrorKind::InvalidCellPayload);
7624 +
            }
7625 +
            return setNodeType(self, node, leftTy);
7626 +
        }
7627 +
    }
7628 +
5837 7629
    // Check if the left-hand side can be assigned to by checking if it's a mutable location.
5838 7630
    if not try canBorrowMutFrom(self, assign.left) {
5839 7631
        throw emitError(self, assign.left, ErrorKind::ImmutableBinding);
5840 7632
    }
5841 7633
    try checkAssignable(self, assign.right, leftTy);
7634 +
    try validateRegionalStore(self, assign.left, assign.right, leftTy);
5842 7635
5843 7636
    return setNodeType(self, node, leftTy);
5844 7637
}
5845 7638
5846 7639
/// Ensure slice range bounds are valid `u32` values.
5847 -
unsafe fn checkSliceRangeIndices(self: &mut Resolver, range: ast::Range) throws (ResolveError) {
7640 +
unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) {
5848 7641
    if let start = range.start {
5849 7642
        try checkIndex(self, start);
5850 7643
    }
5851 7644
    if let end = range.end {
5852 7645
        try checkIndex(self, end);
5853 7646
    }
5854 7647
}
5855 7648
5856 7649
/// Emit an error when a slice range with compile-tyime values exceeds the array length.
5857 -
unsafe fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
7650 +
unsafe fn validateArraySliceBounds 'arena (self: &mut Resolver 'arena, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5858 7651
    let mut startVal: ?u32 = nil;
5859 7652
    let mut endVal: ?u32 = length;
5860 7653
5861 7654
    if let startNode = range.start {
5862 7655
        if let val = constSliceIndex(self, startNode) {
5882 7675
}
5883 7676
5884 7677
/// Check that an index expression has an unsigned integer type.
5885 7678
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
5886 7679
/// Smaller types are widened to `u32` via a numeric cast coercion.
5887 -
unsafe fn checkIndex(self: &mut Resolver, indexNode: *ast::Node) throws (ResolveError) {
7680 +
unsafe fn checkIndex 'arena (self: &mut Resolver 'arena, indexNode: *ast::Node) throws (ResolveError) {
5888 7681
    let indexTy = try visit(self, indexNode, Type::U32);
5889 7682
    if indexTy == Type::Int or indexTy == Type::U32 {
5890 7683
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
5891 7684
        return;
5892 7685
    }
5904 7697
        }
5905 7698
    }
5906 7699
}
5907 7700
5908 7701
/// Analyze an array or slice subscript expression.
5909 -
unsafe fn resolveSubscript(self: &mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
7702 +
unsafe fn resolveSubscript 'arena (self: &mut Resolver 'arena, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
5910 7703
    throws (ResolveError)
5911 7704
{
5912 7705
    // Range subscripts always require `&` to form a slice.
5913 7706
    if let case ast::NodeValue::Range(range) = indexNode.value {
5914 7707
        let _ = try infer(self, indexNode);
5947 7740
    }
5948 7741
    return nil;
5949 7742
}
5950 7743
5951 7744
/// Analyze a union constructor call with payload.
5952 -
unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
7745 +
unsafe fn resolveUnionConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
5953 7746
    throws (ResolveError)
5954 7747
{
5955 7748
    // Get the union nominal type.
5956 7749
    let case NominalType::Union(unionType) = *unionNominal
5957 7750
        else panic "resolveUnionConstructorCall: not a union type";
5966 7759
    setVariantInfo(self, node, index, tag);
5967 7760
5968 7761
    // Check if this variant expects a payload.
5969 7762
    let payloadType = variant.valueType;
5970 7763
    if payloadType <> Type::Void {
7764 +
        try ensureTypeResolved(self, payloadType, node);
5971 7765
        let recInfo = getRecord(payloadType)
5972 7766
            else panic "resolveUnionVariantConstructor: payload is not a record";
5973 7767
        try checkRecordConstructorArgs(self, node, call.args, recInfo);
5974 7768
    } else {
5975 7769
        if call.args.len > 0 {
5982 7776
/// Analyze an unlabeled record constructor call.
5983 7777
///
5984 7778
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
5985 7779
/// number of arguments matches the record's field count and that each argument
5986 7780
/// is assignable to its corresponding field type.
5987 -
unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
7781 +
unsafe fn resolveRecordConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
5988 7782
    throws (ResolveError)
5989 7783
{
5990 7784
    let case NominalType::Record(recInfo) = *recordType
5991 7785
        else panic "resolveRecordConstructorCall: not a record type";
5992 7786
5994 7788
    return setNodeType(self, node, Type::Nominal(recordType));
5995 7789
}
5996 7790
5997 7791
/// Resolve the type name of a record literal, handling both record types and
5998 7792
/// union variant payloads like `Union::Variant { ... }`.
5999 -
unsafe fn resolveRecordLitType(
6000 -
    self: &mut Resolver, node: *ast::Node, typeIdent: *ast::Node
7793 +
unsafe fn resolveRecordLitType 'arena (
7794 +
    self: &mut Resolver 'arena, node: *ast::Node, typeIdent: *ast::Node, hint: Type
6001 7795
) -> ResolvedRecordLitType
6002 7796
    throws (ResolveError)
6003 7797
{
7798 +
    if let case ast::NodeValue::RegionApply { .. } = typeIdent.value {
7799 +
        let ty = try infer(self, typeIdent);
7800 +
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::ExpectedRecord);
7801 +
        return ResolvedRecordLitType { recordType: info, resultType: ty };
7802 +
    }
6004 7803
    // Check if this is a scope access that might be a union variant.
6005 7804
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
6006 7805
        let scope = self.scope;
6007 7806
        let sym = try resolveAccess(self, typeIdent, access, scope);
6008 7807
6009 7808
        // Check if resolved symbol is a union variant.
6010 7809
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
6011 -
            // Get the union type from the variant's declaration.
6012 -
            let declSym = symbolFor(self, decl)
6013 -
                else throw emitError(self, node, ErrorKind::Internal);
6014 -
            let case SymbolData::Type(unionNominalType) = declSym.data
6015 -
                else throw emitError(self, node, ErrorKind::Internal);
6016 -
6017 -
            // Get the variant's payload type.
6018 -
            let case Type::Nominal(payloadInfo) = type
7810 +
            let sourceTy = typeFor(self, typeIdent) else panic "resolveRecordLitType: missing union type";
7811 +
            let case Type::Nominal(source) = sourceTy else panic "resolveRecordLitType: invalid union type";
7812 +
            let unionNominalType = hintedNominal(source, hint);
7813 +
            try requireNominalArguments(self, unionNominalType, typeIdent);
7814 +
            try ensureNominalResolved(self, unionNominalType, typeIdent);
7815 +
            let case NominalType::Union(body) = *unionNominalType else panic;
7816 +
            let case Type::Nominal(payloadInfo) = body.variants[ordinal].valueType
6019 7817
                else throw emitError(self, node, ErrorKind::ExpectedRecord);
6020 7818
6021 7819
            // Store the variant index for the lowerer.
6022 7820
            setVariantInfo(self, node, ordinal, index);
6023 7821
6041 7839
        resultType: Type::Nominal(tyInfo),
6042 7840
    };
6043 7841
}
6044 7842
6045 7843
/// Analyze a record literal expression.
6046 -
unsafe fn resolveRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
7844 +
unsafe fn resolveRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6047 7845
    throws (ResolveError)
6048 7846
{
6049 7847
    // If no type name, infer an anonymous tuple type.
6050 7848
    let typeIdent = lit.typeName else {
6051 7849
        return try resolveAnonRecordLit(self, node, lit, hint);
6052 7850
    };
6053 7851
    // Resolve the type name, handling both record types and union variants.
6054 -
    let resolved = try resolveRecordLitType(self, node, typeIdent);
6055 -
    let tyInfo = resolved.recordType;
6056 -
    let resultType = resolved.resultType;
7852 +
    let resolved = try resolveRecordLitType(self, node, typeIdent, hint);
7853 +
    let mut tyInfo = resolved.recordType;
7854 +
    let mut resultType = resolved.resultType;
7855 +
    let mut target = hint;
7856 +
    if let case Type::Optional(inner) = target {
7857 +
        set target = *inner;
7858 +
    }
7859 +
    if nominalApplication(tyInfo) == nil {
7860 +
        if let case Type::Nominal(info) = target {
7861 +
            if let applied = nominalApplication(info); applied.base == tyInfo {
7862 +
                set tyInfo = info;
7863 +
                set resultType = target;
7864 +
            }
7865 +
        }
7866 +
    }
7867 +
    try requireNominalArguments(self, tyInfo, typeIdent);
6057 7868
6058 7869
    // Lazily resolve record body if not yet done.
6059 7870
    try ensureNominalResolved(self, tyInfo, typeIdent);
6060 7871
    let case NominalType::Record(recordType) = *tyInfo
6061 7872
        else throw emitError(self, node, ErrorKind::ExpectedRecord);
6098 7909
    }
6099 7910
    return setNodeType(self, node, resultType);
6100 7911
}
6101 7912
6102 7913
/// Analyze an anonymous record literal, checking fields against the hint type.
6103 -
unsafe fn resolveAnonRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
7914 +
unsafe fn resolveAnonRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6104 7915
    throws (ResolveError)
6105 7916
{
6106 7917
    // Unwrap optional hint to get the inner record type.
6107 7918
    let mut innerHint = hint;
6108 7919
    if let case Type::Optional(inner) = hint {
6156 7967
    }
6157 7968
    return setNodeType(self, node, innerHint);
6158 7969
}
6159 7970
6160 7971
/// Analyze an array literal expression.
6161 -
unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
7972 +
unsafe fn resolveArrayLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
6162 7973
    throws (ResolveError)
6163 7974
{
6164 7975
    let length = items.len;
6165 7976
    let mut expectedTy: Type = Type::Unknown;
6166 7977
6188 7999
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
6189 8000
    return setNodeType(self, node, arrayTy);
6190 8001
}
6191 8002
6192 8003
/// Analyze an array repeat literal expression.
6193 -
unsafe fn resolveArrayRepeat(self: &mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
8004 +
unsafe fn resolveArrayRepeat 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
6194 8005
    throws (ResolveError)
6195 8006
{
6196 8007
    let mut itemHint = hint;
6197 8008
    if let case Type::Array(ary) = hint {
6198 8009
        set itemHint = *ary.item;
6209 8020
    });
6210 8021
    return setNodeType(self, node, arrayTy);
6211 8022
}
6212 8023
6213 8024
/// Resolve union variant access.
6214 -
unsafe fn resolveUnionVariantAccess(
6215 -
    self: &mut Resolver,
8025 +
unsafe fn resolveUnionVariantAccess 'arena (
8026 +
    self: &mut Resolver 'arena,
6216 8027
    node: *ast::Node,
6217 8028
    access: ast::Access,
6218 8029
    unionType: UnionType,
6219 8030
    variantName: *[u8]
6220 8031
) -> *unsafe mut Symbol throws (ResolveError) {
6237 8048
    }
6238 8049
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
6239 8050
}
6240 8051
6241 8052
/// Analyze a scope access expression.
6242 -
unsafe fn resolveScopeAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
8053 +
unsafe fn resolveScopeAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access, hint: Type) -> Type
6243 8054
    throws (ResolveError)
6244 8055
{
6245 8056
    let scope = self.scope;
6246 8057
    let sym = try resolveAccess(self, node, access, scope);
6247 8058
    try checkStaticAccess(self, node, sym);
6260 8071
            setNodeSymbol(self, node, sym);
6261 8072
            set ty = type;
6262 8073
        }
6263 8074
        case SymbolData::Type(t) => {
6264 8075
            setNodeSymbol(self, node, sym);
6265 -
            set ty = Type::Nominal(t);
8076 +
            set ty = Type::Nominal(hintedNominal(t, hint));
6266 8077
        }
6267 8078
        case SymbolData::Variant { index, .. } => {
6268 8079
            let ty = typeFor(self, node)
6269 8080
                else throw emitError(self, node, ErrorKind::Internal);
8081 +
            let case Type::Nominal(info) = ty else panic "resolveScopeAccess: invalid variant type";
8082 +
            let applied = hintedNominal(info, hint);
8083 +
            try requireNominalArguments(self, applied, node);
8084 +
            try ensureNominalResolved(self, applied, node);
8085 +
            let variantTy = Type::Nominal(applied);
6270 8086
            // For unions without payload, store the variant index as a constant.
6271 -
            if isVoidUnion(ty) {
8087 +
            if isVoidUnion(variantTy) {
6272 8088
                setNodeConstValue(self, node, ConstValue::Int(ConstInt {
6273 8089
                    magnitude: index as u64,
6274 8090
                    bits: 32,
6275 8091
                    signed: false,
6276 8092
                    negative: false,
6277 8093
                }));
6278 8094
            }
6279 -
            return setNodeType(self, node, ty);
8095 +
            return setNodeType(self, node, variantTy);
6280 8096
        }
6281 8097
        case SymbolData::Module { .. } => {
6282 8098
            throw emitError(self, node, ErrorKind::UnexpectedModuleName);
6283 8099
        }
6284 8100
        case SymbolData::Trait(_) => { // Trait names are not values.
6287 8103
    }
6288 8104
    return setNodeType(self, node, ty);
6289 8105
}
6290 8106
6291 8107
/// Analyze a field access expression.
6292 -
unsafe fn resolveFieldAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
8108 +
unsafe fn resolveFieldAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access) -> Type
6293 8109
    throws (ResolveError)
6294 8110
{
6295 8111
    let parentTy = try infer(self, access.parent);
6296 8112
    if isUnsafePointerType(parentTy) {
6297 8113
        try requireUnsafe(self, access.parent);
6298 8114
    }
6299 8115
    let subjectTy = autoDeref(parentTy);
8116 +
    try ensureTypeResolved(self, subjectTy, access.parent);
6300 8117
6301 8118
    if let case Type::Slice { class, item, mutable } = subjectTy {
6302 8119
        let fieldNode = access.child;
6303 8120
        let fieldName = try nodeName(self, fieldNode);
6304 8121
        if mem::eq(fieldName, PTR_FIELD) {
6366 8183
            throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
6367 8184
        }
6368 8185
    }
6369 8186
}
6370 8187
6371 -
/// Check target mutability for implicit pointer access.
6372 -
unsafe fn canMutateThrough(self: &mut Resolver, node: *ast::Node) -> bool
6373 -
    throws (ResolveError)
6374 -
{
6375 -
    let ty = try infer(self, node);
8188 +
/// Return whether a pointer-like value grants mutable access.
8189 +
fn isMutablePointerLike(ty: Type) -> bool {
6376 8190
    match ty {
6377 8191
        case Type::Pointer { mutable, .. } => return mutable,
6378 8192
        case Type::Slice { mutable, .. } => return mutable,
6379 -
        else => return try canBorrowMutFrom(self, node),
8193 +
        case Type::TraitObject { mutable, .. } => return mutable,
8194 +
        else => return false,
6380 8195
    }
6381 8196
}
6382 8197
6383 -
/// Determine whether an expression can yield a mutable location for borrowing.
6384 -
unsafe fn canBorrowMutFrom(self: &mut Resolver, node: *ast::Node) -> bool
8198 +
/// Check exclusive access to an element or field of a container.
8199 +
unsafe fn canAccessExclusiveProjection 'arena (self: &mut Resolver 'arena, container: *ast::Node) -> bool
6385 8200
    throws (ResolveError)
6386 8201
{
6387 -
    match node.value {
6388 -
        case ast::NodeValue::Ident(name) => {
6389 -
            let sym = findValueSymbol(self.scope, name)
6390 -
                else return false;
6391 -
            let case SymbolData::Value { mutable, .. } = sym.data
6392 -
                else return false;
6393 -
            return mutable;
8202 +
    let ty = try infer(self, container);
8203 +
    if let case Type::Slice { mutable: false, .. } = autoDeref(ty) {
8204 +
        return false;
8205 +
    }
8206 +
    match ty {
8207 +
        case Type::Pointer { class, mutable, .. } => {
8208 +
            if not mutable {
8209 +
                return false;
8210 +
            }
8211 +
            if class == types::PointerClass::Unsafe {
8212 +
                return true;
8213 +
            }
6394 8214
        }
6395 -
        case ast::NodeValue::FieldAccess(access) => {
8215 +
        case Type::Slice { class, mutable, .. } => {
8216 +
            if not mutable {
8217 +
                return false;
8218 +
            }
8219 +
            if class == types::PointerClass::Unsafe {
8220 +
                return true;
8221 +
            }
8222 +
        }
8223 +
        else => {
8224 +
        },
8225 +
    }
8226 +
    return try canAccessExclusiveHandle(self, container);
8227 +
}
8228 +
8229 +
/// Check that a stored exclusive handle is not reached through shared access.
8230 +
unsafe fn canAccessExclusiveHandle 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8231 +
    throws (ResolveError)
8232 +
{
8233 +
    match node.value {
8234 +
        case ast::NodeValue::FieldAccess(access) =>
8235 +
            return try canAccessExclusiveProjection(self, access.parent),
8236 +
        case ast::NodeValue::Subscript { container, .. } =>
8237 +
            return try canAccessExclusiveProjection(self, container),
8238 +
        case ast::NodeValue::Deref(inner) =>
8239 +
            return try canAccessExclusiveProjection(self, inner),
8240 +
        case ast::NodeValue::As(expr) =>
8241 +
            return try canAccessExclusiveHandle(self, expr.value),
8242 +
        case ast::NodeValue::CondExpr(cond) => {
8243 +
            if not try canAccessExclusiveHandle(self, cond.thenExpr) {
8244 +
                return false;
8245 +
            }
8246 +
            return try canAccessExclusiveHandle(self, cond.elseExpr);
8247 +
        }
8248 +
        else => return true,
8249 +
    }
8250 +
}
8251 +
8252 +
/// Check target mutability for implicit pointer access.
8253 +
unsafe fn canMutateThrough 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8254 +
    throws (ResolveError)
8255 +
{
8256 +
    let ty = try infer(self, node);
8257 +
    match ty {
8258 +
        case Type::Pointer { class, mutable, .. } => {
8259 +
            if not mutable {
8260 +
                return false;
8261 +
            }
8262 +
            if class == types::PointerClass::Unsafe {
8263 +
                return true;
8264 +
            }
8265 +
            return try canAccessExclusiveHandle(self, node);
8266 +
        }
8267 +
        case Type::Slice { class, mutable, .. } => {
8268 +
            if not mutable {
8269 +
                return false;
8270 +
            }
8271 +
            if class == types::PointerClass::Unsafe {
8272 +
                return true;
8273 +
            }
8274 +
            return try canAccessExclusiveHandle(self, node);
8275 +
        }
8276 +
        case Type::TraitObject { class, mutable, .. } => {
8277 +
            if not mutable {
8278 +
                return false;
8279 +
            }
8280 +
            if class == types::PointerClass::Unsafe {
8281 +
                return true;
8282 +
            }
8283 +
            return try canAccessExclusiveHandle(self, node);
8284 +
        }
8285 +
        else => return try canBorrowMutFrom(self, node),
8286 +
    }
8287 +
}
8288 +
8289 +
/// Determine whether an expression can yield a mutable location for borrowing.
8290 +
unsafe fn canBorrowMutFrom 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8291 +
    throws (ResolveError)
8292 +
{
8293 +
    match node.value {
8294 +
        case ast::NodeValue::Ident(name) => {
8295 +
            let sym = findValueSymbol(self.scope, name)
8296 +
                else return false;
8297 +
            let case SymbolData::Value { mutable, .. } = sym.data
8298 +
                else return false;
8299 +
            return mutable;
8300 +
        }
8301 +
        case ast::NodeValue::FieldAccess(access) => {
6396 8302
            let parentTy = try infer(self, access.parent);
6397 8303
            if let case Type::Slice { .. } = autoDeref(parentTy) {
6398 8304
                try requireUnsafe(self, node);
6399 8305
            }
6400 8306
            return try canMutateThrough(self, access.parent);
6415 8321
            let containerTy = try infer(self, container);
6416 8322
            // Subscript auto-derefs pointers, so check the actual indexed type.
6417 8323
            let subjectTy = autoDeref(containerTy);
6418 8324
6419 8325
            if let case Type::Slice { mutable, .. } = subjectTy {
6420 -
                return mutable;
8326 +
                if not mutable {
8327 +
                    return false;
8328 +
                }
8329 +
                return try canMutateThrough(self, container);
6421 8330
            }
6422 8331
            if let case Type::Array(_) = subjectTy {
6423 8332
                return try canMutateThrough(self, container);
6424 8333
            }
6425 8334
            return false;
6442 8351
            return false;
6443 8352
        }
6444 8353
        case ast::NodeValue::Deref(inner) => {
6445 8354
            let innerTy = try infer(self, inner);
6446 8355
6447 -
            if let case Type::Pointer { mutable, .. } = innerTy {
6448 -
                return mutable;
8356 +
            if let case Type::Pointer { .. } = innerTy {
8357 +
                return try canMutateThrough(self, inner);
6449 8358
            }
6450 -
            if let case Type::Slice { mutable, .. } = innerTy {
6451 -
                return mutable;
8359 +
            if let case Type::Slice { .. } = innerTy {
8360 +
                return try canMutateThrough(self, inner);
6452 8361
            }
6453 8362
            // Record deref: mutability depends on the inner binding.
6454 8363
            if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy {
6455 8364
                if not recInfo.labeled and recInfo.fields.len == 1 {
6456 8365
                    return try canBorrowMutFrom(self, inner);
6462 8371
            return false;
6463 8372
        }
6464 8373
    }
6465 8374
}
6466 8375
8376 +
/// Restrict a pointee lifetime to the borrow of its exclusive owner.
8377 +
unsafe fn constrainAddressClass(storage: types::PointerClass, owner: types::PointerClass) -> types::PointerClass {
8378 +
    if owner == types::PointerClass::Owned or owner == types::PointerClass::Unsafe {
8379 +
        return storage;
8380 +
    }
8381 +
    if owner == types::PointerClass::Ref {
8382 +
        return owner;
8383 +
    }
8384 +
    let case types::PointerClass::Region(ownerRegion) = owner else panic;
8385 +
    if let case types::PointerClass::Region(storageRegion) = storage {
8386 +
        if types::regionContains(storageRegion, ownerRegion) {
8387 +
            return owner;
8388 +
        }
8389 +
        if types::regionContains(ownerRegion, storageRegion) {
8390 +
            return storage;
8391 +
        }
8392 +
        return types::PointerClass::Ref;
8393 +
    }
8394 +
    if storage == types::PointerClass::Owned {
8395 +
        return owner;
8396 +
    }
8397 +
    return storage;
8398 +
}
8399 +
8400 +
/// Get the usable pointee lifetime of a pointer or slice value.
8401 +
unsafe fn pointerAddressClass 'arena (
8402 +
    self: &Resolver 'arena, node: *ast::Node, class: types::PointerClass, mutable: bool
8403 +
) -> types::PointerClass {
8404 +
    if not mutable or class == types::PointerClass::Unsafe {
8405 +
        return class;
8406 +
    }
8407 +
    return constrainAddressClass(class, exclusiveOwnerClass(self, node));
8408 +
}
8409 +
8410 +
/// Get the lifetime of storage selected by a pointer or slice subscript.
8411 +
unsafe fn indexedPointerClass 'arena (self: &Resolver 'arena, container: *ast::Node) -> ?types::PointerClass {
8412 +
    let ty = typeFor(self, container) else return nil;
8413 +
    if let case Type::Pointer { class, target, mutable } = ty {
8414 +
        let parentClass = pointerAddressClass(self, container, class, mutable);
8415 +
        if let case Type::Slice { class: sliceClass, mutable: sliceMutable, .. } = *target {
8416 +
            if not sliceMutable or sliceClass == types::PointerClass::Unsafe {
8417 +
                return sliceClass;
8418 +
            }
8419 +
            return constrainAddressClass(sliceClass, parentClass);
8420 +
        }
8421 +
        return parentClass;
8422 +
    }
8423 +
    if let case Type::Slice { class, mutable, .. } = ty {
8424 +
        return pointerAddressClass(self, container, class, mutable);
8425 +
    }
8426 +
    return nil;
8427 +
}
8428 +
8429 +
/// Get the borrow that controls access to a stored exclusive handle.
8430 +
/// Directly owned values have no additional borrow restriction.
8431 +
unsafe fn exclusiveOwnerClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
8432 +
    match node.value {
8433 +
        case ast::NodeValue::FieldAccess(access) => {
8434 +
            if let ty = typeFor(self, access.parent) {
8435 +
                match ty {
8436 +
                    case Type::Pointer { class, mutable, .. } =>
8437 +
                        return pointerAddressClass(self, access.parent, class, mutable),
8438 +
                    case Type::Slice { class, mutable, .. } =>
8439 +
                        return pointerAddressClass(self, access.parent, class, mutable),
8440 +
                    else => {
8441 +
                    },
8442 +
                }
8443 +
            }
8444 +
            return exclusiveOwnerClass(self, access.parent);
8445 +
        }
8446 +
        case ast::NodeValue::Subscript { container, .. } => {
8447 +
            if let class = indexedPointerClass(self, container) {
8448 +
                return class;
8449 +
            }
8450 +
            return exclusiveOwnerClass(self, container);
8451 +
        }
8452 +
        case ast::NodeValue::Deref(target) => {
8453 +
            if let ty = typeFor(self, target) {
8454 +
                if let case Type::Pointer { class, mutable, .. } = ty {
8455 +
                    return pointerAddressClass(self, target, class, mutable);
8456 +
                }
8457 +
            }
8458 +
            return exclusiveOwnerClass(self, target);
8459 +
        }
8460 +
        case ast::NodeValue::As(expr) => return exclusiveOwnerClass(self, expr.value),
8461 +
        case ast::NodeValue::CondExpr(cond) =>
8462 +
            return constrainAddressClass(exclusiveOwnerClass(self, cond.thenExpr), exclusiveOwnerClass(self, cond.elseExpr)),
8463 +
        else => return types::PointerClass::Owned,
8464 +
    }
8465 +
}
8466 +
6467 8467
/// Return the storage class of an addressed location.
6468 -
unsafe fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass {
8468 +
unsafe fn addressStorageClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
6469 8469
    match node.value {
6470 8470
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
6471 8471
            if let sym = symbolFor(self, node) {
6472 8472
                match sym.node.value {
6473 8473
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
6476 8476
                }
6477 8477
            }
6478 8478
        }
6479 8479
        case ast::NodeValue::FieldAccess(access) => {
6480 8480
            if let ty = typeFor(self, access.parent) {
6481 -
                if let case Type::Pointer { class, .. } = ty {
6482 -
                    return class;
8481 +
                if let case Type::Pointer { class, mutable, .. } = ty {
8482 +
                    return pointerAddressClass(self, access.parent, class, mutable);
6483 8483
                }
6484 8484
            }
6485 8485
            return addressStorageClass(self, access.parent);
6486 8486
        }
6487 8487
        case ast::NodeValue::Subscript { container, .. } => {
6488 -
            if let ty = typeFor(self, container) {
6489 -
                if let case Type::Slice { class, .. } = autoDeref(ty) {
6490 -
                    return class;
6491 -
                }
6492 -
                if let case Type::Pointer { class, .. } = ty {
6493 -
                    return class;
6494 -
                }
8488 +
            if let class = indexedPointerClass(self, container) {
8489 +
                return class;
6495 8490
            }
6496 8491
            return addressStorageClass(self, container);
6497 8492
        }
6498 8493
        case ast::NodeValue::Deref(target) => {
6499 8494
            if let ty = typeFor(self, target) {
6500 -
                if let case Type::Pointer { class, .. } = ty {
6501 -
                    return class;
8495 +
                if let case Type::Pointer { class, mutable, .. } = ty {
8496 +
                    return pointerAddressClass(self, target, class, mutable);
6502 8497
                }
6503 8498
            }
6504 8499
            return addressStorageClass(self, target);
6505 8500
        }
6506 8501
        else => {}
6507 8502
    }
6508 8503
    return types::PointerClass::Ref;
6509 8504
}
6510 8505
6511 8506
/// Select an address type without extending the target storage lifetime.
6512 -
unsafe fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass
8507 +
unsafe fn addressClass 'arena (self: &mut Resolver 'arena, target: *ast::Node, hint: Type) -> types::PointerClass
6513 8508
    throws (ResolveError)
6514 8509
{
6515 8510
    if isUnsafePointerType(hint) {
6516 8511
        try requireUnsafe(self, target);
6517 8512
        return types::PointerClass::Unsafe;
6518 8513
    }
6519 8514
    if isRefType(hint) {
8515 +
        if referenceRegion(hint) <> nil {
8516 +
            return addressStorageClass(self, target);
8517 +
        }
6520 8518
        return types::PointerClass::Ref;
6521 8519
    }
6522 8520
    match target.value {
6523 8521
        case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => {
6524 8522
            if isConstExpr(self, target) {
6528 8526
        else => {}
6529 8527
    }
6530 8528
    return addressStorageClass(self, target);
6531 8529
}
6532 8530
8531 +
/// Return whether a place projects into a cell payload snapshot.
8532 +
unsafe fn isCellPayloadPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8533 +
    throws (ResolveError)
8534 +
{
8535 +
    match node.value {
8536 +
        case ast::NodeValue::Deref(target) => {
8537 +
            if let case Type::Cell { .. } = try infer(self, target) {
8538 +
                return true;
8539 +
            }
8540 +
        }
8541 +
        case ast::NodeValue::FieldAccess(access) => return try isCellPayloadPlace(self, access.parent),
8542 +
        case ast::NodeValue::Subscript { container, .. } => return try isCellPayloadPlace(self, container),
8543 +
        else => {}
8544 +
    }
8545 +
    return false;
8546 +
}
8547 +
8548 +
/// Return whether a typed expression accesses a whole cell payload.
8549 +
export unsafe fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
8550 +
    if let case ast::NodeValue::Deref(target) = node.value {
8551 +
        if let ty = typeFor(self, target) {
8552 +
            if let case Type::Cell { .. } = ty {
8553 +
                return true;
8554 +
            }
8555 +
        }
8556 +
    }
8557 +
    return false;
8558 +
}
8559 +
6533 8560
/// Analyze an address-of expression.
6534 -
unsafe fn resolveAddressOf(self: &mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
8561 +
unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
6535 8562
    throws (ResolveError)
6536 8563
{
8564 +
    if try isCellPayloadPlace(self, addr.target) {
8565 +
        throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8566 +
    }
6537 8567
    if addr.mutable {
6538 8568
        if not try canBorrowMutFrom(self, addr.target) {
6539 8569
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6540 8570
        }
6541 8571
    }
6615 8645
    };
6616 8646
    return setNodeType(self, node, pointerTy);
6617 8647
}
6618 8648
6619 8649
/// Analyze a dereference expression.
6620 -
unsafe fn resolveDeref(self: &mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
8650 +
unsafe fn resolveDeref 'arena (self: &mut Resolver 'arena, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
6621 8651
    throws (ResolveError)
6622 8652
{
6623 8653
    let operandTy = try visit(self, targetNode, hint);
8654 +
    if let case Type::Cell { class, payload } = operandTy {
8655 +
        if class == types::PointerClass::Unsafe {
8656 +
            try requireUnsafe(self, targetNode);
8657 +
        }
8658 +
        try validateCellPayload(self, node, *payload);
8659 +
        return setNodeType(self, node, *payload);
8660 +
    }
6624 8661
    if let case Type::Pointer { class, target, .. } = operandTy {
6625 8662
        if class == types::PointerClass::Unsafe {
6626 8663
            try requireUnsafe(self, targetNode);
6627 8664
        }
6628 8665
        // Disallow dereferencing opaque pointers.
6722 8759
        }
6723 8760
    }
6724 8761
    return false;
6725 8762
}
6726 8763
8764 +
/// Require casts into region-dependent storage to preserve its typed contents.
8765 +
fn regionalCastPreservesType(source: Type, target: Type) -> bool {
8766 +
    if typesEqual(source, target) {
8767 +
        return true;
8768 +
    }
8769 +
    if let case Type::Pointer { target: sourceItem, .. } = source {
8770 +
        let case Type::Pointer { target: targetItem, .. } = target else return false;
8771 +
        return typesEqual(*sourceItem, *targetItem);
8772 +
    }
8773 +
    if let case Type::Slice { item: sourceItem, .. } = source {
8774 +
        let case Type::Slice { item: targetItem, .. } = target else return false;
8775 +
        return typesEqual(*sourceItem, *targetItem);
8776 +
    }
8777 +
    return false;
8778 +
}
8779 +
6727 8780
/// Analyze an `as` cast expression.
6728 -
unsafe fn resolveAs(self: &mut Resolver, node: *ast::Node, expr: ast::As) -> Type
8781 +
unsafe fn resolveAs 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: ast::As) -> Type
6729 8782
    throws (ResolveError)
6730 8783
{
6731 8784
    let targetTy = try infer(self, expr.type);
6732 8785
    let sourceTy = try visit(self, expr.value, targetTy);
6733 8786
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6735 8788
    }
6736 8789
6737 8790
    assert sourceTy <> Type::Unknown;
6738 8791
    assert targetTy <> Type::Unknown;
6739 8792
8793 +
    if let case Type::Cell { class, payload } = targetTy {
8794 +
        if let case Type::Pointer { class: sourceClass, target, mutable: true } = sourceTy;
8795 +
            sourceClass == class and class <> types::PointerClass::Unsafe and typesEqual(*target, *payload)
8796 +
        {
8797 +
            return setNodeType(self, node, targetTy);
8798 +
        }
8799 +
        if typesEqual(sourceTy, targetTy) {
8800 +
            return setNodeType(self, node, targetTy);
8801 +
        }
8802 +
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { from: sourceTy, to: targetTy }));
8803 +
    }
8804 +
    if containsRegion(targetTy) and not regionalCastPreservesType(sourceTy, targetTy) {
8805 +
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
8806 +
            from: sourceTy, to: targetTy,
8807 +
        }));
8808 +
    }
6740 8809
    let mut valid = isValidCast(sourceTy, targetTy);
6741 8810
    if let case Type::Pointer {
6742 8811
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6743 8812
    } = sourceTy {
6744 8813
        if let case Type::Pointer {
6745 8814
            class: targetClass, target: targetTarget, mutable: targetMutable,
6746 8815
        } = targetTy {
6747 -
            if sourceClass == types::PointerClass::Ref and
8816 +
            if types::isReference(sourceClass) and
6748 8817
               targetClass == types::PointerClass::Unsafe and
6749 8818
               (not targetMutable or sourceMutable) and
6750 8819
               isValidCast(*sourceTarget, *targetTarget)
6751 8820
            {
6752 8821
                set valid = true;
6757 8826
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6758 8827
    } = sourceTy {
6759 8828
        if let case Type::Slice {
6760 8829
            class: targetClass, item: targetItem, mutable: targetMutable,
6761 8830
        } = targetTy {
6762 -
            if sourceClass == types::PointerClass::Ref and
8831 +
            if types::isReference(sourceClass) and
6763 8832
               targetClass == types::PointerClass::Unsafe and
6764 8833
               (not targetMutable or sourceMutable) and
6765 8834
               isValidCast(*sourceItem, *targetItem)
6766 8835
            {
6767 8836
                set valid = true;
6797 8866
        to: targetTy,
6798 8867
    }));
6799 8868
}
6800 8869
6801 8870
/// Analyze a range expression.
6802 -
unsafe fn resolveRange(self: &mut Resolver, node: *ast::Node, range: ast::Range) -> Type
8871 +
unsafe fn resolveRange 'arena (self: &mut Resolver 'arena, node: *ast::Node, range: ast::Range) -> Type
6803 8872
    throws (ResolveError)
6804 8873
{
6805 8874
    let mut start: ?*Type = nil;
6806 8875
    let mut end: ?*Type = nil;
6807 8876
6834 8903
}
6835 8904
6836 8905
/// Analyze a `try` expression and its handlers.
6837 8906
/// The `expected` type is used to determine if the value is discarded (`Void`)
6838 8907
/// or if the catch expression needs type checking.
6839 -
unsafe fn resolveTry(self: &mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
8908 +
unsafe fn resolveTry 'arena (self: &mut Resolver 'arena, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
6840 8909
    throws (ResolveError)
6841 8910
{
6842 8911
    let call = tryExpr.expr;
6843 8912
    let case ast::NodeValue::Call(callExpr) = call.value
6844 8913
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
6845 -
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try);
8914 +
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try, Type::Unknown);
6846 8915
6847 8916
    // TODO: It's annoying that we need to re-fetch the function type after
6848 8917
    // analyzing the call.
6849 8918
    let calleeTy = typeFor(self, callExpr.callee)
6850 8919
        else return setNodeType(self, node, resultTy);
6892 8961
    return setNodeType(self, node, tryResultTy);
6893 8962
}
6894 8963
6895 8964
/// Check that a `catch` body is assignable to the expected result type, but only
6896 8965
/// in expression context (`hint` is neither `Unknown` nor `Void`).
6897 -
unsafe fn checkCatchBody(self: &mut Resolver, body: *ast::Node, resultTy: Type, hint: Type)
8966 +
unsafe fn checkCatchBody 'arena (self: &mut Resolver 'arena, body: *ast::Node, resultTy: Type, hint: Type)
6898 8967
    throws (ResolveError)
6899 8968
{
6900 8969
    if hint <> Type::Unknown and hint <> Type::Void {
6901 8970
        try checkAssignable(self, body, resultTy);
6902 8971
    }
6905 8974
/// Resolve catch clauses for a `try ... catch` expression.
6906 8975
///
6907 8976
/// For a single untyped catch (with or without binding), resolves the catch
6908 8977
/// body and returns the result type. Multi-error callees with inferred bindings
6909 8978
/// are rejected; you must use typed catches.
6910 -
unsafe fn resolveTryCatches(
6911 -
    self: &mut Resolver,
8979 +
unsafe fn resolveTryCatches 'arena (
8980 +
    self: &mut Resolver 'arena,
6912 8981
    node: *ast::Node,
6913 8982
    catches: *[*ast::Node],
6914 8983
    calleeInfo: *FnType,
6915 8984
    resultTy: Type,
6916 8985
    hint: Type
6945 9014
6946 9015
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
6947 9016
///
6948 9017
/// Validates that each type annotation is in the callee's throw list, that
6949 9018
/// there are no duplicate catch types, and that the clauses are exhaustive.
6950 -
unsafe fn resolveTypedCatches(
6951 -
    self: &mut Resolver,
9019 +
unsafe fn resolveTypedCatches 'arena (
9020 +
    self: &mut Resolver 'arena,
6952 9021
    node: *ast::Node,
6953 9022
    catches: *[*ast::Node],
6954 9023
    calleeInfo: *FnType,
6955 9024
    resultTy: Type,
6956 9025
    hint: Type
7012 9081
    }
7013 9082
    return catchTy if resultTy == Type::Never else resultTy;
7014 9083
}
7015 9084
7016 9085
/// Analyze a `throw` statement.
7017 -
unsafe fn resolveThrow(self: &mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type
9086 +
unsafe fn resolveThrow 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: *ast::Node) -> Type
7018 9087
    throws (ResolveError)
7019 9088
{
7020 9089
    let fnInfo = self.currentFn
7021 9090
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
7022 9091
    if fnInfo.throwList.len == 0 {
7031 9100
    }
7032 9101
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
7033 9102
}
7034 9103
7035 9104
/// Analyze a `return` statement.
7036 -
unsafe fn resolveReturn(self: &mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type
9105 +
unsafe fn resolveReturn 'arena (self: &mut Resolver 'arena, node: *ast::Node, retVal: ?*ast::Node) -> Type
7037 9106
    throws (ResolveError)
7038 9107
{
7039 9108
    let f = self.currentFn
7040 9109
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
7041 9110
    let expected = *f.returnType;
7177 9246
    }
7178 9247
}
7179 9248
7180 9249
/// Try to constant-fold a binary operation on two resolved operands.
7181 9250
/// Only folds when the result type is concrete.
7182 -
fn tryFoldBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
9251 +
fn tryFoldBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
7183 9252
    let leftVal = constValueEntry(self, binop.left)
7184 9253
        else return;
7185 9254
    let rightVal = constValueEntry(self, binop.right)
7186 9255
        else return;
7187 9256
7209 9278
        }
7210 9279
    }
7211 9280
}
7212 9281
7213 9282
/// Analyze a binary expression.
7214 -
unsafe fn resolveBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type
9283 +
unsafe fn resolveBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp) -> Type
7215 9284
    throws (ResolveError)
7216 9285
{
7217 9286
    let mut resultTy = Type::Unknown;
7218 9287
7219 9288
    match binop.op {
7267 9336
                // never on references.
7268 9337
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
7269 9338
                    if *leftTarget == Type::Opaque {
7270 9339
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7271 9340
                    }
7272 -
                    if leftClass <> types::PointerClass::Ref
9341 +
                    if not types::isReference(leftClass)
7273 9342
                        and isNumericType(rightTy)
7274 9343
                    {
7275 9344
                        try requireUnsafe(self, node);
7276 9345
                        return setNodeType(self, node, leftTy);
7277 9346
                    }
7279 9348
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
7280 9349
                    if *rightTarget == Type::Opaque {
7281 9350
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7282 9351
                    }
7283 9352
                    if binop.op == ast::BinaryOp::Add
7284 -
                        and rightClass <> types::PointerClass::Ref
9353 +
                        and not types::isReference(rightClass)
7285 9354
                        and isNumericType(leftTy)
7286 9355
                    {
7287 9356
                        try requireUnsafe(self, node);
7288 9357
                        return setNodeType(self, node, rightTy);
7289 9358
                    }
7320 9389
7321 9390
    return setNodeType(self, node, resultTy);
7322 9391
}
7323 9392
7324 9393
/// Analyze a unary expression.
7325 -
unsafe fn resolveUnOp(self: &mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type
9394 +
unsafe fn resolveUnOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, unop: ast::UnOp) -> Type
7326 9395
    throws (ResolveError)
7327 9396
{
7328 9397
    let mut resultTy = Type::Unknown;
7329 9398
7330 9399
    match unop.op {
7365 9434
    };
7366 9435
    return setNodeType(self, node, resultTy);
7367 9436
}
7368 9437
7369 9438
/// Resolve a type signature node and set its type.
7370 -
unsafe fn inferTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
9439 +
unsafe fn inferTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
7371 9440
    throws (ResolveError)
7372 9441
{
7373 9442
    let resolved = try resolveTypeSig(self, node, sig);
7374 9443
7375 9444
    return setNodeType(self, node, resolved);
7376 9445
}
7377 9446
9447 +
/// Convert a parsed pointer qualifier to its semantic class.
9448 +
fn resolvePointerClass(class: ast::PointerClass) -> types::PointerClass {
9449 +
    match class {
9450 +
        case ast::PointerClass::Owned => return types::PointerClass::Owned,
9451 +
        case ast::PointerClass::Ref => return types::PointerClass::Ref,
9452 +
        case ast::PointerClass::Unsafe => return types::PointerClass::Unsafe,
9453 +
    }
9454 +
}
9455 +
7378 9456
/// Convert a type signature node into a type value.
7379 -
unsafe fn resolveTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
9457 +
unsafe fn resolveTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
7380 9458
    throws (ResolveError)
7381 9459
{
7382 9460
    match sig {
9461 +
        case ast::TypeSig::Cell { class, payload } => {
9462 +
            let inner = try infer(self, payload);
9463 +
            try validateCellPayload(self, node, inner);
9464 +
            return Type::Cell { class: resolvePointerClass(class), payload: allocType(self, inner) };
9465 +
        }
9466 +
        case ast::TypeSig::RegionRef { region, type } => {
9467 +
            let identity = try resolveRegion(self, region);
9468 +
            let base = try infer(self, type);
9469 +
            match base {
9470 +
                case Type::Cell { payload, .. } =>
9471 +
                    return Type::Cell { class: types::PointerClass::Region(identity), payload },
9472 +
                case Type::Pointer { target, mutable, .. } =>
9473 +
                    return Type::Pointer { class: types::PointerClass::Region(identity), target, mutable },
9474 +
                case Type::Slice { item, mutable, .. } =>
9475 +
                    return Type::Slice { class: types::PointerClass::Region(identity), item, mutable },
9476 +
                case Type::TraitObject { traitInfo, mutable, .. } =>
9477 +
                    return Type::TraitObject { class: types::PointerClass::Region(identity), traitInfo, mutable },
9478 +
                else => throw emitError(self, node, ErrorKind::InvalidRefPosition),
9479 +
            }
9480 +
        }
9481 +
        case ast::TypeSig::Applied { name, regions } => {
9482 +
            if let case ast::NodeValue::Ident(spelling) = name.value;
9483 +
                mem::eq(spelling, "Session") and findTypeSymbol(self.scope, spelling) == nil
9484 +
            {
9485 +
                if regions.len <> 1 {
9486 +
                    throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch {
9487 +
                        expected: 1, actual: regions.len,
9488 +
                    }));
9489 +
                }
9490 +
                return Type::Session(try resolveRegion(self, regions[0]));
9491 +
            }
9492 +
            let base = try resolveTypeName(self, name);
9493 +
            return Type::Nominal(try applyNominalRegions(self, base, regions, node));
9494 +
        }
7383 9495
        case ast::TypeSig::Void => {
7384 9496
            return Type::Void;
7385 9497
        }
7386 9498
        case ast::TypeSig::Never => {
7387 9499
            return Type::Never;
7411 9523
            return Type::Array(ArrayType { item: allocType(self, item), length });
7412 9524
        }
7413 9525
        case ast::TypeSig::Slice { class, itemType, mutable } => {
7414 9526
            let item = try infer(self, itemType);
7415 9527
            return Type::Slice {
7416 -
                class,
9528 +
                class: resolvePointerClass(class),
7417 9529
                item: allocType(self, item),
7418 9530
                mutable,
7419 9531
            };
7420 9532
        }
7421 9533
        case ast::TypeSig::Pointer { class, valueType, mutable } => {
7422 9534
            let target = try infer(self, valueType);
7423 9535
            return Type::Pointer {
7424 -
                class,
9536 +
                class: resolvePointerClass(class),
7425 9537
                target: allocType(self, target),
7426 9538
                mutable,
7427 9539
            };
7428 9540
        }
7429 9541
        case ast::TypeSig::Optional { valueType } => {
7430 9542
            let payload = try infer(self, valueType);
7431 9543
            return Type::Optional(allocType(self, payload));
7432 9544
        }
7433 9545
        case ast::TypeSig::Nominal(name) => {
7434 9546
            let ty = try resolveTypeName(self, name);
9547 +
            try requireNominalArguments(self, ty, node);
7435 9548
            return Type::Nominal(ty);
7436 9549
        }
7437 9550
        case ast::TypeSig::Record { fields, labeled } => {
7438 9551
            let mut recordType = try resolveRecordFields(self, node, fields, labeled);
7439 9552
            set recordType.declaredCopy = true;
7440 9553
            for field in recordType.fields {
7441 9554
                if not isCopy(field.fieldType) {
7442 9555
                    set recordType.declaredCopy = false;
7443 9556
                }
7444 9557
            }
9558 +
            set recordType.regions = self.regionScope;
7445 9559
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
9560 +
            if let scope = self.regionScope {
9561 +
                let map = regionSubstitution(self, scope);
9562 +
                for parameter, i in scope.entries {
9563 +
                    set map.arguments[i] = parameter;
9564 +
                }
9565 +
                return Type::Nominal(internNominalApplication(self, nominalTy, &map));
9566 +
            }
7446 9567
            return Type::Nominal(nominalTy);
7447 9568
        }
7448 9569
        case ast::TypeSig::Fn { sig: t, isUnsafe } => {
7449 -
            let a = alloc::arenaAllocator(&mut self.arena);
9570 +
            let a = alloc::arenaAllocator(self.arena);
7450 9571
            let mut paramTypes: *mut [*Type] = &mut [];
7451 9572
            let mut throwList: *mut [*Type] = &mut [];
7452 9573
7453 9574
            if t.params.len > MAX_FN_PARAMS {
7454 9575
                throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
7468 9589
                paramTypes.append(allocType(self, paramTy), a);
7469 9590
            }
7470 9591
            for tyNode in t.throwList {
7471 9592
                let throwTy = try resolveValueType(self, tyNode);
7472 9593
                try ensureStorableType(self, tyNode, throwTy);
9594 +
                try validateErrorTag(self, tyNode, throwTy, &throwList[..]);
7473 9595
                throwList.append(allocType(self, throwTy), a);
7474 9596
            }
7475 9597
            let mut retType = allocType(self, Type::Void);
7476 9598
            if let ret = t.returnType {
7477 9599
                let resolvedRet = try resolveValueType(self, ret);
7478 9600
                try ensureStorableType(self, ret, resolvedRet);
7479 9601
                set retType = allocType(self, resolvedRet);
7480 9602
            }
7481 9603
            let fnType = FnType {
9604 +
                regions: nil,
7482 9605
                paramTypes: &paramTypes[..],
7483 9606
                returnType: retType,
7484 9607
                throwList: &throwList[..],
7485 9608
                isUnsafe,
7486 9609
            };
7491 9614
            let sym = try resolveNamePath(self, traitName);
7492 9615
            let case SymbolData::Trait(traitInfo) = sym.data
7493 9616
                else throw emitError(self, traitName, ErrorKind::Internal);
7494 9617
            setNodeSymbol(self, traitName, sym);
7495 9618
7496 -
            return Type::TraitObject { class, traitInfo, mutable };
9619 +
            return Type::TraitObject { class: resolvePointerClass(class), traitInfo, mutable };
7497 9620
        }
7498 9621
    }
7499 9622
}
7500 9623
7501 9624
/// Check if a type can be used for inferrence.
7510 9633
        else => return true,
7511 9634
    }
7512 9635
}
7513 9636
7514 9637
/// Analyze a standalone expression by wrapping it in a synthetic function.
7515 -
export unsafe fn resolveExpr(
7516 -
    self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena
9638 +
export unsafe fn resolveExpr 'arena (
9639 +
    self: &mut Resolver 'arena, expr: *ast::Node, arena: &mut ast::NodeArena
7517 9640
) -> Diagnostics throws (ResolveError) {
7518 9641
    let a = alloc::arenaAllocator(&mut arena.arena);
7519 9642
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
7520 9643
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
7521 9644
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
7533 9656
7534 9657
    return diagnostics(self);
7535 9658
}
7536 9659
7537 9660
/// Analyze a parsed module root, ie. a block of top-level statements.
7538 -
export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
9661 +
export unsafe fn resolveModuleRoot 'arena (self: &mut Resolver 'arena, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7539 9662
    let case ast::NodeValue::Block(block) = root.value
7540 9663
        else panic "resolveModuleRoot: expected block for module root";
7541 9664
7542 9665
    enterScope(self, root);
7543 9666
    try resolveModuleDecls(self, &block) catch {
7553 9676
}
7554 9677
7555 9678
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
7556 9679
/// and scopes for them, and also binds type names in each module so that cross-module
7557 9680
/// type references work regardless of declaration order.
7558 -
unsafe fn resolveModuleGraph(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
9681 +
unsafe fn resolveModuleGraph 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
7559 9682
    try bindTypeNames(self, block);
7560 9683
7561 9684
    for node in block.statements {
7562 9685
        if let case ast::NodeValue::Mod(decl) = node.value {
7563 9686
            try resolveModGraph(self, node, decl);
7565 9688
    }
7566 9689
}
7567 9690
7568 9691
/// Bind all type names in a module.
7569 9692
/// Skips declarations that have already been bound.
7570 -
unsafe fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
9693 +
unsafe fn bindTypeNames 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
7571 9694
    for node in block.statements {
7572 9695
        match node.value {
7573 9696
            case ast::NodeValue::RecordDecl(decl) => {
7574 9697
                if symbolFor(self, node) == nil {
7575 9698
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7589 9712
        }
7590 9713
    }
7591 9714
}
7592 9715
7593 9716
/// Resolve all type bodies in a module.
7594 -
unsafe fn resolveTypeBodies(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
9717 +
unsafe fn resolveTypeBodies 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
7595 9718
    for node in block.statements {
7596 9719
        match node.value {
7597 9720
            case ast::NodeValue::RecordDecl(decl) => {
7598 9721
                try resolveRecordBody(self, node, decl) catch {
7599 9722
                    // Continue resolving other types even if one fails.
7622 9745
/// previous pass.
7623 9746
///
7624 9747
/// This function uses a two-phase approach:
7625 9748
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
7626 9749
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
7627 -
unsafe fn resolveModuleDecls(res: &mut Resolver, block: &ast::Block) throws (ResolveError) {
9750 +
unsafe fn resolveModuleDecls 'arena (res: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
7628 9751
    // Phase 1: Bind all type names as placeholders.
7629 9752
    try bindTypeNames(res, block);
7630 9753
    // Phase 2: Process imports so names available from the module graph can
7631 9754
    // be used in function signatures.
7632 9755
    for node in block.statements {
7688 9811
fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool {
7689 9812
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7690 9813
}
7691 9814
7692 9815
/// Add a local binding when its resolved type moves by value.
7693 -
unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node)
7694 -
    throws (ResolveError)
9816 +
unsafe fn addLinearBinding 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
9817 +
    throws (ResolveError) where 'arena: 'checking
7695 9818
{
7696 9819
    let sym = symbolFor(checker.resolver, node) else return;
7697 9820
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7698 9821
    if not isMoveOnly(ty) {
7699 9822
        return;
7705 9828
    set env.available |= (1 as u64) << (env.len as u64);
7706 9829
    set env.len += 1;
7707 9830
}
7708 9831
7709 9832
/// Mark a tracked binding as uninitialized.
7710 -
unsafe fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) {
9833 +
unsafe fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) {
7711 9834
    let sym = symbolFor(self, node) else return;
7712 9835
    let index = findLinearBinding(env, sym) else return;
7713 9836
    set env.available &= ~((1 as u64) << (index as u64));
7714 9837
}
7715 9838
7716 9839
/// Require exact-use bindings introduced after `start` to be consumed.
7717 -
unsafe fn finishLinearScope(
7718 -
    checker: &mut LinearChecker,
9840 +
unsafe fn finishLinearScope 'arena 'checking (
9841 +
    checker: &mut LinearChecker 'arena 'checking,
7719 9842
    env: &mut LinearEnv,
7720 9843
    start: u32,
7721 -
) throws (ResolveError) {
9844 +
) throws (ResolveError) where 'arena: 'checking {
7722 9845
    if not env.terminated {
7723 9846
        for i in start..env.len {
7724 9847
            if linearBindingAvailable(env, i) {
7725 9848
                let sym = env.symbols[i];
7726 9849
                let case SymbolData::Value { type: ty, .. } = sym.data
7737 9860
    }
7738 9861
    set env.len = start;
7739 9862
}
7740 9863
7741 9864
/// Require a tracked identifier to remain available for any access.
7742 -
unsafe fn checkLinearIdent(
7743 -
    checker: &mut LinearChecker,
9865 +
unsafe fn checkLinearIdent 'arena 'checking (
9866 +
    checker: &mut LinearChecker 'arena 'checking,
7744 9867
    env: &mut LinearEnv,
7745 9868
    node: *ast::Node,
7746 -
) throws (ResolveError) {
9869 +
) throws (ResolveError) where 'arena: 'checking {
7747 9870
    let sym = symbolFor(checker.resolver, node) else return;
7748 9871
    let index = findLinearBinding(env, sym) else return;
7749 9872
    if not linearBindingAvailable(env, index) {
7750 9873
        let case SymbolData::Value { type: ty, .. } = sym.data
7751 9874
            else panic "consumeLinearIdent: expected value symbol";
7754 9877
        throw emitError(checker.resolver, node, kind);
7755 9878
    }
7756 9879
}
7757 9880
7758 9881
/// Move or consume a tracked identifier once.
7759 -
unsafe fn consumeLinearIdent(
7760 -
    checker: &mut LinearChecker,
9882 +
unsafe fn consumeLinearIdent 'arena 'checking (
9883 +
    checker: &mut LinearChecker 'arena 'checking,
7761 9884
    env: &mut LinearEnv,
7762 9885
    node: *ast::Node,
7763 -
) throws (ResolveError) {
9886 +
) throws (ResolveError) where 'arena: 'checking {
7764 9887
    try checkLinearIdent(checker, env, node);
7765 9888
    let sym = symbolFor(checker.resolver, node) else return;
7766 9889
    let index = findLinearBinding(env, sym) else return;
7767 9890
    set env.available &= ~((1 as u64) << (index as u64));
7768 9891
}
7769 9892
7770 9893
/// Merge ownership availability across two live branches.
7771 9894
/// Validate both inputs before writing to an output that can alias either input.
7772 -
unsafe fn joinLinearBranches(
7773 -
    checker: &mut LinearChecker,
9895 +
unsafe fn joinLinearBranches 'arena 'checking (
9896 +
    checker: &mut LinearChecker 'arena 'checking,
7774 9897
    env: &mut LinearEnv,
7775 9898
    left: &LinearEnv,
7776 9899
    right: &LinearEnv,
7777 9900
    node: *ast::Node,
7778 -
) throws (ResolveError) {
9901 +
) throws (ResolveError) where 'arena: 'checking {
7779 9902
    if left.terminated and right.terminated {
7780 9903
        set *env = *left;
7781 9904
        set env.terminated = true;
7782 9905
        return;
7783 9906
    }
7804 9927
                );
7805 9928
            }
7806 9929
            set available &= ~((1 as u64) << (i as u64));
7807 9930
        }
7808 9931
    }
9932 +
    let regionalLoans = left.regionalLoans | right.regionalLoans;
7809 9933
    set *env = *left;
7810 9934
    set env.available = available;
9935 +
    set env.regionalLoans = regionalLoans;
7811 9936
}
7812 9937
7813 9938
/// Require all available exact-use bindings to be consumed at a function exit.
7814 -
unsafe fn finishLinearExit(
7815 -
    checker: &mut LinearChecker,
9939 +
unsafe fn finishLinearExit 'arena 'checking (
9940 +
    checker: &mut LinearChecker 'arena 'checking,
7816 9941
    env: &mut LinearEnv,
7817 -
) throws (ResolveError) {
9942 +
) throws (ResolveError) where 'arena: 'checking {
7818 9943
    if env.terminated {
7819 9944
        return;
7820 9945
    }
7821 9946
    for i in 0..env.len {
7822 9947
        if linearBindingAvailable(env, i) {
7834 9959
    }
7835 9960
    set env.terminated = true;
7836 9961
}
7837 9962
7838 9963
/// Find the local root borrowed or consumed by an argument expression.
7839 -
fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Symbol {
9964 +
fn linearRootSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
7840 9965
    match node.value {
7841 9966
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) =>
7842 9967
            return symbolFor(self, node),
7843 9968
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
7844 9969
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7849 9974
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
7850 9975
        else => return nil,
7851 9976
    }
7852 9977
}
7853 9978
9979 +
/// A projection loan that remains active until its named region ends.
9980 +
record RegionalLoan: Copy {
9981 +
    /// Address expression that supplies access to the loan.
9982 +
    source: *ast::Node,
9983 +
    /// Declared lifetime of the projection.
9984 +
    region: *unsafe types::Region,
9985 +
    /// Storage protected by the projection.
9986 +
    place: BorrowPlace,
9987 +
    /// Whether accesses through other references are excluded.
9988 +
    exclusive: bool,
9989 +
}
9990 +
9991 +
/// Return whether a region identity is visible in a lexical environment.
9992 +
unsafe fn regionInScope(scope: ?*RegionScope, region: *unsafe types::Region) -> bool {
9993 +
    let mut cursor = scope;
9994 +
    while let current = cursor {
9995 +
        for entry in current.entries {
9996 +
            if entry.id == region.id {
9997 +
                return true;
9998 +
            }
9999 +
        }
10000 +
        set cursor = current.parent;
10001 +
    }
10002 +
    return false;
10003 +
}
10004 +
10005 +
/// Retain only loans whose regions remain active at a control-flow destination.
10006 +
unsafe fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking {
10007 +
    let mut result: u64 = 0;
10008 +
    for i in 0..checker.regionalLen {
10009 +
        let bit = (1 as u64) << (i as u64);
10010 +
        if (mask & bit) <> 0 and regionInScope(scope, checker.regional[i].region) {
10011 +
            set result |= bit;
10012 +
        }
10013 +
    }
10014 +
    return result;
10015 +
}
10016 +
10017 +
/// Remap one loan mask after the regional loan table is compacted.
10018 +
fn remapRegionalLoans(mask: u64, mapping: &[u64]) -> u64 {
10019 +
    let mut result: u64 = 0;
10020 +
    for replacement, i in mapping {
10021 +
        if (mask & ((1 as u64) << (i as u64))) <> 0 {
10022 +
            set result |= replacement;
10023 +
        }
10024 +
    }
10025 +
    return result;
10026 +
}
10027 +
10028 +
/// Reclaim ended-region entries and preserve loans for enclosing regions.
10029 +
unsafe fn compactRegionalLoans 'arena 'checking (
10030 +
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
10031 +
    scope: ?*RegionScope
10032 +
) where 'arena: 'checking {
10033 +
    let oldLen = checker.regionalLen;
10034 +
    let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS];
10035 +
    let mut next: u32 = 0;
10036 +
    for i in 0..oldLen {
10037 +
        let loan = checker.regional[i];
10038 +
        if regionInScope(scope, loan.region) {
10039 +
            set checker.regional[next] = loan;
10040 +
            set mapping[i] = (1 as u64) << (next as u64);
10041 +
            set next += 1;
10042 +
        }
10043 +
    }
10044 +
    set env.regionalLoans = remapRegionalLoans(env.regionalLoans, &mapping[..oldLen]);
10045 +
    for i in 0..checker.loopDepth {
10046 +
        set checker.loopBackLoans[i] = remapRegionalLoans(checker.loopBackLoans[i], &mapping[..oldLen]);
10047 +
        set checker.loopExitLoans[i] = remapRegionalLoans(checker.loopExitLoans[i], &mapping[..oldLen]);
10048 +
    }
10049 +
    set checker.regionalLen = next;
10050 +
}
10051 +
10052 +
/// Check whether an access comes from the reference created by a projection.
10053 +
unsafe fn usesRegionalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: *ast::Node) -> bool {
10054 +
    if node.id == source.id {
10055 +
        return true;
10056 +
    }
10057 +
    let root = linearRootSymbol(self, node) else return false;
10058 +
    let origin = localReferenceSource(root) else return false;
10059 +
    return usesRegionalLoan(self, origin, source);
10060 +
}
10061 +
10062 +
/// Retain a full-region projection independently of its local binding scope.
10063 +
unsafe fn addRegionalLoan 'arena 'checking (
10064 +
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node, address: ast::AddressOf
10065 +
) throws (ResolveError) where 'arena: 'checking {
10066 +
    let ty = typeFor(checker.resolver, node) else return;
10067 +
    let mut class = types::PointerClass::Ref;
10068 +
    match ty {
10069 +
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
10070 +
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
10071 +
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
10072 +
        else => return,
10073 +
    }
10074 +
    let case types::PointerClass::Region(region) = class else return;
10075 +
    let storage = addressStorageClass(checker.resolver, address.target);
10076 +
    let case types::PointerClass::Region(parent) = storage else return;
10077 +
    if parent.id <> region.id {
10078 +
        return;
10079 +
    }
10080 +
    let place = borrowPlace(checker.resolver, address.target);
10081 +
    if place.root == nil {
10082 +
        return;
10083 +
    }
10084 +
    for loan, i in &checker.regional[..checker.regionalLen] {
10085 +
        if loan.source.id == node.id {
10086 +
            set env.regionalLoans |= (1 as u64) << (i as u64);
10087 +
            return;
10088 +
        }
10089 +
    }
10090 +
    if checker.regionalLen >= MAX_REGIONAL_LOANS {
10091 +
        throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow);
10092 +
    }
10093 +
    let index = checker.regionalLen;
10094 +
    set checker.regional[index] = RegionalLoan { source: node, region, place, exclusive: address.mutable };
10095 +
    set checker.regionalLen += 1;
10096 +
    set env.regionalLoans |= (1 as u64) << (index as u64);
10097 +
}
10098 +
10099 +
/// Return whether an initializer copies a shared reference with a named region.
10100 +
/// Address expressions and casts retain a loan on their source storage.
10101 +
unsafe fn copiesRegionalReference(ty: Type, value: *ast::Node) -> bool {
10102 +
    if referenceRegion(ty) == nil or not isCopy(ty) {
10103 +
        return false;
10104 +
    }
10105 +
    match value.value {
10106 +
        case ast::NodeValue::AddressOf(_), ast::NodeValue::As(_) => return false,
10107 +
        else => return true,
10108 +
    }
10109 +
}
10110 +
7854 10111
/// Return the initializer that supplies a local reference's storage.
7855 10112
unsafe fn localReferenceSource(sym: *unsafe mut Symbol) -> ?*ast::Node {
7856 10113
    let case SymbolData::Value { type: ty, .. } = sym.data else return nil;
10114 +
    if let case Type::Session(_) = ty {
10115 +
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
10116 +
            return binding.value;
10117 +
        }
10118 +
    }
7857 10119
    if isRefType(ty) {
7858 10120
        if let case ast::NodeValue::Let(binding) = sym.node.value {
10121 +
            if copiesRegionalReference(ty, binding.value) {
10122 +
                return nil;
10123 +
            }
10124 +
            return binding.value;
10125 +
        }
10126 +
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
7859 10127
            return binding.value;
7860 10128
        }
7861 10129
    }
7862 10130
    return nil;
7863 10131
}
7864 10132
7865 10133
/// Resolve a place through reference locals without extending its storage lifetime.
7866 -
unsafe fn borrowPlace(self: &mut Resolver, node: *ast::Node) -> BorrowPlace {
10134 +
unsafe fn borrowPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> BorrowPlace {
7867 10135
    let mut place = BorrowPlace { root: nil, fields: undefined, len: 0, precise: true };
7868 10136
    match node.value {
7869 10137
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
7870 10138
            let sym = symbolFor(self, node) else return place;
7871 10139
            if let source = localReferenceSource(sym) {
7872 -
                return borrowPlace(self, source);
10140 +
                let origin = borrowPlace(self, source);
10141 +
                if origin.root <> nil {
10142 +
                    return origin;
10143 +
                }
7873 10144
            }
7874 10145
            set place.root = sym;
7875 10146
        }
7876 10147
        case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target),
7877 10148
        case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value),
7895 10166
            set place.precise = false;
7896 10167
        }
7897 10168
        case ast::NodeValue::Subscript { container, .. } => {
7898 10169
            set place = borrowPlace(self, container);
7899 10170
            if let ty = typeFor(self, container) {
7900 -
                if let case Type::Slice { class, .. } = autoDeref(ty); class <> types::PointerClass::Ref {
10171 +
                if let case Type::Slice { class, .. } = autoDeref(ty); not types::isReference(class) {
7901 10172
                    set place.len = 0;
7902 10173
                }
7903 10174
            }
7904 10175
            set place.precise = false;
7905 10176
        }
7928 10199
    }
7929 10200
    return true;
7930 10201
}
7931 10202
7932 10203
/// Check whether access uses a reference or one of its lexical reborrows.
7933 -
unsafe fn usesLocalLoan(self: &mut Resolver, node: *ast::Node, binding: *unsafe mut Symbol) -> bool {
10204 +
unsafe fn usesLocalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, binding: *unsafe mut Symbol) -> bool {
7934 10205
    let root = linearRootSymbol(self, node) else return false;
7935 10206
    if root == binding {
7936 10207
        return true;
7937 10208
    }
7938 10209
    let source = localReferenceSource(root) else return false;
7939 10210
    return usesLocalLoan(self, source, binding);
7940 10211
}
7941 10212
7942 10213
/// Reject accesses that conflict with a reference in an active lexical scope.
7943 -
unsafe fn checkLocalLoans(checker: &mut LinearChecker, node: *ast::Node, exclusive: bool)
7944 -
    throws (ResolveError)
10214 +
unsafe fn checkLocalLoans 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, exclusive: bool)
10215 +
    throws (ResolveError) where 'arena: 'checking
7945 10216
{
7946 10217
    let place = borrowPlace(checker.resolver, node);
7947 10218
    let root = place.root else return;
10219 +
    for i in 0..checker.regionalLen {
10220 +
        if (env.regionalLoans & ((1 as u64) << (i as u64))) == 0 {
10221 +
            continue;
10222 +
        }
10223 +
        let loan = checker.regional[i];
10224 +
        if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
10225 +
            and not usesRegionalLoan(checker.resolver, node, loan.source)
10226 +
        {
10227 +
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
10228 +
        }
10229 +
    }
7948 10230
    for i in 0..checker.localLen {
7949 10231
        let loan = checker.locals[i];
7950 10232
        if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
7951 10233
            and not usesLocalLoan(checker.resolver, node, loan.binding)
7952 10234
        {
7953 10235
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
7954 10236
        }
7955 10237
    }
7956 10238
}
7957 10239
7958 -
/// Retain existing storage until its immutable reference binding leaves scope.
7959 -
unsafe fn addLocalLoan(checker: &mut LinearChecker, node: *ast::Node, binding: ast::Let)
7960 -
    throws (ResolveError)
10240 +
/// Retain source storage for local borrows and region headers.
10241 +
unsafe fn addLocalLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, binding: ast::Let)
10242 +
    throws (ResolveError) where 'arena: 'checking
7961 10243
{
7962 10244
    let ty = typeFor(checker.resolver, binding.ident) else return;
7963 10245
    if not isRefType(ty) {
10246 +
        let case Type::Session(_) = ty else return;
10247 +
        let case ast::NodeValue::RegionBinding(_) = node.value else return;
10248 +
    }
10249 +
    if let case ast::NodeValue::Let(_) = node.value;
10250 +
        copiesRegionalReference(ty, binding.value)
10251 +
    {
7964 10252
        return;
7965 10253
    }
7966 10254
    let place = borrowPlace(checker.resolver, binding.value);
7967 10255
    if place.root == nil {
10256 +
        if referenceRegion(ty) <> nil {
10257 +
            return;
10258 +
        }
7968 10259
        throw emitError(checker.resolver, node, ErrorKind::RefBinding);
7969 10260
    }
7970 10261
    if checker.localLen >= MAX_LINEAR_BINDINGS {
7971 10262
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7972 10263
    }
7973 10264
    let sym = symbolFor(checker.resolver, node) else panic "reference without binding";
7974 -
    let exclusive = isExclusiveArgument(ty);
7975 -
    try checkLocalLoans(checker, binding.value, exclusive);
10265 +
    let mut exclusive = isExclusiveArgument(ty);
10266 +
    if let case Type::Cell { .. } = ty {
10267 +
        if let case ast::NodeValue::As(expr) = binding.value.value {
10268 +
            if let source = typeFor(checker.resolver, expr.value) {
10269 +
                if let case Type::Pointer { mutable: true, .. } = source {
10270 +
                    set exclusive = true;
10271 +
                }
10272 +
            }
10273 +
        }
10274 +
    }
10275 +
    try checkLocalLoans(checker, env, binding.value, exclusive);
7976 10276
    set checker.locals[checker.localLen] = LocalLoan { binding: sym, place, exclusive };
7977 10277
    set checker.localLen += 1;
7978 10278
}
7979 10279
7980 -
/// Protect a pattern source until its reference bindings leave scope.
7981 -
unsafe fn addPatternLoan(checker: &mut LinearChecker, subject: *ast::Node)
7982 -
    throws (ResolveError)
10280 +
/// Protect storage borrowed by a pointer pattern until its bindings leave scope.
10281 +
unsafe fn addPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, subject: *ast::Node)
10282 +
    throws (ResolveError) where 'arena: 'checking
7983 10283
{
10284 +
    let ty = typeFor(checker.resolver, subject) else return;
10285 +
    if unwrapMatchSubject(ty).by == MatchBy::Value {
10286 +
        return;
10287 +
    }
7984 10288
    let place = borrowPlace(checker.resolver, subject);
7985 10289
    if place.root == nil {
7986 10290
        return;
7987 10291
    }
7988 10292
    if checker.loanLen >= MAX_LINEAR_BINDINGS {
7991 10295
    set checker.loans[checker.loanLen] = place;
7992 10296
    set checker.loanLen += 1;
7993 10297
}
7994 10298
7995 10299
/// Reject a write, mutable loan, or ownership transfer of a pattern source.
7996 -
unsafe fn checkPatternLoan(checker: &mut LinearChecker, node: *ast::Node)
7997 -
    throws (ResolveError)
10300 +
unsafe fn checkPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, node: *ast::Node)
10301 +
    throws (ResolveError) where 'arena: 'checking
7998 10302
{
7999 10303
    let place = borrowPlace(checker.resolver, node);
8000 10304
    let root = place.root else return;
8001 10305
    for i in 0..checker.loanLen {
8002 10306
        if placesOverlap(&checker.loans[i], &place) {
8003 10307
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
8004 10308
        }
8005 10309
    }
8006 10310
}
8007 10311
10312 +
/// Return whether a parameter borrows its argument only for the call.
10313 +
unsafe fn isBorrowedReferenceParameter(ty: Type) -> bool {
10314 +
    if not isRefType(ty) {
10315 +
        return false;
10316 +
    }
10317 +
    match ty {
10318 +
        case Type::Cell { class, .. } => return class == types::PointerClass::Ref,
10319 +
        case Type::Pointer { class, .. } => return class == types::PointerClass::Ref,
10320 +
        case Type::Slice { class, .. } => return class == types::PointerClass::Ref,
10321 +
        case Type::TraitObject { class, .. } => return class == types::PointerClass::Ref,
10322 +
        else => return false,
10323 +
    }
10324 +
}
10325 +
8008 10326
/// Return whether a parameter can mutate or consume its argument's storage.
8009 10327
unsafe fn isExclusiveArgument(ty: Type) -> bool {
8010 10328
    match ty {
8011 10329
        case Type::Pointer { mutable, .. } => return mutable,
8012 10330
        case Type::Slice { mutable, .. } => return mutable,
8015 10333
    }
8016 10334
}
8017 10335
8018 10336
/// Add the value identifiers introduced by a pattern.
8019 10337
/// Return whether the pattern introduces references to its source storage.
8020 -
unsafe fn addLinearPatternBindings(
8021 -
    checker: &mut LinearChecker,
10338 +
unsafe fn addLinearPatternBindings 'arena 'checking (
10339 +
    checker: &mut LinearChecker 'arena 'checking,
8022 10340
    env: &mut LinearEnv,
8023 10341
    pattern: *ast::Node,
8024 -
) -> bool throws (ResolveError) {
10342 +
) -> bool throws (ResolveError) where 'arena: 'checking {
8025 10343
    let mut hasReferences = false;
8026 10344
    match pattern.value {
8027 10345
        case ast::NodeValue::Ident(_) => {
8028 10346
            try addLinearBinding(checker, env, pattern);
8029 10347
            if let ty = typeFor(checker.resolver, pattern) {
8057 10375
    }
8058 10376
    return hasReferences;
8059 10377
}
8060 10378
8061 10379
/// Check a lexical block and exact-use of locals introduced in it.
8062 -
unsafe fn checkLinearBlock(
8063 -
    checker: &mut LinearChecker,
10380 +
unsafe fn checkLinearBlock 'arena 'checking (
10381 +
    checker: &mut LinearChecker 'arena 'checking,
8064 10382
    env: &mut LinearEnv,
8065 10383
    node: *ast::Node,
8066 -
) throws (ResolveError) {
10384 +
) throws (ResolveError) where 'arena: 'checking {
8067 10385
    let start = env.len;
8068 10386
    let localStart = checker.localLen;
8069 10387
    let case ast::NodeValue::Block(block) = node.value
8070 10388
        else panic "checkLinearBlock: expected block";
8071 10389
    for stmt in block.statements {
8078 10396
    set checker.localLen = localStart;
8079 10397
}
8080 10398
8081 10399
/// Push a repeated-control-flow boundary.
8082 10400
/// Initialize all loop state at this depth before increasing `loopDepth`.
8083 -
fn enterLinearLoop(checker: &mut LinearChecker, env: &LinearEnv) {
10401 +
fn enterLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
8084 10402
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
8085 10403
    let depth = checker.loopDepth;
10404 +
    set checker.loopBackLoans[depth] = 0;
10405 +
    set checker.loopExitLoans[depth] = 0;
10406 +
    set checker.loopRegions[depth] = checker.regions;
8086 10407
    set checker.loopMarks[depth] = env.len;
8087 10408
    set checker.loopAvailable[depth] = env.available;
8088 10409
    set checker.loopExitAvailable[depth] = env.available;
8089 10410
    set checker.loopHasNaturalExit[depth] = false;
8090 10411
    set checker.loopBreakSeen[depth] = false;
8091 10412
    set checker.loopDepth += 1;
8092 10413
}
8093 10414
8094 10415
/// Require a repeated body's outer bindings to match its entry state.
8095 -
unsafe fn checkLinearLoopBackEdge(
8096 -
    checker: &mut LinearChecker,
10416 +
unsafe fn checkLinearLoopBackEdge 'arena 'checking (
10417 +
    checker: &mut LinearChecker 'arena 'checking,
8097 10418
    env: &LinearEnv,
8098 10419
    node: *ast::Node,
8099 -
) throws (ResolveError) {
10420 +
) throws (ResolveError) where 'arena: 'checking {
8100 10421
    if env.terminated {
8101 10422
        return;
8102 10423
    }
8103 10424
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
8104 10425
    let depth = checker.loopDepth - 1;
10426 +
    set checker.loopBackLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
8105 10427
    let mark = checker.loopMarks[depth];
8106 10428
    let entryAvailable = checker.loopAvailable[depth];
8107 10429
    for i in 0..mark {
8108 10430
        let bit = (1 as u64) << (i as u64);
8109 10431
        if (env.available & bit) <> (entryAvailable & bit) {
8116 10438
        }
8117 10439
    }
8118 10440
}
8119 10441
8120 10442
/// Record the ownership state of a loop's condition-false exit.
8121 -
fn setLinearLoopNaturalExit(checker: &mut LinearChecker, env: &LinearEnv) {
10443 +
unsafe fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
8122 10444
    assert checker.loopDepth > 0, "linear loop exit outside loop";
8123 10445
    let depth = checker.loopDepth - 1;
10446 +
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
8124 10447
    set checker.loopExitAvailable[depth] = env.available;
8125 10448
    set checker.loopHasNaturalExit[depth] = true;
8126 10449
}
8127 10450
8128 10451
/// Require a break exit to agree with every other exit from this loop.
8129 -
unsafe fn checkLinearLoopBreak(
8130 -
    checker: &mut LinearChecker,
10452 +
unsafe fn checkLinearLoopBreak 'arena 'checking (
10453 +
    checker: &mut LinearChecker 'arena 'checking,
8131 10454
    env: &LinearEnv,
8132 10455
    node: *ast::Node,
8133 -
) throws (ResolveError) {
10456 +
) throws (ResolveError) where 'arena: 'checking {
8134 10457
    assert checker.loopDepth > 0, "linear loop break outside loop";
8135 10458
    let depth = checker.loopDepth - 1;
10459 +
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
8136 10460
    let mark = checker.loopMarks[depth];
8137 10461
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
8138 10462
        let expected = checker.loopExitAvailable[depth];
8139 10463
        for i in 0..mark {
8140 10464
            let bit = (1 as u64) << (i as u64);
8152 10476
    }
8153 10477
    set checker.loopBreakSeen[depth] = true;
8154 10478
}
8155 10479
8156 10480
/// Pop a repeated-control-flow boundary.
8157 -
fn exitLinearLoop(checker: &mut LinearChecker) {
10481 +
fn exitLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking) where 'arena: 'checking {
8158 10482
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
8159 10483
    set checker.loopDepth -= 1;
8160 10484
}
8161 10485
8162 10486
/// Check a conditional and merge its ownership states.
8163 -
unsafe fn checkLinearIf(
8164 -
    checker: &mut LinearChecker,
10487 +
unsafe fn checkLinearIf 'arena 'checking (
10488 +
    checker: &mut LinearChecker 'arena 'checking,
8165 10489
    env: &mut LinearEnv,
8166 10490
    node: *ast::Node,
8167 10491
    conditional: ast::If,
8168 -
) throws (ResolveError) {
10492 +
) throws (ResolveError) where 'arena: 'checking {
8169 10493
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
8170 10494
    let base = *env;
8171 10495
    let mut thenEnv = base;
8172 10496
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
8173 10497
    let mut elseEnv = base;
8176 10500
    }
8177 10501
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8178 10502
}
8179 10503
8180 10504
/// Check an expression conditional and merge its ownership states.
8181 -
unsafe fn checkLinearCondExpr(
8182 -
    checker: &mut LinearChecker,
10505 +
unsafe fn checkLinearCondExpr 'arena 'checking (
10506 +
    checker: &mut LinearChecker 'arena 'checking,
8183 10507
    env: &mut LinearEnv,
8184 10508
    node: *ast::Node,
8185 10509
    conditional: ast::CondExpr,
8186 10510
    usage: LinearUse,
8187 -
) throws (ResolveError) {
10511 +
) throws (ResolveError) where 'arena: 'checking {
8188 10512
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
8189 10513
    let base = *env;
8190 10514
    let mut thenEnv = base;
8191 10515
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
8192 10516
    let mut elseEnv = base;
8193 10517
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
8194 10518
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8195 10519
}
8196 10520
8197 10521
/// Pointer patterns borrow their subject; value patterns consume it.
8198 -
unsafe fn patternSubjectUse(self: &Resolver, subject: *ast::Node) -> LinearUse {
10522 +
unsafe fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse {
8199 10523
    if let ty = typeFor(self, subject) {
8200 10524
        if let case Type::Pointer { .. } = ty {
8201 10525
            return LinearUse::Borrow;
8202 10526
        }
8203 10527
    }
8204 10528
    return LinearUse::Consume;
8205 10529
}
8206 10530
8207 10531
/// Check a match expression, including ownership transferred into patterns.
8208 -
unsafe fn checkLinearMatch(
8209 -
    checker: &mut LinearChecker,
10532 +
unsafe fn checkLinearMatch 'arena 'checking (
10533 +
    checker: &mut LinearChecker 'arena 'checking,
8210 10534
    env: &mut LinearEnv,
8211 10535
    node: *ast::Node,
8212 10536
    matchExpr: ast::Match,
8213 -
) throws (ResolveError) {
10537 +
) throws (ResolveError) where 'arena: 'checking {
8214 10538
    try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject));
8215 10539
    let base = *env;
8216 10540
    let mut haveResult = false;
8217 10541
    let mut result = base;
8218 10542
    for prongNode in matchExpr.prongs {
8264 10588
        set *env = result;
8265 10589
    }
8266 10590
}
8267 10591
8268 10592
/// Check call-scoped loans and argument ownership transfers.
8269 -
unsafe fn checkLinearCall(
8270 -
    checker: &mut LinearChecker,
10593 +
unsafe fn checkLinearCall 'arena 'checking (
10594 +
    checker: &mut LinearChecker 'arena 'checking,
8271 10595
    env: &mut LinearEnv,
8272 10596
    node: *ast::Node,
8273 10597
    call: ast::Call,
8274 -
) throws (ResolveError) {
10598 +
) throws (ResolveError) where 'arena: 'checking {
8275 10599
    match checker.resolver.nodeData.entries[node.id].extra {
8276 10600
        case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => {
8277 10601
            let case ast::NodeValue::FieldAccess(access) = call.callee.value
8278 10602
                else panic "slice mutation without receiver";
8279 10603
            try checkPatternLoan(checker, access.parent);
8280 -
            try checkLocalLoans(checker, access.parent, true);
10604 +
            try checkLocalLoans(checker, env, access.parent, true);
8281 10605
        }
8282 10606
        else => {}
8283 10607
    }
8284 10608
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
8285 10609
    let mut fnInfo: ?*FnType = nil;
8325 10649
                set haveReceiver = true;
8326 10650
            }
8327 10651
            else => {}
8328 10652
        }
8329 10653
        if haveReceiver {
8330 -
            try checkLocalLoans(checker, access.parent,
10654 +
            try checkLocalLoans(checker, env, access.parent,
8331 10655
                receiverMutable or receiverClass == types::PointerClass::Owned);
8332 10656
            if receiverMutable or receiverClass == types::PointerClass::Owned {
8333 10657
                try checkPatternLoan(checker, access.parent);
8334 10658
            }
8335 10659
            if receiverClass <> types::PointerClass::Unsafe {
8339 10663
                    set exclusive[placesLen] =
8340 10664
                        receiverClass == types::PointerClass::Owned or receiverMutable;
8341 10665
                    set placesLen += 1;
8342 10666
                }
8343 10667
            }
8344 -
            if receiverClass == types::PointerClass::Ref {
10668 +
            if types::isReference(receiverClass) {
8345 10669
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
8346 10670
            } else if receiverClass == types::PointerClass::Owned {
8347 10671
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
8348 10672
            }
8349 10673
        }
8366 10690
                set places[placesLen] = place;
8367 10691
                set exclusive[placesLen] = argExclusive;
8368 10692
                set placesLen += 1;
8369 10693
            }
8370 10694
        }
8371 -
        try checkLocalLoans(checker, arg, argExclusive);
8372 -
        if isRefType(expected) {
10695 +
        try checkLocalLoans(checker, env, arg, argExclusive);
10696 +
        if isBorrowedReferenceParameter(expected) {
8373 10697
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
8374 10698
        } else {
8375 10699
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
8376 10700
        }
8377 10701
    }
8379 10703
        set env.terminated = true;
8380 10704
    }
8381 10705
}
8382 10706
8383 10707
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
8384 -
unsafe fn checkLinearIfLet(
8385 -
    checker: &mut LinearChecker,
10708 +
unsafe fn checkLinearIfLet 'arena 'checking (
10709 +
    checker: &mut LinearChecker 'arena 'checking,
8386 10710
    env: &mut LinearEnv,
8387 10711
    node: *ast::Node,
8388 10712
    conditional: ast::IfLet,
8389 -
) throws (ResolveError) {
10713 +
) throws (ResolveError) where 'arena: 'checking {
8390 10714
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
8391 10715
        isLinear(subjectTy)
8392 10716
    {
8393 10717
        throw emitError(
8394 10718
            checker.resolver,
8420 10744
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
8421 10745
    }
8422 10746
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8423 10747
}
8424 10748
8425 -
/// Check one expression or statement under an ownership-use context.
8426 -
unsafe fn checkLinearNode(
8427 -
    checker: &mut LinearChecker,
8428 -
    env: &mut LinearEnv,
10749 +
/// Check a repeated region-loan flow until its loop-entry mask is stable.
10750 +
/// Each additional pass must add a bit from the bounded regional loan table.
10751 +
unsafe fn checkLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
10752 +
    throws (ResolveError) where 'arena: 'checking
10753 +
{
10754 +
    if let case ast::NodeValue::For(forStmt) = node.value {
10755 +
        try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
10756 +
    }
10757 +
    let base = *env;
10758 +
    let depth = checker.loopDepth;
10759 +
    let mut entryLoans = base.regionalLoans;
10760 +
    loop {
10761 +
        let mut pass = base;
10762 +
        set pass.regionalLoans = entryLoans;
10763 +
        try checkLinearLoopPass(checker, &mut pass, node);
10764 +
        let next = entryLoans | checker.loopBackLoans[depth];
10765 +
        if next == entryLoans {
10766 +
            set *env = pass;
10767 +
            return;
10768 +
        }
10769 +
        set entryLoans = next;
10770 +
    }
10771 +
}
10772 +
10773 +
/// Check one pass through a loop with the current loop-entry loan state.
10774 +
unsafe fn checkLinearLoopPass 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
10775 +
    throws (ResolveError) where 'arena: 'checking
10776 +
{
10777 +
    match node.value {
10778 +
        case ast::NodeValue::While(whileStmt) => {
10779 +
            enterLinearLoop(checker, env);
10780 +
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
10781 +
            let conditionExit = *env;
10782 +
            setLinearLoopNaturalExit(checker, &conditionExit);
10783 +
            let mut bodyEnv = conditionExit;
10784 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
10785 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
10786 +
            exitLinearLoop(checker);
10787 +
            set *env = conditionExit;
10788 +
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
10789 +
            if let elseBranch = whileStmt.elseBranch {
10790 +
                let mut elseEnv = conditionExit;
10791 +
                try checkLinearNode(
10792 +
                    checker,
10793 +
                    &mut elseEnv,
10794 +
                    elseBranch,
10795 +
                    LinearUse::Discard,
10796 +
                );
10797 +
                let exits = *env;
10798 +
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
10799 +
            }
10800 +
        }
10801 +
        case ast::NodeValue::WhileLet(whileStmt) => {
10802 +
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
10803 +
                isLinear(subjectTy)
10804 +
            {
10805 +
                throw emitError(
10806 +
                    checker.resolver,
10807 +
                    whileStmt.pattern.scrutinee,
10808 +
                    ErrorKind::LinearPartialMove,
10809 +
                );
10810 +
            }
10811 +
            let base = *env;
10812 +
            enterLinearLoop(checker, env);
10813 +
            let mut bodyEnv = base;
10814 +
            try checkLinearNode(
10815 +
                checker,
10816 +
                &mut bodyEnv,
10817 +
                whileStmt.pattern.scrutinee,
10818 +
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
10819 +
            );
10820 +
            let mut conditionExit = bodyEnv;
10821 +
            let start = bodyEnv.len;
10822 +
            let loanStart = checker.loanLen;
10823 +
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
10824 +
                try addPatternLoan(checker, whileStmt.pattern.scrutinee);
10825 +
            }
10826 +
            if let guard = whileStmt.pattern.guard {
10827 +
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
10828 +
                let mut guardExit = bodyEnv;
10829 +
                try finishLinearScope(checker, &mut guardExit, start);
10830 +
                let previous = conditionExit;
10831 +
                try joinLinearBranches(
10832 +
                    checker,
10833 +
                    &mut conditionExit,
10834 +
                    &previous,
10835 +
                    &guardExit,
10836 +
                    guard,
10837 +
                );
10838 +
            }
10839 +
            setLinearLoopNaturalExit(checker, &conditionExit);
10840 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
10841 +
            try finishLinearScope(checker, &mut bodyEnv, start);
10842 +
            set checker.loanLen = loanStart;
10843 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
10844 +
            exitLinearLoop(checker);
10845 +
            set *env = conditionExit;
10846 +
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
10847 +
            if let elseBranch = whileStmt.elseBranch {
10848 +
                let mut elseEnv = conditionExit;
10849 +
                try checkLinearNode(
10850 +
                    checker,
10851 +
                    &mut elseEnv,
10852 +
                    elseBranch,
10853 +
                    LinearUse::Discard,
10854 +
                );
10855 +
                let exits = *env;
10856 +
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
10857 +
            }
10858 +
        }
10859 +
        case ast::NodeValue::For(forStmt) => {
10860 +
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
10861 +
                if isLinear(iterableTy) {
10862 +
                    throw emitError(
10863 +
                        checker.resolver,
10864 +
                        forStmt.iterable,
10865 +
                        ErrorKind::LinearPartialMove,
10866 +
                    );
10867 +
                }
10868 +
            }
10869 +
            let base = *env;
10870 +
            enterLinearLoop(checker, env);
10871 +
            setLinearLoopNaturalExit(checker, &base);
10872 +
            let mut bodyEnv = base;
10873 +
            let start = bodyEnv.len;
10874 +
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
10875 +
            if let index = forStmt.index {
10876 +
                try addLinearBinding(checker, &mut bodyEnv, index);
10877 +
            }
10878 +
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
10879 +
            try finishLinearScope(checker, &mut bodyEnv, start);
10880 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
10881 +
            exitLinearLoop(checker);
10882 +
            set *env = base;
10883 +
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
10884 +
            if let elseBranch = forStmt.elseBranch {
10885 +
                let mut elseEnv = base;
10886 +
                try checkLinearNode(
10887 +
                    checker,
10888 +
                    &mut elseEnv,
10889 +
                    elseBranch,
10890 +
                    LinearUse::Discard,
10891 +
                );
10892 +
                let exits = *env;
10893 +
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
10894 +
            }
10895 +
        }
10896 +
        case ast::NodeValue::Loop { body } => {
10897 +
            let base = *env;
10898 +
            enterLinearLoop(checker, env);
10899 +
            let mut bodyEnv = base;
10900 +
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
10901 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
10902 +
            let depth = checker.loopDepth - 1;
10903 +
            let breakSeen = checker.loopBreakSeen[depth];
10904 +
            let exitAvailable = checker.loopExitAvailable[depth];
10905 +
            exitLinearLoop(checker);
10906 +
            set *env = base;
10907 +
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
10908 +
            if breakSeen {
10909 +
                set env.available = exitAvailable;
10910 +
            } else {
10911 +
                set env.terminated = true;
10912 +
            }
10913 +
        }
10914 +
        else => panic "checkLinearLoopPass: expected loop",
10915 +
    }
10916 +
}
10917 +
10918 +
/// Check one expression or statement under an ownership-use context.
10919 +
unsafe fn checkLinearNode 'arena 'checking (
10920 +
    checker: &mut LinearChecker 'arena 'checking,
10921 +
    env: &mut LinearEnv,
8429 10922
    node: *ast::Node,
8430 10923
    usage: LinearUse,
8431 -
) throws (ResolveError) {
10924 +
) throws (ResolveError) where 'arena: 'checking {
8432 10925
    if env.terminated {
8433 10926
        return;
8434 10927
    }
8435 10928
    if usage <> LinearUse::Locate {
8436 10929
        match node.value {
8437 10930
            case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_),
8438 10931
                 ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. },
8439 10932
                 ast::NodeValue::Deref(_) => {
8440 -
                let mut exclusive = usage == LinearUse::Place;
10933 +
                let mut exclusive = usage == LinearUse::Place and not isCellDeref(checker.resolver, node);
8441 10934
                if usage == LinearUse::Consume {
8442 10935
                    if let ty = typeFor(checker.resolver, node) {
8443 10936
                        set exclusive = isExclusiveArgument(ty);
8444 10937
                    }
8445 10938
                }
8446 -
                try checkLocalLoans(checker, node, exclusive);
10939 +
                try checkLocalLoans(checker, env, node, exclusive);
8447 10940
            }
8448 10941
            else => {}
8449 10942
        }
8450 10943
    }
8451 10944
    match node.value {
8466 10959
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
8467 10960
                }
8468 10961
            }
8469 10962
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8470 10963
        }
10964 +
        case ast::NodeValue::RegionBlock { bindings, body, .. } => {
10965 +
            let previousRegions = checker.regions;
10966 +
            let case NodeExtra::Regions(scope) = checker.resolver.nodeData.entries[node.id].extra else {
10967 +
                if checker.resolver.errors.len > 0 {
10968 +
                    return;
10969 +
                }
10970 +
                panic "checkLinearNode: missing region scope";
10971 +
            };
10972 +
            set checker.regions = scope;
10973 +
            let start = env.len;
10974 +
            let localStart = checker.localLen;
10975 +
            for bindingNode in bindings {
10976 +
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
10977 +
                    else panic "checkLinearNode: invalid borrow binding";
10978 +
                try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
10979 +
                if env.terminated {
10980 +
                    break;
10981 +
                }
10982 +
                try addLinearBinding(checker, env, bindingNode);
10983 +
                try addLocalLoan(checker, env, bindingNode, ast::borrowBinding(binding));
10984 +
            }
10985 +
            try checkLinearBlock(checker, env, body);
10986 +
            try finishLinearScope(checker, env, start);
10987 +
            set checker.localLen = localStart;
10988 +
            set checker.regions = previousRegions;
10989 +
            compactRegionalLoans(checker, env, previousRegions);
10990 +
        }
8471 10991
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
8472 10992
        case ast::NodeValue::Let(binding) => {
8473 10993
            let mut isUndefined = false;
8474 10994
            if let case ast::NodeValue::Undef = binding.value.value {
8475 10995
                set isUndefined = true;
8488 11008
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
8489 11009
            if env.terminated {
8490 11010
                return;
8491 11011
            }
8492 11012
            try addLinearBinding(checker, env, node);
8493 -
            try addLocalLoan(checker, node, binding);
11013 +
            try addLocalLoan(checker, env, node, binding);
8494 11014
            if isUndefined {
8495 11015
                markLinearBindingUnavailable(checker.resolver, env, node);
8496 11016
            }
8497 11017
        }
8498 11018
        case ast::NodeValue::Assign(assign) => {
8499 -
            try checkPatternLoan(checker, assign.left);
11019 +
            if not isCellDeref(checker.resolver, assign.left) {
11020 +
                try checkPatternLoan(checker, assign.left);
11021 +
            }
8500 11022
            let mut target: ?u32 = nil;
8501 11023
            let mut targetLinear = false;
8502 11024
            if let leftTy = typeFor(checker.resolver, assign.left) {
8503 11025
                if isMoveOnly(leftTy) {
8504 11026
                    set targetLinear = isLinear(leftTy);
8527 11049
                    );
8528 11050
                }
8529 11051
                set env.available |= (1 as u64) << (index as u64);
8530 11052
            }
8531 11053
        }
11054 +
        case ast::NodeValue::RegionApply { value, .. } =>
11055 +
            try checkLinearNode(checker, env, value, usage),
8532 11056
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
8533 11057
        case ast::NodeValue::AddressOf(addr) => {
8534 -
            try checkLocalLoans(checker, addr.target, addr.mutable);
11058 +
            try checkLocalLoans(checker, env, addr.target, addr.mutable);
8535 11059
            if addr.mutable {
8536 11060
                try checkPatternLoan(checker, addr.target);
8537 11061
            }
8538 11062
            try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
11063 +
            try addRegionalLoan(checker, env, node, addr);
8539 11064
        }
8540 11065
        case ast::NodeValue::Deref(target) => {
8541 11066
            if let resultTy = typeFor(checker.resolver, node) {
8542 11067
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8543 11068
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8603 11128
        case ast::NodeValue::UnOp(op) => {
8604 11129
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
8605 11130
        }
8606 11131
        case ast::NodeValue::As(expr) => {
8607 11132
            let mut castUse = usage;
11133 +
            if let targetTy = typeFor(checker.resolver, node) {
11134 +
                if let case Type::Cell { .. } = targetTy {
11135 +
                    set castUse = LinearUse::Consume;
11136 +
                }
11137 +
            }
8608 11138
            if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) {
8609 11139
                set castUse = LinearUse::Observe;
8610 11140
            }
8611 11141
            try checkLinearNode(checker, env, expr.value, castUse);
8612 11142
        }
8715 11245
                try finishLinearScope(checker, &mut branch, start);
8716 11246
                let previous = *env;
8717 11247
                try joinLinearBranches(checker, env, &previous, &branch, node);
8718 11248
            }
8719 11249
        }
8720 -
        case ast::NodeValue::While(whileStmt) => {
8721 -
            enterLinearLoop(checker, env);
8722 -
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8723 -
            let conditionExit = *env;
8724 -
            setLinearLoopNaturalExit(checker, &conditionExit);
8725 -
            let mut bodyEnv = conditionExit;
8726 -
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8727 -
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8728 -
            exitLinearLoop(checker);
8729 -
            set *env = conditionExit;
8730 -
            if let elseBranch = whileStmt.elseBranch {
8731 -
                let mut elseEnv = conditionExit;
8732 -
                try checkLinearNode(
8733 -
                    checker,
8734 -
                    &mut elseEnv,
8735 -
                    elseBranch,
8736 -
                    LinearUse::Discard,
8737 -
                );
8738 -
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8739 -
            }
8740 -
        }
8741 -
        case ast::NodeValue::WhileLet(whileStmt) => {
8742 -
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8743 -
                isLinear(subjectTy)
8744 -
            {
8745 -
                throw emitError(
8746 -
                    checker.resolver,
8747 -
                    whileStmt.pattern.scrutinee,
8748 -
                    ErrorKind::LinearPartialMove,
8749 -
                );
8750 -
            }
8751 -
            let base = *env;
8752 -
            enterLinearLoop(checker, env);
8753 -
            let mut bodyEnv = base;
8754 -
            try checkLinearNode(
8755 -
                checker,
8756 -
                &mut bodyEnv,
8757 -
                whileStmt.pattern.scrutinee,
8758 -
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
8759 -
            );
8760 -
            let mut conditionExit = bodyEnv;
8761 -
            let start = bodyEnv.len;
8762 -
            let loanStart = checker.loanLen;
8763 -
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
8764 -
                try addPatternLoan(checker, whileStmt.pattern.scrutinee);
8765 -
            }
8766 -
            if let guard = whileStmt.pattern.guard {
8767 -
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8768 -
                let mut guardExit = bodyEnv;
8769 -
                try finishLinearScope(checker, &mut guardExit, start);
8770 -
                let previous = conditionExit;
8771 -
                try joinLinearBranches(
8772 -
                    checker,
8773 -
                    &mut conditionExit,
8774 -
                    &previous,
8775 -
                    &guardExit,
8776 -
                    guard,
8777 -
                );
8778 -
            }
8779 -
            setLinearLoopNaturalExit(checker, &conditionExit);
8780 -
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8781 -
            try finishLinearScope(checker, &mut bodyEnv, start);
8782 -
            set checker.loanLen = loanStart;
8783 -
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8784 -
            exitLinearLoop(checker);
8785 -
            set *env = conditionExit;
8786 -
            if let elseBranch = whileStmt.elseBranch {
8787 -
                let mut elseEnv = conditionExit;
8788 -
                try checkLinearNode(
8789 -
                    checker,
8790 -
                    &mut elseEnv,
8791 -
                    elseBranch,
8792 -
                    LinearUse::Discard,
8793 -
                );
8794 -
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8795 -
            }
8796 -
        }
8797 -
        case ast::NodeValue::For(forStmt) => {
8798 -
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8799 -
                if isLinear(iterableTy) {
8800 -
                    throw emitError(
8801 -
                        checker.resolver,
8802 -
                        forStmt.iterable,
8803 -
                        ErrorKind::LinearPartialMove,
8804 -
                    );
8805 -
                }
8806 -
            }
8807 -
            try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
8808 -
            let base = *env;
8809 -
            enterLinearLoop(checker, env);
8810 -
            setLinearLoopNaturalExit(checker, &base);
8811 -
            let mut bodyEnv = base;
8812 -
            let start = bodyEnv.len;
8813 -
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
8814 -
            if let index = forStmt.index {
8815 -
                try addLinearBinding(checker, &mut bodyEnv, index);
8816 -
            }
8817 -
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
8818 -
            try finishLinearScope(checker, &mut bodyEnv, start);
8819 -
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
8820 -
            exitLinearLoop(checker);
8821 -
            set *env = base;
8822 -
            if let elseBranch = forStmt.elseBranch {
8823 -
                let mut elseEnv = base;
8824 -
                try checkLinearNode(
8825 -
                    checker,
8826 -
                    &mut elseEnv,
8827 -
                    elseBranch,
8828 -
                    LinearUse::Discard,
8829 -
                );
8830 -
                try joinLinearBranches(checker, env, &base, &elseEnv, node);
8831 -
            }
8832 -
        }
8833 -
        case ast::NodeValue::Loop { body } => {
8834 -
            let base = *env;
8835 -
            enterLinearLoop(checker, env);
8836 -
            let mut bodyEnv = base;
8837 -
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
8838 -
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
8839 -
            let depth = checker.loopDepth - 1;
8840 -
            let breakSeen = checker.loopBreakSeen[depth];
8841 -
            let exitAvailable = checker.loopExitAvailable[depth];
8842 -
            exitLinearLoop(checker);
8843 -
            set *env = base;
8844 -
            if breakSeen {
8845 -
                set env.available = exitAvailable;
8846 -
            } else {
8847 -
                set env.terminated = true;
8848 -
            }
8849 -
        }
11250 +
        case ast::NodeValue::While(_), ast::NodeValue::WhileLet(_),
11251 +
             ast::NodeValue::For(_), ast::NodeValue::Loop { .. } =>
11252 +
            try checkLinearLoop(checker, env, node),
8850 11253
        case ast::NodeValue::Break => {
8851 11254
            assert checker.loopDepth > 0, "linear loop control outside loop";
8852 11255
            let start = checker.loopMarks[checker.loopDepth - 1];
8853 11256
            try finishLinearScope(checker, env, start);
8854 11257
            try checkLinearLoopBreak(checker, env, node);
8886 11289
        else => {}
8887 11290
    }
8888 11291
}
8889 11292
8890 11293
/// Check exact-use ownership for one resolved function.
8891 -
unsafe fn checkLinearFn(
8892 -
    self: &mut Resolver,
11294 +
unsafe fn checkLinearFn 'arena (
11295 +
    self: &mut Resolver 'arena,
8893 11296
    receiver: ?*ast::Node,
8894 11297
    params: *[*ast::Node],
8895 11298
    body: *ast::Node,
8896 11299
) throws (ResolveError) {
8897 -
    let mut checker = LinearChecker {
8898 -
        resolver: self as *unsafe mut Resolver,
8899 -
        loans: undefined,
8900 -
        loanLen: 0,
8901 -
        locals: undefined,
8902 -
        localLen: 0,
8903 -
        loopMarks: undefined,
8904 -
        loopAvailable: undefined,
8905 -
        loopExitAvailable: undefined,
8906 -
        loopHasNaturalExit: undefined,
8907 -
        loopBreakSeen: undefined,
8908 -
        loopDepth: 0,
8909 -
    };
8910 -
    let mut env = LinearEnv {
8911 -
        symbols: undefined,
8912 -
        available: 0,
8913 -
        len: 0,
8914 -
        terminated: false,
8915 -
    };
8916 -
    if let receiverNode = receiver {
8917 -
        try addLinearBinding(&mut checker, &mut env, receiverNode);
8918 -
    }
8919 -
    for paramNode in params {
8920 -
        let case ast::NodeValue::FnParam(_) = paramNode.value
8921 -
            else panic "checkLinearFn: expected parameter";
8922 -
        try addLinearBinding(&mut checker, &mut env, paramNode);
11300 +
    try resolveNominalApplications(self, body);
11301 +
    let regions = self.regionScope;
11302 +
    let resolved: 'checking = &mut *self where 'arena: 'checking in {
11303 +
        let mut checker = LinearChecker 'arena 'checking {
11304 +
            resolver: resolved,
11305 +
            regional: undefined,
11306 +
            regionalLen: 0,
11307 +
            regions,
11308 +
            loopBackLoans: undefined,
11309 +
            loopExitLoans: undefined,
11310 +
            loopRegions: undefined,
11311 +
            loans: undefined,
11312 +
            loanLen: 0,
11313 +
            locals: undefined,
11314 +
            localLen: 0,
11315 +
            loopMarks: undefined,
11316 +
            loopAvailable: undefined,
11317 +
            loopExitAvailable: undefined,
11318 +
            loopHasNaturalExit: undefined,
11319 +
            loopBreakSeen: undefined,
11320 +
            loopDepth: 0,
11321 +
        };
11322 +
        let mut env = LinearEnv {
11323 +
            regionalLoans: 0,
11324 +
            symbols: undefined,
11325 +
            available: 0,
11326 +
            len: 0,
11327 +
            terminated: false,
11328 +
        };
11329 +
        if let receiverNode = receiver {
11330 +
            try addLinearBinding(&mut checker, &mut env, receiverNode);
11331 +
        }
11332 +
        for paramNode in params {
11333 +
            let case ast::NodeValue::FnParam(_) = paramNode.value
11334 +
                else panic "checkLinearFn: expected parameter";
11335 +
            try addLinearBinding(&mut checker, &mut env, paramNode);
11336 +
        }
11337 +
        try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
11338 +
        try finishLinearScope(&mut checker, &mut env, 0);
8923 11339
    }
8924 -
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
8925 -
    try finishLinearScope(&mut checker, &mut env, 0);
8926 11340
}
8927 11341
8928 11342
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
8929 -
unsafe fn resolveModuleDefs(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
11343 +
unsafe fn resolveModuleDefs 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
8930 11344
    for stmt in block.statements {
11345 +
        try resolveNominalApplications(self, stmt);
8931 11346
        try visitDef(self, stmt);
11347 +
        try resolveNominalApplications(self, stmt);
8932 11348
    }
8933 11349
}
8934 11350
8935 11351
/// Resolve all packages.
8936 11352
/// The graph must outlive later uses of the resolver.
8937 -
export unsafe fn resolve(self: &mut Resolver, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
11353 +
export unsafe fn resolve 'arena (self: &mut Resolver 'arena, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
8938 11354
    set self.moduleGraph = graph as *unsafe module::ModuleGraph;
8939 11355
8940 11356
    // 1. Bind all package roots to enable cross-package references.
8941 11357
    for i in 0..packages.len {
8942 11358
        let pkg = packages[i];
8958 11374
    }
8959 11375
    return diagnostics(self);
8960 11376
}
8961 11377
8962 11378
/// Resolve a package.
8963 -
unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
11379 +
unsafe fn resolvePackage 'arena (self: &mut Resolver 'arena, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
8964 11380
    let rootId = rootEntry.id;
8965 11381
    let scope = self.moduleScopes[rootId as u32]
8966 11382
        else panic "resolvePackage: module scope not found";
8967 11383
8968 11384
    // Set up the module scope for this package.
lib/std/lang/resolver/printer.rad +91 -6
30 30
    io::print(name);
31 31
    io::print("'");
32 32
}
33 33
34 34
/// Print a pointer-like type prefix.
35 -
fn printPtrPrefix(class: types::PointerClass, mutable: bool) {
35 +
unsafe fn printPtrPrefix(class: types::PointerClass, mutable: bool) {
36 36
    match class {
37 37
        case types::PointerClass::Owned => io::print("*"),
38 38
        case types::PointerClass::Ref => io::print("&"),
39 +
        case types::PointerClass::Region(region) => {
40 +
            io::print("&");
41 +
            io::print(region.name);
42 +
            io::print(" ");
43 +
        }
39 44
        case types::PointerClass::Unsafe => io::print("*unsafe "),
40 45
    }
41 46
    if mutable {
42 47
        io::print("mut ");
43 48
    }
102 107
            io::print("i32");
103 108
        }
104 109
        case super::Type::I64 => {
105 110
            io::print("i64");
106 111
        }
112 +
        case super::Type::Cell { class, payload } => {
113 +
            printPtrPrefix(class, false);
114 +
            io::print("cell ");
115 +
            printTypeBody(*payload, brief);
116 +
        }
117 +
        case super::Type::Session(region) => {
118 +
            io::print("Session ");
119 +
            io::print(region.name);
120 +
        }
107 121
        case super::Type::Pointer { class, target, mutable } => {
108 122
            printPtrPrefix(class, mutable);
109 123
            printTypeBody(*target, brief);
110 124
        }
111 125
        case super::Type::Slice { class, item, mutable } => {
174 188
}
175 189
176 190
/// Print detailed information about a nominal type (record or union).
177 191
unsafe fn printNominalType(info: *unsafe super::NominalType) {
178 192
    match *info {
179 -
        case super::NominalType::Placeholder(_) => {
193 +
        case super::NominalType::Placeholder(_), super::NominalType::Resolving(_) => {
180 194
            io::print("<placeholder>");
181 195
        }
196 +
        case super::NominalType::Application(applied) => {
197 +
            printNominalTypeName(applied.base);
198 +
            printNominalArguments(applied);
199 +
        }
182 200
        case super::NominalType::Record(recordType) => {
183 201
            io::print("record {");
184 202
            if recordType.fields.len > 0 {
185 203
                io::print(" ");
186 204
                for i in 0..recordType.fields.len {
224 242
                io::print(" ");
225 243
            }
226 244
            io::print("}");
227 245
        }
228 246
    }
247 +
    if let applied = super::nominalApplication(info) {
248 +
        if let case super::NominalType::Application(_) = *info {
249 +
            return;
250 +
        }
251 +
        printNominalArguments(applied);
252 +
    }
229 253
}
230 254
231 255
/// Print just the type kind for a nominal type, without detailed info.
232 256
unsafe fn printNominalTypeName(info: *unsafe super::NominalType) {
233 257
    match *info {
234 -
        case super::NominalType::Placeholder(_) => {
258 +
        case super::NominalType::Placeholder(_), super::NominalType::Resolving(_) => {
235 259
            io::print("<placeholder>");
236 260
        }
261 +
        case super::NominalType::Application(applied) => {
262 +
            printNominalTypeName(applied.base);
263 +
            printNominalArguments(applied);
264 +
        }
237 265
        case super::NominalType::Record(_) => {
238 266
            io::print("<record>");
239 267
        }
240 268
        case super::NominalType::Union(_) => {
241 269
            io::print("<union>");
242 270
        }
243 271
    }
272 +
    if let applied = super::nominalApplication(info) {
273 +
        if let case super::NominalType::Application(_) = *info {
274 +
            return;
275 +
        }
276 +
        printNominalArguments(applied);
277 +
    }
244 278
}
245 279
246 280
/// Print a single diagnostic entry.
247 -
unsafe fn printError(err: &super::Error, res: &super::Resolver) {
281 +
unsafe fn printError 'arena (err: &super::Error, res: &super::Resolver 'arena) {
248 282
    if let node = err.node {
249 283
        // Find the module containing this error.
250 284
        if let moduleEntry = module::get(res.moduleGraph, err.moduleId) {
251 285
            // Get the source text if available.
252 -
            if let source = moduleEntry.source {
286 +
            if let source = module::sourceFor(moduleEntry) {
253 287
                // Convert offset to location.
254 288
                if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) {
255 289
                    // Print: filename:line:col: error: message
256 290
                    if let case scanner::SourceLoc::File(path) = loc.source {
257 291
                        io::print(path);
445 479
            io::print("catch with inferred binding requires single error type; use typed catches for multiple error types");
446 480
        }
447 481
        case super::ErrorKind::TryCatchDuplicateType => {
448 482
            io::print("duplicate error type in catch clauses");
449 483
        }
484 +
        case super::ErrorKind::AmbiguousRegionalError => {
485 +
            io::print("error types must remain distinct after region erasure");
486 +
        }
450 487
        case super::ErrorKind::TryCatchNonExhaustive => {
451 488
            io::print("catch clauses do not cover all error types");
452 489
        }
453 490
        case super::ErrorKind::MissingTry => {
454 491
            io::print("called fallible function without using try");
530 567
            io::print("`Copy` composite contains a non-copy value");
531 568
        }
532 569
        case super::ErrorKind::ConflictingOwnershipMarkers => {
533 570
            io::print("a composite cannot be both `Copy` and `Once`");
534 571
        }
572 +
        case super::ErrorKind::UnknownRegion(name) => {
573 +
            io::print("unknown region ");
574 +
            io::print(name);
575 +
        }
576 +
        case super::ErrorKind::RegionArgumentCount(count) => {
577 +
            printMismatch("region argument count", "expected", count);
578 +
        }
579 +
        case super::ErrorKind::RegionInference(name) => {
580 +
            io::print("cannot infer a consistent argument for region ");
581 +
            io::print(name);
582 +
        }
583 +
        case super::ErrorKind::RegionParent(name) => {
584 +
            io::print("region argument does not satisfy the parent relation for ");
585 +
            io::print(name);
586 +
        }
587 +
        case super::ErrorKind::RecursiveType => io::print("recursive value type has no finite layout"),
588 +
        case super::ErrorKind::RegionCycle(name) => {
589 +
            io::print("cyclic region parent relation for ");
590 +
            io::print(name);
591 +
        }
535 592
        case super::ErrorKind::InvalidRefPosition => {
536 593
            io::print("reference type is only allowed as a parameter or local binding");
537 594
        }
538 595
        case super::ErrorKind::RefBinding => {
539 596
            io::print("reference local requires an immutable binding to existing storage");
545 602
            io::print("unsafe operation requires an unsafe function or block");
546 603
        }
547 604
        case super::ErrorKind::UnsafeCall => {
548 605
            io::print("calling an unsafe function requires an unsafe function or block");
549 606
        }
607 +
        case super::ErrorKind::InvalidAllocationValue => {
608 +
            io::print("session allocation requires a value safe for bulk reclamation and plain Copy elements for slices");
609 +
        }
610 +
        case super::ErrorKind::InvalidAllocationLayout => {
611 +
            io::print("invalid or overflowing allocation layout");
612 +
        }
613 +
        case super::ErrorKind::InvalidAllocationRuntime => {
614 +
            io::print("invalid standard allocation trait signature");
615 +
        }
616 +
        case super::ErrorKind::InvalidCellPayload => {
617 +
            io::print("cell payload must be a storable plain Copy value");
618 +
        }
619 +
        case super::ErrorKind::InvalidSessionSource => {
620 +
            io::print("session requires one exclusive borrow of an std::lang::alloc::Alloc implementer");
621 +
        }
622 +
        case super::ErrorKind::RegionalLoanOverflow => io::print("too many simultaneous regional projections"),
623 +
        case super::ErrorKind::RegionEscape(name) => {
624 +
            io::print("value outlives region ");
625 +
            io::print(name);
626 +
        }
550 627
        case super::ErrorKind::Internal => {
551 628
            io::print("internal compiler error");
552 629
        }
553 630
        case super::ErrorKind::RecordFieldOutOfOrder { .. } => {
554 631
            io::print("record field out of order");
568 645
    }
569 646
    io::print("\n");
570 647
}
571 648
572 649
/// Entry point for printing resolver diagnostics in vim quickfix format.
573 -
export unsafe fn printDiagnostics(diag: &super::Diagnostics, res: &super::Resolver) {
650 +
export unsafe fn printDiagnostics 'arena (diag: &super::Diagnostics, res: &super::Resolver 'arena) {
574 651
    for i in 0..diag.errors.len {
575 652
        printError(&diag.errors[i], res);
576 653
    }
577 654
}
655 +
656 +
/// Print an applied nominal type's explicit region arguments.
657 +
unsafe fn printNominalArguments(applied: *unsafe super::NominalApplication) {
658 +
    for region in applied.arguments {
659 +
        io::print(" ");
660 +
        io::print(region.name);
661 +
    }
662 +
}
lib/std/lang/resolver/tests.rad +4954 -3120
1 1
//! Resolver tests.
2 2
3 +
/// Region identity, borrowing, and nominal application tests.
4 +
@test mod regions;
5 +
3 6
use std::mem;
4 7
use std::testing;
5 8
use std::lang::alloc;
6 9
use std::lang::ast;
7 10
use std::lang::types;
27 30
28 31
/// Package scope used by resolver tests.
29 32
unsafe static PKG_SCOPE: super::Scope = undefined;
30 33
31 34
/// Module entries used by resolver tests.
32 -
unsafe static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined;
35 +
unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; 8] = [nil; 8];
33 36
34 37
/// Module graph used by resolver tests.
35 38
unsafe static MODULE_GRAPH: module::ModuleGraph = undefined;
36 39
37 40
/// Module AST arena storage used by resolver tests.
38 -
static MODULE_ARENA_STORAGE: [u8; 4096] = [0; 4096];
41 +
static MODULE_ARENA_STORAGE: [u8; 16384] = [0; 16384];
39 42
40 43
/// Module AST arena used by resolver tests.
41 44
unsafe static MODULE_ARENA: ast::NodeArena = undefined;
42 45
43 46
/// Interned string pool used by resolver tests.
51 54
    "Second", "Opt", "x",
52 55
    "value", "idx"
53 56
];
54 57
55 58
/// Resolver result with AST, used by test helpers.
56 -
record TestResult: Copy {
59 +
export record TestResult: Copy {
60 +
    /// Diagnostics from semantic analysis.
57 61
    diagnostics: super::Diagnostics,
62 +
    /// Root of the parsed test program.
58 63
    root: *ast::Node,
59 64
}
60 65
61 66
/// Create isolated storage for tests to avoid conflicts with global resolver storage.
62 67
unsafe fn testStorage() -> super::ResolverStorage {
63 68
    return super::ResolverStorage {
64 -
        arena: alloc::new(&mut ARENA_STORAGE[..]),
65 69
        nodeData: &mut NODE_DATA_STORAGE[..],
66 70
        pkgScope: &mut PKG_SCOPE,
67 71
        errors: &mut ERROR_STORAGE[..],
68 72
    };
69 73
}
70 74
75 +
/// Create an arena owner for one resolver test scope.
76 +
export unsafe fn testArena() -> alloc::Arena {
77 +
    return alloc::new(&mut ARENA_STORAGE[..]);
78 +
}
79 +
71 80
/// Construct a resolver backed by test storage and a synthetic module graph.
72 -
unsafe fn testResolver() -> super::Resolver {
81 +
export unsafe fn testResolver 'arena (arena: &'arena mut alloc::Arena) -> super::Resolver 'arena {
73 82
    // TODO: This should be initialized only once.
74 83
    for i in 0..LITERALS.len {
75 84
        strings::intern(&mut STRING_POOL, LITERALS[i]);
76 85
    }
77 86
    // TODO: Use local static for this.
78 87
    // Reset the module graph for each test.
79 88
    set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]);
80 89
    set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA);
81 90
    let config = super::Config { buildTest: true };
82 -
    let res = super::resolver(testStorage(), config);
91 +
    let res = super::resolver(arena, testStorage(), config);
83 92
84 93
    return res;
85 94
}
86 95
87 96
/// Resolve a block of statements by wrapping them in a synthetic function.
88 -
unsafe fn resolveStatements(
89 -
    self: &mut super::Resolver, block: ast::Block, arena: &mut ast::NodeArena
97 +
unsafe fn resolveStatements 'arena (
98 +
    self: &mut super::Resolver 'arena, block: ast::Block, arena: &mut ast::NodeArena
90 99
) -> TestResult throws (super::ResolveError) {
91 100
    let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements);
92 101
    let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch {
93 102
        return TestResult { diagnostics: super::diagnostics(self), root: module.modBody };
94 103
    };
95 104
    return TestResult { diagnostics, root: module.fnBody };
96 105
}
97 106
98 107
/// Parse and analyze an expression string for testing.
99 -
unsafe fn resolveExprStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
108 +
unsafe fn resolveExprStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) {
100 109
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
101 110
    let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL);
102 111
    parser::advance(&mut p);
103 112
104 113
    let expr = try parser::parseExpr(&mut p) catch {
110 119
    return TestResult { diagnostics, root: expr };
111 120
}
112 121
113 122
/// Parse and analyze a module string for testing.
114 123
/// Use this for code with `fn`, `record`, `union`, etc. at the top level.
115 -
unsafe fn resolveProgramStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
124 +
export unsafe fn resolveProgramStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) {
116 125
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
117 126
    let stmt: *ast::Node = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
118 127
        panic "resolveProgramStr: parsing failed";
119 128
    };
120 129
    let diagnostics = try super::resolveModuleRoot(self, stmt) catch {
121 130
        throw testing::TestError::Failed;
122 131
    };
123 132
    return TestResult { diagnostics, root: stmt };
124 133
}
125 134
135 +
/// Resolve session diagnostics against the standard allocator declaration contract.
136 +
export unsafe fn resolveSessionProgramStr 'arena (self: &mut super::Resolver 'arena, program: *[u8]) -> TestResult
137 +
    throws (testing::TestError)
138 +
{
139 +
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
140 +
    let root = try registerModule(&mut MODULE_GRAPH, nil, "std", "export mod lang; mod app;", &mut arena);
141 +
    let lang = try registerModule(&mut MODULE_GRAPH, root, "lang", "export mod alloc;", &mut arena);
142 +
    let allocator = try registerModule(&mut MODULE_GRAPH, lang, "alloc",
143 +
        "export record Arena { data: *mut [u8], offset: u32 } export fn reset(arena: &mut Arena) { set arena.offset = 0; } export union AllocError: Copy { OutOfMemory } export trait Alloc { unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); } instance Alloc for Arena { unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { panic; } unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { panic; } }",
144 +
        &mut arena);
145 +
    let app = try registerModule(&mut MODULE_GRAPH, root, "app", program, &mut arena);
146 +
    return try resolveModuleTree(self, root);
147 +
}
148 +
126 149
/// Parse and analyze a block of statements (eg. inside a function body) for testing.
127 150
/// Use this for code with `let` bindings and expressions, not module-level declarations.
128 -
unsafe fn resolveBlockStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
151 +
unsafe fn resolveBlockStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) {
129 152
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
130 153
    let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
131 154
        panic "resolveBlockStr: parsing failed";
132 155
    };
133 156
    let case ast::NodeValue::Block(block) = parsed.value
141 164
        root: analysis.root,
142 165
    };
143 166
}
144 167
145 168
/// Resolve a module with the full resolution process.
146 -
unsafe fn resolveModuleTree(
147 -
    res: &mut super::Resolver,
169 +
unsafe fn resolveModuleTree 'arena (
170 +
    res: &mut super::Resolver 'arena,
148 171
    rootId: u16
149 172
) -> TestResult throws (testing::TestError) {
150 173
    let root = module::get(&MODULE_GRAPH, rootId)
151 174
        else throw testing::TestError::Failed;
152 -
    let rootAst = root.ast
175 +
    let rootAst = module::astFor(root)
153 176
        else throw testing::TestError::Failed;
154 177
    let packages = [super::Pkg {
155 178
        rootEntry: root,
156 179
        rootAst,
157 180
    }];
189 212
    };
190 213
    return modId;
191 214
}
192 215
193 216
/// Ensure an expression statement produces the expected type and return the expression node.
194 -
fn expectExprStmtType(self: &super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node
217 +
fn expectExprStmtType 'arena (self: &super::Resolver 'arena, node: *ast::Node, expected: super::Type) -> *ast::Node
195 218
    throws (testing::TestError)
196 219
{
197 220
    let case ast::NodeValue::ExprStmt(expr) = node.value
198 221
        else throw testing::TestError::Failed;
199 222
    try expectType(self, expr, expected);
200 223
201 224
    return expr;
202 225
}
203 226
204 227
/// Assert that the test result contains no diagnostic errors.
205 -
fn expectNoErrors(r: &TestResult) throws (testing::TestError) {
228 +
export fn expectNoErrors(r: &TestResult) throws (testing::TestError) {
206 229
    try testing::expect(super::success(&r.diagnostics));
207 230
}
208 231
209 232
/// Extract the first error from a test result, failing if none exists.
210 -
fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) {
233 +
export fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) {
211 234
    let err = super::errorAt(&result.diagnostics.errors[..], 0)
212 235
        else throw testing::TestError::Failed;
213 236
    return err;
214 237
}
215 238
313 336
    }
314 337
    return *actual == expected;
315 338
}
316 339
317 340
/// Extract the first error and ensure it has the expected kind.
318 -
fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error
341 +
export fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error
319 342
    throws (testing::TestError)
320 343
{
321 344
    let err = try expectError(result);
322 345
    try testing::expect(errorKindMatches(&err.kind, kind));
323 346
    return err;
324 347
}
325 348
326 349
/// Ensure an expression resolves to the expected type annotation.
327 -
fn expectType(self: &super::Resolver, expr: *ast::Node, expected: super::Type)
350 +
fn expectType 'arena (self: &super::Resolver 'arena, expr: *ast::Node, expected: super::Type)
328 351
    throws (testing::TestError)
329 352
{
330 353
    let actual = super::typeFor(self, expr)
331 354
        else throw testing::TestError::Failed;
332 355
345 368
    try testing::expect(mismatch.actual == actual);
346 369
}
347 370
348 371
/// Resolve a program and require successful analysis.
349 372
unsafe fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) {
350 -
    let mut a = testResolver();
351 -
    let result = try resolveProgramStr(&mut a, program);
352 -
    try expectNoErrors(&result);
373 +
    let mut testArena1 = testArena();
374 +
    let testStorage1: 'test1 = &mut testArena1 in {
375 +
        let mut a = testResolver(testStorage1);
376 +
        let result = try resolveProgramStr(&mut a, program);
377 +
        try expectNoErrors(&result);
378 +
    }
353 379
}
354 380
355 381
/// Require an inferred integer type mismatch.
356 382
unsafe fn expectIntMismatch(program: *[u8], expected: super::Type)
357 383
    throws (testing::TestError)
358 384
{
359 -
    let mut a = testResolver();
360 -
    let result = try resolveProgramStr(&mut a, program);
361 -
    let err = try expectError(&result);
362 -
    try expectTypeMismatch(err, expected, super::Type::Int);
385 +
    let mut testArena2 = testArena();
386 +
    let testStorage2: 'test2 = &mut testArena2 in {
387 +
        let mut a = testResolver(testStorage2);
388 +
        let result = try resolveProgramStr(&mut a, program);
389 +
        let err = try expectError(&result);
390 +
        try expectTypeMismatch(err, expected, super::Type::Int);
391 +
    }
363 392
}
364 393
365 394
/// Retrieve the nth statement from a block node.
366 -
fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node
395 +
export fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node
367 396
    throws (testing::TestError)
368 397
{
369 398
    let case ast::NodeValue::Block(body) = block.value
370 399
        else throw testing::TestError::Failed;
371 400
374 403
    }
375 404
    return body.statements[index];
376 405
}
377 406
378 407
/// Retrieve a function body block by function name from the program scope.
379 -
unsafe fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block
408 +
unsafe fn getFnBody 'arena (a: &super::Resolver 'arena, root: *ast::Node, name: *[u8]) -> ast::Block
380 409
    throws (testing::TestError)
381 410
{
382 411
    let scope = super::scopeFor(a, root)
383 412
        else throw testing::TestError::Failed;
384 413
    let sym = super::findSymbolInScope(scope, name)
417 446
    }
418 447
    panic "getUnionVariantPayload: variant not found";
419 448
}
420 449
421 450
/// Get a nominal type by name, in the scope of the given block node.
422 -
unsafe fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType
451 +
unsafe fn getTypeInScopeOf 'arena (a: &super::Resolver 'arena, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType
423 452
    throws (testing::TestError)
424 453
{
425 454
    let scope = super::scopeFor(a, blk)
426 455
        else throw testing::TestError::Failed;
427 456
    let sym = super::findSymbolInScope(scope, name)
430 459
        else throw testing::TestError::Failed;
431 460
    return ty;
432 461
}
433 462
434 463
/// Return the resolved type of a syntax node.
435 -
fn typeOf(a: &super::Resolver, node: *ast::Node) -> super::Type
464 +
fn typeOf 'arena (a: &super::Resolver 'arena, node: *ast::Node) -> super::Type
436 465
    throws (testing::TestError)
437 466
{
438 467
    let ty = super::typeFor(a, node)
439 468
        else throw testing::TestError::Failed;
440 469
    return ty;
472 501
473 502
    return *target;
474 503
}
475 504
476 505
/// Verify that a node has a constant integer value with the expected magnitude.
477 -
fn expectConstInt(a: &super::Resolver, node: *ast::Node, expected: u32)
506 +
fn expectConstInt 'arena (a: &super::Resolver 'arena, node: *ast::Node, expected: u32)
478 507
    throws (testing::TestError)
479 508
{
480 509
    let constVal = super::constValueEntry(a, node)
481 510
        else throw testing::TestError::Failed;
482 511
488 517
489 518
/// Resolve an expression that should evaluate to a constant, and verify it equals the expected value.
490 519
unsafe fn resolveAndExpectConstExpr(expr: *[u8], expected: u32)
491 520
    throws (testing::TestError)
492 521
{
493 -
    let mut a = testResolver();
494 -
    let result = try resolveExprStr(&mut a, expr);
495 -
    try expectNoErrors(&result);
496 -
    try expectType(&a, result.root, super::Type::U32);
497 -
    try expectConstInt(&a, result.root, expected);
522 +
    let mut testArena3 = testArena();
523 +
    let testStorage3: 'test3 = &mut testArena3 in {
524 +
        let mut a = testResolver(testStorage3);
525 +
        let result = try resolveExprStr(&mut a, expr);
526 +
        try expectNoErrors(&result);
527 +
        try expectType(&a, result.root, super::Type::U32);
528 +
        try expectConstInt(&a, result.root, expected);
529 +
    }
498 530
}
499 531
500 532
/// Resolve a statement that should evaluate to a constant, and verify it equals the expected value.
501 533
unsafe fn resolveAndExpectConstStmt(expr: *[u8], expected: u32)
502 534
    throws (testing::TestError)
503 535
{
504 -
    let mut a = testResolver();
505 -
    let result = try resolveProgramStr(&mut a, expr);
506 -
    try expectNoErrors(&result);
507 -
    let stmt = try getBlockStmt(result.root, 1);
508 -
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
509 -
    try expectConstInt(&a, expr, expected);
536 +
    let mut testArena4 = testArena();
537 +
    let testStorage4: 'test4 = &mut testArena4 in {
538 +
        let mut a = testResolver(testStorage4);
539 +
        let result = try resolveProgramStr(&mut a, expr);
540 +
        try expectNoErrors(&result);
541 +
        let stmt = try getBlockStmt(result.root, 1);
542 +
        let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
543 +
        try expectConstInt(&a, expr, expected);
544 +
    }
510 545
}
511 546
512 547
// Tests ///////////////////////////////////////////////////////////////////////
513 548
514 549
@test unsafe fn testResolveLit() throws (testing::TestError) {
515 -
    let mut a = testResolver();
516 -
    let result = try resolveExprStr(&mut a, "true");
550 +
    let mut testArena5 = testArena();
551 +
    let testStorage5: 'test5 = &mut testArena5 in {
552 +
        let mut a = testResolver(testStorage5);
553 +
        let result = try resolveExprStr(&mut a, "true");
517 554
518 -
    try expectNoErrors(&result);
519 -
    try expectType(&a, result.root, super::Type::Bool);
555 +
        try expectNoErrors(&result);
556 +
        try expectType(&a, result.root, super::Type::Bool);
557 +
    }
520 558
}
521 559
522 560
@test unsafe fn testResolveStringLiteralType() throws (testing::TestError) {
523 -
    let mut a = testResolver();
524 -
    let result = try resolveExprStr(&mut a, "\"hello\"");
561 +
    let mut testArena6 = testArena();
562 +
    let testStorage6: 'test6 = &mut testArena6 in {
563 +
        let mut a = testResolver(testStorage6);
564 +
        let result = try resolveExprStr(&mut a, "\"hello\"");
525 565
526 -
    try expectNoErrors(&result);
527 -
    let ty = try typeOf(&a, result.root);
528 -
    let elemTy = try expectSliceType(ty, false);
529 -
    try testing::expect(elemTy == super::Type::U8);
566 +
        try expectNoErrors(&result);
567 +
        let ty = try typeOf(&a, result.root);
568 +
        let elemTy = try expectSliceType(ty, false);
569 +
        try testing::expect(elemTy == super::Type::U8);
570 +
    }
530 571
}
531 572
532 573
@test unsafe fn testResolveAsNumeric() throws (testing::TestError) {
533 574
    {
534 -
        let mut a = testResolver();
535 -
        let result = try resolveExprStr(&mut a, "1 as u32");
536 -
        try expectNoErrors(&result);
537 -
        try expectType(&a, result.root, super::Type::U32);
575 +
        let mut testArena7 = testArena();
576 +
        let testStorage7: 'test7 = &mut testArena7 in {
577 +
            let mut a = testResolver(testStorage7);
578 +
            let result = try resolveExprStr(&mut a, "1 as u32");
579 +
            try expectNoErrors(&result);
580 +
            try expectType(&a, result.root, super::Type::U32);
581 +
        }
538 582
    } {
539 -
        let mut a = testResolver();
540 -
        let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;");
541 -
        try expectNoErrors(&result);
583 +
        let mut testArena8 = testArena();
584 +
        let testStorage8: 'test8 = &mut testArena8 in {
585 +
            let mut a = testResolver(testStorage8);
586 +
            let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;");
587 +
            try expectNoErrors(&result);
542 588
543 -
        let x = try getBlockStmt(result.root, 1);
544 -
        try expectExprStmtType(&a, x, super::Type::U8);
589 +
            let x = try getBlockStmt(result.root, 1);
590 +
            try expectExprStmtType(&a, x, super::Type::U8);
591 +
        }
545 592
    }
546 593
}
547 594
548 595
@test unsafe fn testResolveAsInvalid() throws (testing::TestError) {
549 -
    let mut a = testResolver();
550 -
    let result = try resolveProgramStr(&mut a, "true as u32");
551 -
552 -
    try expectErrorKind(
553 -
        &result,
554 -
        super::ErrorKind::InvalidAsCast(super::InvalidAsCast {
555 -
            from: super::Type::Bool,
556 -
            to: super::Type::U32,
596 +
    let mut testArena9 = testArena();
597 +
    let testStorage9: 'test9 = &mut testArena9 in {
598 +
        let mut a = testResolver(testStorage9);
599 +
        let result = try resolveProgramStr(&mut a, "true as u32");
600 +
601 +
        try expectErrorKind(
602 +
            &result,
603 +
            super::ErrorKind::InvalidAsCast(super::InvalidAsCast {
604 +
                from: super::Type::Bool,
605 +
                to: super::Type::U32,
557 606
        })
558 -
    );
607 +
        );
608 +
    }
559 609
}
560 610
561 611
@test unsafe fn testResolveAsUnionToInt() throws (testing::TestError) {
562 -
    let mut a = testResolver();
563 -
    let program = "union Color { Red } Color::Red as u32;";
564 -
    let result = try resolveProgramStr(&mut a, program);
565 -
    try expectNoErrors(&result);
612 +
    let mut testArena10 = testArena();
613 +
    let testStorage10: 'test10 = &mut testArena10 in {
614 +
        let mut a = testResolver(testStorage10);
615 +
        let program = "union Color { Red } Color::Red as u32;";
616 +
        let result = try resolveProgramStr(&mut a, program);
617 +
        try expectNoErrors(&result);
566 618
567 -
    let red = try getBlockStmt(result.root, 1);
568 -
    try expectExprStmtType(&a, red, super::Type::U32);
619 +
        let red = try getBlockStmt(result.root, 1);
620 +
        try expectExprStmtType(&a, red, super::Type::U32);
621 +
    }
569 622
}
570 623
571 624
@test unsafe fn testResolveBinding() throws (testing::TestError) {
572 -
    let mut a = testResolver();
573 -
    let result = try resolveBlockStr(&mut a, "let x: bool = true; x;");
574 -
    let stmt = try parser::tests::getBlockLastStmt(result.root);
625 +
    let mut testArena11 = testArena();
626 +
    let testStorage11: 'test11 = &mut testArena11 in {
627 +
        let mut a = testResolver(testStorage11);
628 +
        let result = try resolveBlockStr(&mut a, "let x: bool = true; x;");
629 +
        let stmt = try parser::tests::getBlockLastStmt(result.root);
575 630
576 -
    try expectNoErrors(&result);
577 -
    try expectType(&a, stmt, super::Type::Void);
578 -
    try expectExprStmtType(&a, stmt, super::Type::Bool);
631 +
        try expectNoErrors(&result);
632 +
        try expectType(&a, stmt, super::Type::Void);
633 +
        try expectExprStmtType(&a, stmt, super::Type::Bool);
579 634
580 -
    let case ast::NodeValue::ExprStmt(x) = stmt.value
581 -
        else throw testing::TestError::Failed;
635 +
        let case ast::NodeValue::ExprStmt(x) = stmt.value
636 +
            else throw testing::TestError::Failed;
582 637
583 -
    let sym = super::symbolFor(&a, x)
584 -
        else throw testing::TestError::Failed;
585 -
    let case super::SymbolData::Value { type: valType, .. } = sym.data
586 -
        else throw testing::TestError::Failed;
587 -
    try testing::expect(valType == super::Type::Bool);
638 +
        let sym = super::symbolFor(&a, x)
639 +
            else throw testing::TestError::Failed;
640 +
        let case super::SymbolData::Value { type: valType, .. } = sym.data
641 +
            else throw testing::TestError::Failed;
642 +
        try testing::expect(valType == super::Type::Bool);
643 +
    }
588 644
}
589 645
590 646
@test unsafe fn testResolveBindingInvalid() throws (testing::TestError) {
591 -
    let mut a = testResolver();
592 -
    let result = try resolveBlockStr(&mut a, "let x: i32 = true;");
593 -
    let err = try expectError(&result);
594 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
647 +
    let mut testArena12 = testArena();
648 +
    let testStorage12: 'test12 = &mut testArena12 in {
649 +
        let mut a = testResolver(testStorage12);
650 +
        let result = try resolveBlockStr(&mut a, "let x: i32 = true;");
651 +
        let err = try expectError(&result);
652 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
653 +
    }
595 654
}
596 655
597 656
@test unsafe fn testResolveDuplicateBinding() throws (testing::TestError) {
598 -
    let mut a = testResolver();
599 -
    let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;");
600 -
    let stmt = try parser::tests::getBlockLastStmt(result.root);
601 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x"));
657 +
    let mut testArena13 = testArena();
658 +
    let testStorage13: 'test13 = &mut testArena13 in {
659 +
        let mut a = testResolver(testStorage13);
660 +
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;");
661 +
        let stmt = try parser::tests::getBlockLastStmt(result.root);
662 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x"));
663 +
    }
602 664
}
603 665
604 666
@test unsafe fn testResolveConstLiteralValue() throws (testing::TestError) {
605 -
    let mut a = testResolver();
606 -
    let program = "constant ANSWER: i32 = 42;";
607 -
    let result = try resolveProgramStr(&mut a, program);
608 -
    try expectNoErrors(&result);
667 +
    let mut testArena14 = testArena();
668 +
    let testStorage14: 'test14 = &mut testArena14 in {
669 +
        let mut a = testResolver(testStorage14);
670 +
        let program = "constant ANSWER: i32 = 42;";
671 +
        let result = try resolveProgramStr(&mut a, program);
672 +
        try expectNoErrors(&result);
609 673
610 -
    let constNode = try getBlockStmt(result.root, 0);
611 -
    let sym = super::symbolFor(&a, constNode)
612 -
        else throw testing::TestError::Failed;
613 -
    let case super::SymbolData::Constant { type: constType, .. } = sym.data
614 -
        else throw testing::TestError::Failed;
615 -
    try testing::expect(constType == super::Type::I32);
674 +
        let constNode = try getBlockStmt(result.root, 0);
675 +
        let sym = super::symbolFor(&a, constNode)
676 +
            else throw testing::TestError::Failed;
677 +
        let case super::SymbolData::Constant { type: constType, .. } = sym.data
678 +
            else throw testing::TestError::Failed;
679 +
        try testing::expect(constType == super::Type::I32);
680 +
    }
616 681
}
617 682
618 683
@test unsafe fn testResolveConstRequiresConstantExpr() throws (testing::TestError) {
619 -
    let mut a = testResolver();
620 -
    let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }";
621 -
    let result = try resolveProgramStr(&mut a, program);
622 -
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
684 +
    let mut testArena15 = testArena();
685 +
    let testStorage15: 'test15 = &mut testArena15 in {
686 +
        let mut a = testResolver(testStorage15);
687 +
        let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }";
688 +
        let result = try resolveProgramStr(&mut a, program);
689 +
        let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
623 690
624 -
    let errNode = err.node
625 -
        else throw testing::TestError::Failed;
626 -
    let case ast::NodeValue::Call(_) = errNode.value
627 -
        else throw testing::TestError::Failed;
691 +
        let errNode = err.node
692 +
            else throw testing::TestError::Failed;
693 +
        let case ast::NodeValue::Call(_) = errNode.value
694 +
            else throw testing::TestError::Failed;
695 +
    }
628 696
}
629 697
630 698
@test unsafe fn testResolveStaticLiteralValue() throws (testing::TestError) {
631 -
    let mut a = testResolver();
632 -
    let program = "static COUNTER: i32 = 0;";
633 -
    let result = try resolveProgramStr(&mut a, program);
634 -
    try expectNoErrors(&result);
699 +
    let mut testArena16 = testArena();
700 +
    let testStorage16: 'test16 = &mut testArena16 in {
701 +
        let mut a = testResolver(testStorage16);
702 +
        let program = "static COUNTER: i32 = 0;";
703 +
        let result = try resolveProgramStr(&mut a, program);
704 +
        try expectNoErrors(&result);
635 705
636 -
    let staticNode = try getBlockStmt(result.root, 0);
637 -
    let sym = super::symbolFor(&a, staticNode)
638 -
        else throw testing::TestError::Failed;
639 -
    let case super::SymbolData::Value { type: valType, .. } = sym.data
640 -
        else throw testing::TestError::Failed;
641 -
    try testing::expect(valType == super::Type::I32);
706 +
        let staticNode = try getBlockStmt(result.root, 0);
707 +
        let sym = super::symbolFor(&a, staticNode)
708 +
            else throw testing::TestError::Failed;
709 +
        let case super::SymbolData::Value { type: valType, .. } = sym.data
710 +
            else throw testing::TestError::Failed;
711 +
        try testing::expect(valType == super::Type::I32);
712 +
    }
642 713
}
643 714
644 715
@test unsafe fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) {
645 -
    let mut a = testResolver();
646 -
    let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();";
647 -
    let result = try resolveProgramStr(&mut a, program);
648 -
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
716 +
    let mut testArena17 = testArena();
717 +
    let testStorage17: 'test17 = &mut testArena17 in {
718 +
        let mut a = testResolver(testStorage17);
719 +
        let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();";
720 +
        let result = try resolveProgramStr(&mut a, program);
721 +
        let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
649 722
650 -
    let errNode = err.node
651 -
        else throw testing::TestError::Failed;
652 -
    let case ast::NodeValue::Call(_) = errNode.value
653 -
        else throw testing::TestError::Failed;
723 +
        let errNode = err.node
724 +
            else throw testing::TestError::Failed;
725 +
        let case ast::NodeValue::Call(_) = errNode.value
726 +
            else throw testing::TestError::Failed;
727 +
    }
654 728
}
655 729
656 730
@test unsafe fn testSymbolStoresFnAttributes() throws (testing::TestError) {
657 -
    let mut a = testResolver();
658 -
    let program = "@default export fn f() { return; }";
659 -
    let result = try resolveProgramStr(&mut a, program);
660 -
    try expectNoErrors(&result);
731 +
    let mut testArena18 = testArena();
732 +
    let testStorage18: 'test18 = &mut testArena18 in {
733 +
        let mut a = testResolver(testStorage18);
734 +
        let program = "@default export fn f() { return; }";
735 +
        let result = try resolveProgramStr(&mut a, program);
736 +
        try expectNoErrors(&result);
661 737
662 -
    let scope = super::scopeFor(&a, result.root)
663 -
        else throw testing::TestError::Failed;
664 -
    let sym = super::findSymbolInScope(scope, "f")
665 -
        else throw testing::TestError::Failed;
738 +
        let scope = super::scopeFor(&a, result.root)
739 +
            else throw testing::TestError::Failed;
740 +
        let sym = super::findSymbolInScope(scope, "f")
741 +
            else throw testing::TestError::Failed;
666 742
667 -
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
668 -
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
669 -
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern));
743 +
        try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
744 +
        try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
745 +
        try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern));
746 +
    }
670 747
}
671 748
672 749
@test unsafe fn testSymbolStoresRecordAttributes() throws (testing::TestError) {
673 -
    let mut a = testResolver();
674 -
    let program = "export record S { value: i32 }";
675 -
    let result = try resolveProgramStr(&mut a, program);
676 -
    try expectNoErrors(&result);
750 +
    let mut testArena19 = testArena();
751 +
    let testStorage19: 'test19 = &mut testArena19 in {
752 +
        let mut a = testResolver(testStorage19);
753 +
        let program = "export record S { value: i32 }";
754 +
        let result = try resolveProgramStr(&mut a, program);
755 +
        try expectNoErrors(&result);
677 756
678 -
    let scope = super::scopeFor(&a, result.root)
679 -
        else throw testing::TestError::Failed;
680 -
    let sym = super::findSymbolInScope(scope, "S")
681 -
        else throw testing::TestError::Failed;
757 +
        let scope = super::scopeFor(&a, result.root)
758 +
            else throw testing::TestError::Failed;
759 +
        let sym = super::findSymbolInScope(scope, "S")
760 +
            else throw testing::TestError::Failed;
682 761
683 -
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
684 -
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
762 +
        try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
763 +
        try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
764 +
    }
685 765
}
686 766
687 767
@test unsafe fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) {
688 -
    let mut a = testResolver();
689 -
    let program = "@default record T { value: i32 }";
690 -
    let result = try resolveProgramStr(&mut a, program);
691 -
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
768 +
    let mut testArena20 = testArena();
769 +
    let testStorage20: 'test20 = &mut testArena20 in {
770 +
        let mut a = testResolver(testStorage20);
771 +
        let program = "@default record T { value: i32 }";
772 +
        let result = try resolveProgramStr(&mut a, program);
773 +
        try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
774 +
    }
692 775
}
693 776
694 777
@test unsafe fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) {
695 -
    let mut a = testResolver();
696 -
    let program = "@default union Result { Ok, Err }";
697 -
    let result = try resolveProgramStr(&mut a, program);
698 -
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
778 +
    let mut testArena21 = testArena();
779 +
    let testStorage21: 'test21 = &mut testArena21 in {
780 +
        let mut a = testResolver(testStorage21);
781 +
        let program = "@default union Result { Ok, Err }";
782 +
        let result = try resolveProgramStr(&mut a, program);
783 +
        try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
784 +
    }
699 785
}
700 786
701 787
@test unsafe fn testResolveArrayLiteralTyped() throws (testing::TestError) {
702 -
    let mut a = testResolver();
703 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];");
704 -
    try expectNoErrors(&result);
788 +
    let mut testArena22 = testArena();
789 +
    let testStorage22: 'test22 = &mut testArena22 in {
790 +
        let mut a = testResolver(testStorage22);
791 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];");
792 +
        try expectNoErrors(&result);
705 793
706 -
    let stmt = try getBlockStmt(result.root, 0);
707 -
    let case ast::NodeValue::Let(decl) = stmt.value
708 -
        else throw testing::TestError::Failed;
709 -
    let arrayTy = try typeOf(&a, decl.value);
710 -
    let elemTy = try expectArrayType(arrayTy, 2);
711 -
    try testing::expect(elemTy == super::Type::I32);
794 +
        let stmt = try getBlockStmt(result.root, 0);
795 +
        let case ast::NodeValue::Let(decl) = stmt.value
796 +
            else throw testing::TestError::Failed;
797 +
        let arrayTy = try typeOf(&a, decl.value);
798 +
        let elemTy = try expectArrayType(arrayTy, 2);
799 +
        try testing::expect(elemTy == super::Type::I32);
800 +
    }
712 801
}
713 802
714 803
@test unsafe fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) {
715 -
    let mut a = testResolver();
716 -
    let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];");
717 -
    let err = try expectError(&result);
718 -
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
804 +
    let mut testArena23 = testArena();
805 +
    let testStorage23: 'test23 = &mut testArena23 in {
806 +
        let mut a = testResolver(testStorage23);
807 +
        let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];");
808 +
        let err = try expectError(&result);
809 +
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
810 +
    }
719 811
}
720 812
721 813
@test unsafe fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) {
722 -
    let mut a = testResolver();
723 -
    let result = try resolveProgramStr(&mut a, "let xs = [1, 2];");
724 -
    try expectErrorKind(&result, super::ErrorKind::CannotInferType);
814 +
    let mut testArena24 = testArena();
815 +
    let testStorage24: 'test24 = &mut testArena24 in {
816 +
        let mut a = testResolver(testStorage24);
817 +
        let result = try resolveProgramStr(&mut a, "let xs = [1, 2];");
818 +
        try expectErrorKind(&result, super::ErrorKind::CannotInferType);
819 +
    }
725 820
}
726 821
727 822
@test unsafe fn testResolveArrayLiteralOverflow() throws (testing::TestError) {
728 -
    let mut a = testResolver();
729 -
    let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];");
730 -
    let err = try expectError(&result);
731 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
732 -
        else throw testing::TestError::Failed;
823 +
    let mut testArena25 = testArena();
824 +
    let testStorage25: 'test25 = &mut testArena25 in {
825 +
        let mut a = testResolver(testStorage25);
826 +
        let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];");
827 +
        let err = try expectError(&result);
828 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
829 +
            else throw testing::TestError::Failed;
830 +
    }
733 831
}
734 832
735 833
@test unsafe fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) {
736 -
    let mut a = testResolver();
737 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];");
738 -
    let err = try expectError(&result);
739 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
740 -
        else throw testing::TestError::Failed;
834 +
    let mut testArena26 = testArena();
835 +
    let testStorage26: 'test26 = &mut testArena26 in {
836 +
        let mut a = testResolver(testStorage26);
837 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];");
838 +
        let err = try expectError(&result);
839 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
840 +
            else throw testing::TestError::Failed;
841 +
    }
741 842
}
742 843
743 844
@test unsafe fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) {
744 -
    let mut a = testResolver();
745 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];");
746 -
    let err = try expectError(&result);
747 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
748 -
        else throw testing::TestError::Failed;
845 +
    let mut testArena27 = testArena();
846 +
    let testStorage27: 'test27 = &mut testArena27 in {
847 +
        let mut a = testResolver(testStorage27);
848 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];");
849 +
        let err = try expectError(&result);
850 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
851 +
            else throw testing::TestError::Failed;
852 +
    }
749 853
}
750 854
751 855
@test unsafe fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) {
752 -
    let mut a = testResolver();
753 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];");
754 -
    try expectNoErrors(&result);
856 +
    let mut testArena28 = testArena();
857 +
    let testStorage28: 'test28 = &mut testArena28 in {
858 +
        let mut a = testResolver(testStorage28);
859 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];");
860 +
        try expectNoErrors(&result);
861 +
    }
755 862
}
756 863
757 864
@test unsafe fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) {
758 -
    let mut a = testResolver();
759 -
    let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];");
760 -
    try expectNoErrors(&result);
865 +
    let mut testArena29 = testArena();
866 +
    let testStorage29: 'test29 = &mut testArena29 in {
867 +
        let mut a = testResolver(testStorage29);
868 +
        let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];");
869 +
        try expectNoErrors(&result);
761 870
762 -
    let stmt = try getBlockStmt(result.root, 0);
763 -
    let case ast::NodeValue::Let(decl) = stmt.value
764 -
        else throw testing::TestError::Failed;
765 -
    let gridTy = try typeOf(&a, decl.value);
766 -
    let rowTy = try expectArrayType(gridTy, 2);
767 -
    let elemTy = try expectArrayType(rowTy, 2);
768 -
    try testing::expect(elemTy == super::Type::I32);
871 +
        let stmt = try getBlockStmt(result.root, 0);
872 +
        let case ast::NodeValue::Let(decl) = stmt.value
873 +
            else throw testing::TestError::Failed;
874 +
        let gridTy = try typeOf(&a, decl.value);
875 +
        let rowTy = try expectArrayType(gridTy, 2);
876 +
        let elemTy = try expectArrayType(rowTy, 2);
877 +
        try testing::expect(elemTy == super::Type::I32);
878 +
    }
769 879
}
770 880
771 881
@test unsafe fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) {
772 -
    let mut a = testResolver();
773 -
    let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];");
774 -
    try expectNoErrors(&result);
882 +
    let mut testArena30 = testArena();
883 +
    let testStorage30: 'test30 = &mut testArena30 in {
884 +
        let mut a = testResolver(testStorage30);
885 +
        let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];");
886 +
        try expectNoErrors(&result);
775 887
776 -
    let stmt = try getBlockStmt(result.root, 0);
777 -
    let case ast::NodeValue::Let(decl) = stmt.value
778 -
        else throw testing::TestError::Failed;
779 -
    let arrayTy = try typeOf(&a, decl.value);
780 -
    let elemTy = try expectArrayType(arrayTy, 2);
781 -
    let case super::Type::Optional(inner) = elemTy
782 -
        else throw testing::TestError::Failed;
783 -
    try testing::expect(*inner == super::Type::I32);
888 +
        let stmt = try getBlockStmt(result.root, 0);
889 +
        let case ast::NodeValue::Let(decl) = stmt.value
890 +
            else throw testing::TestError::Failed;
891 +
        let arrayTy = try typeOf(&a, decl.value);
892 +
        let elemTy = try expectArrayType(arrayTy, 2);
893 +
        let case super::Type::Optional(inner) = elemTy
894 +
            else throw testing::TestError::Failed;
895 +
        try testing::expect(*inner == super::Type::I32);
896 +
    }
784 897
}
785 898
786 899
@test unsafe fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) {
787 -
    let mut a = testResolver();
788 -
    let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];");
789 -
    let err = try expectError(&result);
790 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
791 -
        else throw testing::TestError::Failed;
900 +
    let mut testArena31 = testArena();
901 +
    let testStorage31: 'test31 = &mut testArena31 in {
902 +
        let mut a = testResolver(testStorage31);
903 +
        let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];");
904 +
        let err = try expectError(&result);
905 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
906 +
            else throw testing::TestError::Failed;
907 +
    }
792 908
}
793 909
794 910
@test unsafe fn testResolveArrayRepeatBasic() throws (testing::TestError) {
795 -
    let mut a = testResolver();
796 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];");
797 -
    try expectNoErrors(&result);
911 +
    let mut testArena32 = testArena();
912 +
    let testStorage32: 'test32 = &mut testArena32 in {
913 +
        let mut a = testResolver(testStorage32);
914 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];");
915 +
        try expectNoErrors(&result);
798 916
799 -
    let stmt = try getBlockStmt(result.root, 0);
800 -
    let case ast::NodeValue::Let(decl) = stmt.value
801 -
        else throw testing::TestError::Failed;
802 -
    let arrayTy = try typeOf(&a, decl.value);
803 -
    let elemTy = try expectArrayType(arrayTy, 3);
804 -
    try testing::expect(elemTy == super::Type::I32);
917 +
        let stmt = try getBlockStmt(result.root, 0);
918 +
        let case ast::NodeValue::Let(decl) = stmt.value
919 +
            else throw testing::TestError::Failed;
920 +
        let arrayTy = try typeOf(&a, decl.value);
921 +
        let elemTy = try expectArrayType(arrayTy, 3);
922 +
        try testing::expect(elemTy == super::Type::I32);
923 +
    }
805 924
}
806 925
807 926
@test unsafe fn testResolveArrayRepeatWithExpression() throws (testing::TestError) {
808 -
    let mut a = testResolver();
809 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];");
810 -
    try expectNoErrors(&result);
927 +
    let mut testArena33 = testArena();
928 +
    let testStorage33: 'test33 = &mut testArena33 in {
929 +
        let mut a = testResolver(testStorage33);
930 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];");
931 +
        try expectNoErrors(&result);
811 932
812 -
    let stmt = try getBlockStmt(result.root, 0);
813 -
    let case ast::NodeValue::Let(decl) = stmt.value
814 -
        else throw testing::TestError::Failed;
815 -
    let arrayTy = try typeOf(&a, decl.value);
816 -
    let elemTy = try expectArrayType(arrayTy, 5);
817 -
    try testing::expect(elemTy == super::Type::I32);
933 +
        let stmt = try getBlockStmt(result.root, 0);
934 +
        let case ast::NodeValue::Let(decl) = stmt.value
935 +
            else throw testing::TestError::Failed;
936 +
        let arrayTy = try typeOf(&a, decl.value);
937 +
        let elemTy = try expectArrayType(arrayTy, 5);
938 +
        try testing::expect(elemTy == super::Type::I32);
939 +
    }
818 940
}
819 941
820 942
@test unsafe fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) {
821 -
    let mut a = testResolver();
822 -
    // `3 * 1` folds to a compile-time constant, so the repeat count is valid.
823 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];");
824 -
    try expectNoErrors(&result);
943 +
    let mut testArena34 = testArena();
944 +
    let testStorage34: 'test34 = &mut testArena34 in {
945 +
        let mut a = testResolver(testStorage34);
946 +
        // `3 * 1` folds to a compile-time constant, so the repeat count is valid.
947 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];");
948 +
        try expectNoErrors(&result);
949 +
    }
825 950
}
826 951
827 952
@test unsafe fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) {
828 -
    let mut a = testResolver();
829 -
    // A function call is not a constant expression.
830 -
    let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];");
831 -
    try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
953 +
    let mut testArena35 = testArena();
954 +
    let testStorage35: 'test35 = &mut testArena35 in {
955 +
        let mut a = testResolver(testStorage35);
956 +
        // A function call is not a constant expression.
957 +
        let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];");
958 +
        try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
959 +
    }
832 960
}
833 961
834 962
@test unsafe fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) {
835 -
    let mut a = testResolver();
836 -
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];");
837 -
    let err = try expectError(&result);
838 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
839 -
        else throw testing::TestError::Failed;
963 +
    let mut testArena36 = testArena();
964 +
    let testStorage36: 'test36 = &mut testArena36 in {
965 +
        let mut a = testResolver(testStorage36);
966 +
        let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];");
967 +
        let err = try expectError(&result);
968 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
969 +
            else throw testing::TestError::Failed;
970 +
    }
840 971
}
841 972
842 973
@test unsafe fn testResolveArrayIndex() throws (testing::TestError) {
843 -
    let mut a = testResolver();
844 -
    let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];";
845 -
    let result = try resolveProgramStr(&mut a, program);
846 -
    try expectNoErrors(&result);
974 +
    let mut testArena37 = testArena();
975 +
    let testStorage37: 'test37 = &mut testArena37 in {
976 +
        let mut a = testResolver(testStorage37);
977 +
        let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];";
978 +
        let result = try resolveProgramStr(&mut a, program);
979 +
        try expectNoErrors(&result);
847 980
848 -
    let stmt = try getBlockStmt(result.root, 1);
849 -
    try expectExprStmtType(&a, stmt, super::Type::I32);
981 +
        let stmt = try getBlockStmt(result.root, 1);
982 +
        try expectExprStmtType(&a, stmt, super::Type::I32);
983 +
    }
850 984
}
851 985
852 986
@test unsafe fn testResolveSliceIndex() throws (testing::TestError) {
853 -
    let mut a = testResolver();
854 -
    let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];";
855 -
    let result = try resolveProgramStr(&mut a, program);
856 -
    try expectNoErrors(&result);
987 +
    let mut testArena38 = testArena();
988 +
    let testStorage38: 'test38 = &mut testArena38 in {
989 +
        let mut a = testResolver(testStorage38);
990 +
        let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];";
991 +
        let result = try resolveProgramStr(&mut a, program);
992 +
        try expectNoErrors(&result);
857 993
858 -
    let sliceStmt = try getBlockStmt(result.root, 1);
859 -
    let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value
860 -
        else throw testing::TestError::Failed;
861 -
    let sliceTy = try typeOf(&a, sliceDecl.value);
862 -
    let elemTy = try expectSliceType(sliceTy, false);
863 -
    try testing::expect(elemTy == super::Type::I32);
994 +
        let sliceStmt = try getBlockStmt(result.root, 1);
995 +
        let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value
996 +
            else throw testing::TestError::Failed;
997 +
        let sliceTy = try typeOf(&a, sliceDecl.value);
998 +
        let elemTy = try expectSliceType(sliceTy, false);
999 +
        try testing::expect(elemTy == super::Type::I32);
864 1000
865 -
    let indexStmt = try getBlockStmt(result.root, 2);
866 -
    try expectExprStmtType(&a, indexStmt, super::Type::I32);
1001 +
        let indexStmt = try getBlockStmt(result.root, 2);
1002 +
        try expectExprStmtType(&a, indexStmt, super::Type::I32);
1003 +
    }
867 1004
}
868 1005
869 1006
@test unsafe fn testResolveSliceFields() throws (testing::TestError) {
870 -
    let mut a = testResolver();
871 -
    let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; unsafe { slice.ptr; }";
872 -
    let result = try resolveProgramStr(&mut a, program);
873 -
    try expectNoErrors(&result);
1007 +
    let mut testArena39 = testArena();
1008 +
    let testStorage39: 'test39 = &mut testArena39 in {
1009 +
        let mut a = testResolver(testStorage39);
1010 +
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; unsafe { slice.ptr; }";
1011 +
        let result = try resolveProgramStr(&mut a, program);
1012 +
        try expectNoErrors(&result);
874 1013
875 -
    let lenStmt = try getBlockStmt(result.root, 2);
876 -
    let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value
877 -
        else throw testing::TestError::Failed;
878 -
    let lenTy = try typeOf(&a, lenExpr);
879 -
    try testing::expect(lenTy == super::Type::U32);
1014 +
        let lenStmt = try getBlockStmt(result.root, 2);
1015 +
        let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value
1016 +
            else throw testing::TestError::Failed;
1017 +
        let lenTy = try typeOf(&a, lenExpr);
1018 +
        try testing::expect(lenTy == super::Type::U32);
880 1019
881 -
    let ptrStmt = try getBlockStmt(result.root, 3);
882 -
    let case ast::NodeValue::Block(ptrBlock) = ptrStmt.value
883 -
        else throw testing::TestError::Failed;
884 -
    let case ast::NodeValue::ExprStmt(ptrExpr) = ptrBlock.statements[0].value
885 -
        else throw testing::TestError::Failed;
886 -
    let ptrTy = try typeOf(&a, ptrExpr);
887 -
    let targetTy = try expectPointerType(ptrTy, false);
888 -
    try testing::expect(targetTy == super::Type::I32);
1020 +
        let ptrStmt = try getBlockStmt(result.root, 3);
1021 +
        let case ast::NodeValue::Block(ptrBlock) = ptrStmt.value
1022 +
            else throw testing::TestError::Failed;
1023 +
        let case ast::NodeValue::ExprStmt(ptrExpr) = ptrBlock.statements[0].value
1024 +
            else throw testing::TestError::Failed;
1025 +
        let ptrTy = try typeOf(&a, ptrExpr);
1026 +
        let targetTy = try expectPointerType(ptrTy, false);
1027 +
        try testing::expect(targetTy == super::Type::I32);
1028 +
    }
889 1029
}
890 1030
891 1031
@test unsafe fn testResolveSliceLiteralImmutable() throws (testing::TestError) {
892 -
    let mut a = testResolver();
893 -
    let program = "let slice: *[i32] = &[1, 2, 3];";
894 -
    let result = try resolveProgramStr(&mut a, program);
895 -
    try expectNoErrors(&result);
1032 +
    let mut testArena40 = testArena();
1033 +
    let testStorage40: 'test40 = &mut testArena40 in {
1034 +
        let mut a = testResolver(testStorage40);
1035 +
        let program = "let slice: *[i32] = &[1, 2, 3];";
1036 +
        let result = try resolveProgramStr(&mut a, program);
1037 +
        try expectNoErrors(&result);
1038 +
    }
896 1039
}
897 1040
898 1041
/// Empty array literal infers element type from slice annotation.
899 1042
@test unsafe fn testResolveSliceLiteralEmpty() throws (testing::TestError) {
900 -
    let mut a = testResolver();
901 -
    let program = "let slice: *[i32] = &[];";
902 -
    let result = try resolveProgramStr(&mut a, program);
903 -
    try expectNoErrors(&result);
1043 +
    let mut testArena41 = testArena();
1044 +
    let testStorage41: 'test41 = &mut testArena41 in {
1045 +
        let mut a = testResolver(testStorage41);
1046 +
        let program = "let slice: *[i32] = &[];";
1047 +
        let result = try resolveProgramStr(&mut a, program);
1048 +
        try expectNoErrors(&result);
1049 +
    }
904 1050
}
905 1051
906 1052
/// Nested array literal should infer inner element type from slice annotation.
907 1053
@test unsafe fn testResolveSliceLiteralNestedArray() throws (testing::TestError) {
908 -
    let mut a = testResolver();
909 -
    let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];";
910 -
    let result = try resolveProgramStr(&mut a, program);
911 -
    try expectNoErrors(&result);
1054 +
    let mut testArena42 = testArena();
1055 +
    let testStorage42: 'test42 = &mut testArena42 in {
1056 +
        let mut a = testResolver(testStorage42);
1057 +
        let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];";
1058 +
        let result = try resolveProgramStr(&mut a, program);
1059 +
        try expectNoErrors(&result);
1060 +
    }
912 1061
}
913 1062
914 1063
@test unsafe fn testResolveSliceFromArray() throws (testing::TestError) {
915 1064
    {
916 -
        let mut a = testResolver();
917 -
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];";
918 -
        let result = try resolveProgramStr(&mut a, program);
919 -
        try expectNoErrors(&result);
1065 +
        let mut testArena43 = testArena();
1066 +
        let testStorage43: 'test43 = &mut testArena43 in {
1067 +
            let mut a = testResolver(testStorage43);
1068 +
            let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];";
1069 +
            let result = try resolveProgramStr(&mut a, program);
1070 +
            try expectNoErrors(&result);
1071 +
        }
920 1072
    } {
921 -
        let mut a = testResolver();
922 -
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];";
923 -
        let result = try resolveProgramStr(&mut a, program);
924 -
        try expectNoErrors(&result);
1073 +
        let mut testArena44 = testArena();
1074 +
        let testStorage44: 'test44 = &mut testArena44 in {
1075 +
            let mut a = testResolver(testStorage44);
1076 +
            let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];";
1077 +
            let result = try resolveProgramStr(&mut a, program);
1078 +
            try expectNoErrors(&result);
1079 +
        }
925 1080
    } {
926 -
        let mut a = testResolver();
927 -
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];";
928 -
        let result = try resolveProgramStr(&mut a, program);
929 -
        try expectNoErrors(&result);
1081 +
        let mut testArena45 = testArena();
1082 +
        let testStorage45: 'test45 = &mut testArena45 in {
1083 +
            let mut a = testResolver(testStorage45);
1084 +
            let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];";
1085 +
            let result = try resolveProgramStr(&mut a, program);
1086 +
            try expectNoErrors(&result);
1087 +
        }
930 1088
    } {
931 -
        let mut a = testResolver();
932 -
        let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];";
933 -
        let result = try resolveProgramStr(&mut a, program);
934 -
        try expectNoErrors(&result);
1089 +
        let mut testArena46 = testArena();
1090 +
        let testStorage46: 'test46 = &mut testArena46 in {
1091 +
            let mut a = testResolver(testStorage46);
1092 +
            let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];";
1093 +
            let result = try resolveProgramStr(&mut a, program);
1094 +
            try expectNoErrors(&result);
1095 +
        }
935 1096
    }
936 1097
}
937 1098
938 1099
@test unsafe fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) {
939 -
    let mut a = testResolver();
940 -
    let program = "let slice: *mut [i32] = &[1, 2, 3];";
941 -
    let result = try resolveProgramStr(&mut a, program);
942 -
    let err = try expectError(&result);
943 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
944 -
        else throw testing::TestError::Failed;
1100 +
    let mut testArena47 = testArena();
1101 +
    let testStorage47: 'test47 = &mut testArena47 in {
1102 +
        let mut a = testResolver(testStorage47);
1103 +
        let program = "let slice: *mut [i32] = &[1, 2, 3];";
1104 +
        let result = try resolveProgramStr(&mut a, program);
1105 +
        let err = try expectError(&result);
1106 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
1107 +
            else throw testing::TestError::Failed;
1108 +
    }
945 1109
}
946 1110
947 1111
@test unsafe fn testResolveSliceLiteralMutable() throws (testing::TestError) {
948 -
    let mut a = testResolver();
949 -
    let program = "let slice: *mut [i32] = &mut [1, 2, 3];";
950 -
    let result = try resolveProgramStr(&mut a, program);
951 -
    try expectNoErrors(&result);
1112 +
    let mut testArena48 = testArena();
1113 +
    let testStorage48: 'test48 = &mut testArena48 in {
1114 +
        let mut a = testResolver(testStorage48);
1115 +
        let program = "let slice: *mut [i32] = &mut [1, 2, 3];";
1116 +
        let result = try resolveProgramStr(&mut a, program);
1117 +
        try expectNoErrors(&result);
1118 +
    }
952 1119
}
953 1120
954 1121
@test unsafe fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) {
955 -
    let mut a = testResolver();
956 -
    let program = "let x: i32 = 0; let ptr: *mut i32 = &x;";
957 -
    let result = try resolveProgramStr(&mut a, program);
958 -
    let err = try expectError(&result);
959 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
960 -
        else throw testing::TestError::Failed;
1122 +
    let mut testArena49 = testArena();
1123 +
    let testStorage49: 'test49 = &mut testArena49 in {
1124 +
        let mut a = testResolver(testStorage49);
1125 +
        let program = "let x: i32 = 0; let ptr: *mut i32 = &x;";
1126 +
        let result = try resolveProgramStr(&mut a, program);
1127 +
        let err = try expectError(&result);
1128 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
1129 +
            else throw testing::TestError::Failed;
1130 +
    }
961 1131
}
962 1132
963 1133
@test unsafe fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) {
964 -
    let mut a = testResolver();
965 -
    let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;";
966 -
    let result = try resolveProgramStr(&mut a, program);
967 -
    try expectNoErrors(&result);
1134 +
    let mut testArena50 = testArena();
1135 +
    let testStorage50: 'test50 = &mut testArena50 in {
1136 +
        let mut a = testResolver(testStorage50);
1137 +
        let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;";
1138 +
        let result = try resolveProgramStr(&mut a, program);
1139 +
        try expectNoErrors(&result);
1140 +
    }
968 1141
}
969 1142
970 1143
@test unsafe fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) {
971 1144
    {
972 -
        let mut a = testResolver();
973 -
        let program = "let x: i32 = 0; let ptr = &mut x;";
974 -
        let result = try resolveProgramStr(&mut a, program);
975 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1145 +
        let mut testArena51 = testArena();
1146 +
        let testStorage51: 'test51 = &mut testArena51 in {
1147 +
            let mut a = testResolver(testStorage51);
1148 +
            let program = "let x: i32 = 0; let ptr = &mut x;";
1149 +
            let result = try resolveProgramStr(&mut a, program);
1150 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1151 +
        }
976 1152
    } {
977 -
        let mut a = testResolver();
978 -
        let program = "let mut x: i32 = 0; &mut x;";
979 -
        let result = try resolveProgramStr(&mut a, program);
980 -
        try expectNoErrors(&result);
1153 +
        let mut testArena52 = testArena();
1154 +
        let testStorage52: 'test52 = &mut testArena52 in {
1155 +
            let mut a = testResolver(testStorage52);
1156 +
            let program = "let mut x: i32 = 0; &mut x;";
1157 +
            let result = try resolveProgramStr(&mut a, program);
1158 +
            try expectNoErrors(&result);
1159 +
        }
981 1160
    }
982 1161
}
983 1162
984 1163
@test unsafe fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) {
985 1164
    {
986 -
        let mut a = testResolver();
987 -
        let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];";
988 -
        let result = try resolveProgramStr(&mut a, program);
989 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1165 +
        let mut testArena53 = testArena();
1166 +
        let testStorage53: 'test53 = &mut testArena53 in {
1167 +
            let mut a = testResolver(testStorage53);
1168 +
            let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];";
1169 +
            let result = try resolveProgramStr(&mut a, program);
1170 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1171 +
        }
990 1172
    } {
991 -
        let mut a = testResolver();
992 -
        let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];";
993 -
        let result = try resolveProgramStr(&mut a, program);
994 -
        try expectNoErrors(&result);
1173 +
        let mut testArena54 = testArena();
1174 +
        let testStorage54: 'test54 = &mut testArena54 in {
1175 +
            let mut a = testResolver(testStorage54);
1176 +
            let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];";
1177 +
            let result = try resolveProgramStr(&mut a, program);
1178 +
            try expectNoErrors(&result);
1179 +
        }
995 1180
    }
996 1181
}
997 1182
998 1183
@test unsafe fn testResolveSliceCannotAssignToArray() throws (testing::TestError) {
999 -
    let mut a = testResolver();
1000 -
    let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;";
1001 -
    let result = try resolveProgramStr(&mut a, program);
1002 -
    let err = try expectError(&result);
1003 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1004 -
        else throw testing::TestError::Failed;
1184 +
    let mut testArena55 = testArena();
1185 +
    let testStorage55: 'test55 = &mut testArena55 in {
1186 +
        let mut a = testResolver(testStorage55);
1187 +
        let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;";
1188 +
        let result = try resolveProgramStr(&mut a, program);
1189 +
        let err = try expectError(&result);
1190 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
1191 +
            else throw testing::TestError::Failed;
1192 +
    }
1005 1193
}
1006 1194
1007 1195
@test unsafe fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) {
1008 -
    let mut a = testResolver();
1009 -
    let program = "let xs: [u8; 2] = [1, 2]; xs[..];";
1010 -
    let result = try resolveProgramStr(&mut a, program);
1011 -
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1196 +
    let mut testArena56 = testArena();
1197 +
    let testStorage56: 'test56 = &mut testArena56 in {
1198 +
        let mut a = testResolver(testStorage56);
1199 +
        let program = "let xs: [u8; 2] = [1, 2]; xs[..];";
1200 +
        let result = try resolveProgramStr(&mut a, program);
1201 +
        try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1202 +
    }
1012 1203
}
1013 1204
1014 1205
@test unsafe fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) {
1015 -
    let mut a = testResolver();
1016 -
    let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }";
1017 -
    let result = try resolveProgramStr(&mut a, program);
1018 -
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1206 +
    let mut testArena57 = testArena();
1207 +
    let testStorage57: 'test57 = &mut testArena57 in {
1208 +
        let mut a = testResolver(testStorage57);
1209 +
        let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }";
1210 +
        let result = try resolveProgramStr(&mut a, program);
1211 +
        try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1212 +
    }
1019 1213
}
1020 1214
1021 1215
@test unsafe fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) {
1022 1216
    {
1023 -
        let mut a = testResolver();
1024 -
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];";
1025 -
        let result = try resolveProgramStr(&mut a, program);
1026 -
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1217 +
        let mut testArena58 = testArena();
1218 +
        let testStorage58: 'test58 = &mut testArena58 in {
1219 +
            let mut a = testResolver(testStorage58);
1220 +
            let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];";
1221 +
            let result = try resolveProgramStr(&mut a, program);
1222 +
            try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1223 +
        }
1027 1224
    } {
1028 -
        let mut a = testResolver();
1029 -
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];";
1030 -
        let result = try resolveProgramStr(&mut a, program);
1031 -
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1225 +
        let mut testArena59 = testArena();
1226 +
        let testStorage59: 'test59 = &mut testArena59 in {
1227 +
            let mut a = testResolver(testStorage59);
1228 +
            let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];";
1229 +
            let result = try resolveProgramStr(&mut a, program);
1230 +
            try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1231 +
        }
1032 1232
    } {
1033 -
        let mut a = testResolver();
1034 -
        let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];";
1035 -
        let result = try resolveProgramStr(&mut a, program);
1036 -
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1233 +
        let mut testArena60 = testArena();
1234 +
        let testStorage60: 'test60 = &mut testArena60 in {
1235 +
            let mut a = testResolver(testStorage60);
1236 +
            let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];";
1237 +
            let result = try resolveProgramStr(&mut a, program);
1238 +
            try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1239 +
        }
1037 1240
    }
1038 1241
}
1039 1242
1040 1243
@test unsafe fn testResolveArrayLenConstValue() throws (testing::TestError) {
1041 -
    let mut a = testResolver();
1042 -
    let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;";
1043 -
    let result = try resolveBlockStr(&mut a, program);
1044 -
    try expectNoErrors(&result);
1244 +
    let mut testArena61 = testArena();
1245 +
    let testStorage61: 'test61 = &mut testArena61 in {
1246 +
        let mut a = testResolver(testStorage61);
1247 +
        let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;";
1248 +
        let result = try resolveBlockStr(&mut a, program);
1249 +
        try expectNoErrors(&result);
1045 1250
1046 -
    let constStmt = try getBlockStmt(result.root, 1);
1047 -
    let case ast::NodeValue::ConstDecl(decl) = constStmt.value
1048 -
        else throw testing::TestError::Failed;
1049 -
    let valueConst = super::constValueEntry(&a, decl.value)
1050 -
        else throw testing::TestError::Failed;
1051 -
    let case super::ConstValue::Int(lenVal) = valueConst
1052 -
        else throw testing::TestError::Failed;
1053 -
    try testing::expect(lenVal.magnitude == 3);
1054 -
    try testing::expect(not lenVal.negative);
1251 +
        let constStmt = try getBlockStmt(result.root, 1);
1252 +
        let case ast::NodeValue::ConstDecl(decl) = constStmt.value
1253 +
            else throw testing::TestError::Failed;
1254 +
        let valueConst = super::constValueEntry(&a, decl.value)
1255 +
            else throw testing::TestError::Failed;
1256 +
        let case super::ConstValue::Int(lenVal) = valueConst
1257 +
            else throw testing::TestError::Failed;
1258 +
        try testing::expect(lenVal.magnitude == 3);
1259 +
        try testing::expect(not lenVal.negative);
1260 +
    }
1055 1261
}
1056 1262
1057 1263
@test unsafe fn testResolveIndexNonIndexable() throws (testing::TestError) {
1058 -
    let mut a = testResolver();
1059 -
    let program = "let flag: bool = true; flag[0];";
1060 -
    let result = try resolveProgramStr(&mut a, program);
1061 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable);
1264 +
    let mut testArena62 = testArena();
1265 +
    let testStorage62: 'test62 = &mut testArena62 in {
1266 +
        let mut a = testResolver(testStorage62);
1267 +
        let program = "let flag: bool = true; flag[0];";
1268 +
        let result = try resolveProgramStr(&mut a, program);
1269 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable);
1270 +
    }
1062 1271
}
1063 1272
1064 1273
@test unsafe fn testResolveSliceFieldUnknown() throws (testing::TestError) {
1065 -
    let mut a = testResolver();
1066 -
    let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;";
1067 -
    let result = try resolveProgramStr(&mut a, program);
1068 -
    try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown"));
1274 +
    let mut testArena63 = testArena();
1275 +
    let testStorage63: 'test63 = &mut testArena63 in {
1276 +
        let mut a = testResolver(testStorage63);
1277 +
        let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;";
1278 +
        let result = try resolveProgramStr(&mut a, program);
1279 +
        try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown"));
1280 +
    }
1069 1281
}
1070 1282
1071 1283
@test unsafe fn testResolveArrayFieldUnknown() throws (testing::TestError) {
1072 -
    let mut a = testResolver();
1073 -
    let program = "let xs: [i32; 2] = [1, 2]; xs.field;";
1074 -
    let result = try resolveProgramStr(&mut a, program);
1075 -
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
1284 +
    let mut testArena64 = testArena();
1285 +
    let testStorage64: 'test64 = &mut testArena64 in {
1286 +
        let mut a = testResolver(testStorage64);
1287 +
        let program = "let xs: [i32; 2] = [1, 2]; xs.field;";
1288 +
        let result = try resolveProgramStr(&mut a, program);
1289 +
        try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
1290 +
    }
1076 1291
}
1077 1292
1078 1293
@test unsafe fn testResolveIfConditionRequiresBool() throws (testing::TestError) {
1079 1294
    {
1080 -
        let mut a = testResolver();
1081 -
        let result = try resolveProgramStr(&mut a, "if 42 {}");
1082 -
        let err = try expectError(&result);
1083 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1295 +
        let mut testArena65 = testArena();
1296 +
        let testStorage65: 'test65 = &mut testArena65 in {
1297 +
            let mut a = testResolver(testStorage65);
1298 +
            let result = try resolveProgramStr(&mut a, "if 42 {}");
1299 +
            let err = try expectError(&result);
1300 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1301 +
        }
1084 1302
    } {
1085 -
        let mut a = testResolver();
1086 -
        let result = try resolveProgramStr(&mut a, "if true {}");
1087 -
        try expectNoErrors(&result);
1303 +
        let mut testArena66 = testArena();
1304 +
        let testStorage66: 'test66 = &mut testArena66 in {
1305 +
            let mut a = testResolver(testStorage66);
1306 +
            let result = try resolveProgramStr(&mut a, "if true {}");
1307 +
            try expectNoErrors(&result);
1308 +
        }
1088 1309
    }
1089 1310
}
1090 1311
1091 1312
@test unsafe fn testResolveIfLetScopeBinding() throws (testing::TestError) {
1092 -
    let mut a = testResolver();
1093 -
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }");
1094 -
    try expectNoErrors(&result);
1313 +
    let mut testArena67 = testArena();
1314 +
    let testStorage67: 'test67 = &mut testArena67 in {
1315 +
        let mut a = testResolver(testStorage67);
1316 +
        let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }");
1317 +
        try expectNoErrors(&result);
1095 1318
1096 -
    // Get the if-let statement and verify `x` has type `i32`.
1097 -
    let ifLetStmt = try parser::tests::getBlockLastStmt(result.root);
1098 -
    let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value
1099 -
        else throw testing::TestError::Failed;
1319 +
        // Get the if-let statement and verify `x` has type `i32`.
1320 +
        let ifLetStmt = try parser::tests::getBlockLastStmt(result.root);
1321 +
        let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value
1322 +
            else throw testing::TestError::Failed;
1100 1323
1101 -
    let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch);
1102 -
    let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value
1103 -
        else throw testing::TestError::Failed;
1324 +
        let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch);
1325 +
        let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value
1326 +
            else throw testing::TestError::Failed;
1104 1327
1105 -
    try expectType(&a, xExpr, super::Type::I32);
1328 +
        try expectType(&a, xExpr, super::Type::I32);
1106 1329
1107 -
    let scope = super::scopeFor(&a, ifLetStmt)
1108 -
        else throw testing::TestError::Failed;
1109 -
    let xSym = super::findSymbolInScope(scope, "x")
1110 -
        else throw testing::TestError::Failed;
1111 -
    let case super::SymbolData::Value { type: valType, .. } = xSym.data
1112 -
        else throw testing::TestError::Failed;
1330 +
        let scope = super::scopeFor(&a, ifLetStmt)
1331 +
            else throw testing::TestError::Failed;
1332 +
        let xSym = super::findSymbolInScope(scope, "x")
1333 +
            else throw testing::TestError::Failed;
1334 +
        let case super::SymbolData::Value { type: valType, .. } = xSym.data
1335 +
            else throw testing::TestError::Failed;
1113 1336
1114 -
    try testing::expect(valType == super::Type::I32);
1337 +
        try testing::expect(valType == super::Type::I32);
1338 +
    }
1115 1339
}
1116 1340
1117 1341
@test unsafe fn testResolveIfLetScopeBindingError() throws (testing::TestError) {
1118 -
    let mut a = testResolver();
1119 -
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }");
1120 -
    let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1121 -
1122 -
    // Verify the error comes from the else branch (offset 48).
1123 -
    let errNode = err.node
1124 -
        else throw testing::TestError::Failed;
1125 -
    try testing::expect(errNode.span.offset == 48);
1342 +
    let mut testArena68 = testArena();
1343 +
    let testStorage68: 'test68 = &mut testArena68 in {
1344 +
        let mut a = testResolver(testStorage68);
1345 +
        let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }");
1346 +
        let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1347 +
1348 +
        // Verify the error comes from the else branch (offset 48).
1349 +
        let errNode = err.node
1350 +
            else throw testing::TestError::Failed;
1351 +
        try testing::expect(errNode.span.offset == 48);
1352 +
    }
1126 1353
}
1127 1354
1128 1355
/// Tests that `if let` with a condition expression binds the variable in scope.
1129 1356
@test unsafe fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) {
1130 -
    let mut a = testResolver();
1131 -
    let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }";
1132 -
    let result = try resolveProgramStr(&mut a, program);
1133 -
    try expectNoErrors(&result);
1357 +
    let mut testArena69 = testArena();
1358 +
    let testStorage69: 'test69 = &mut testArena69 in {
1359 +
        let mut a = testResolver(testStorage69);
1360 +
        let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }";
1361 +
        let result = try resolveProgramStr(&mut a, program);
1362 +
        try expectNoErrors(&result);
1363 +
    }
1134 1364
}
1135 1365
1136 1366
@test unsafe fn testResolveWhileConditionRequiresBool() throws (testing::TestError) {
1137 1367
    {
1138 -
        let mut a = testResolver();
1139 -
        let result = try resolveProgramStr(&mut a, "while 1 {}");
1140 -
        let err = try expectError(&result);
1141 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1368 +
        let mut testArena70 = testArena();
1369 +
        let testStorage70: 'test70 = &mut testArena70 in {
1370 +
            let mut a = testResolver(testStorage70);
1371 +
            let result = try resolveProgramStr(&mut a, "while 1 {}");
1372 +
            let err = try expectError(&result);
1373 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1374 +
        }
1142 1375
    } {
1143 -
        let mut a = testResolver();
1144 -
        let result = try resolveProgramStr(&mut a, "while true {}");
1145 -
        try expectNoErrors(&result);
1376 +
        let mut testArena71 = testArena();
1377 +
        let testStorage71: 'test71 = &mut testArena71 in {
1378 +
            let mut a = testResolver(testStorage71);
1379 +
            let result = try resolveProgramStr(&mut a, "while true {}");
1380 +
            try expectNoErrors(&result);
1381 +
        }
1146 1382
    }
1147 1383
}
1148 1384
1149 1385
@test unsafe fn testResolveWhileLetBindingScope() throws (testing::TestError) {
1150 1386
    {
1151 -
        let mut a = testResolver();
1152 -
        let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }";
1387 +
        let mut testArena72 = testArena();
1388 +
        let testStorage72: 'test72 = &mut testArena72 in {
1389 +
            let mut a = testResolver(testStorage72);
1390 +
            let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }";
1391 +
            let result = try resolveProgramStr(&mut a, program);
1392 +
            try expectNoErrors(&result);
1393 +
1394 +
            let whileStmt = try parser::tests::getBlockLastStmt(result.root);
1395 +
            let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value
1396 +
                else throw testing::TestError::Failed;
1397 +
1398 +
            let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body);
1399 +
            try expectExprStmtType(&a, bodyStmt, super::Type::I32);
1400 +
1401 +
            let scope = super::scopeFor(&a, whileStmt)
1402 +
                else throw testing::TestError::Failed;
1403 +
            let xSym = super::findSymbolInScope(scope, "x")
1404 +
                else throw testing::TestError::Failed;
1405 +
            let case super::SymbolData::Value { type: valType, .. } = xSym.data
1406 +
                else throw testing::TestError::Failed;
1407 +
            try testing::expect(valType == super::Type::I32);
1408 +
        }
1409 +
    } {
1410 +
        let mut testArena73 = testArena();
1411 +
        let testStorage73: 'test73 = &mut testArena73 in {
1412 +
            let mut a = testResolver(testStorage73);
1413 +
            let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }";
1414 +
            let result = try resolveProgramStr(&mut a, program);
1415 +
            try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1416 +
        }
1417 +
    }
1418 +
}
1419 +
1420 +
@test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) {
1421 +
    let mut testArena74 = testArena();
1422 +
    let testStorage74: 'test74 = &mut testArena74 in {
1423 +
        let mut a = testResolver(testStorage74);
1424 +
        let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }";
1153 1425
        let result = try resolveProgramStr(&mut a, program);
1154 1426
        try expectNoErrors(&result);
1155 1427
1156 -
        let whileStmt = try parser::tests::getBlockLastStmt(result.root);
1157 -
        let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value
1428 +
        let forStmt = try parser::tests::getBlockLastStmt(result.root);
1429 +
        let case ast::NodeValue::For(loopNode) = forStmt.value
1158 1430
            else throw testing::TestError::Failed;
1159 1431
1160 -
        let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body);
1161 -
        try expectExprStmtType(&a, bodyStmt, super::Type::I32);
1162 -
1163 -
        let scope = super::scopeFor(&a, whileStmt)
1432 +
        let scope = super::scopeFor(&a, forStmt)
1164 1433
            else throw testing::TestError::Failed;
1165 -
        let xSym = super::findSymbolInScope(scope, "x")
1434 +
        let sym = super::findSymbolInScope(scope, "x")
1166 1435
            else throw testing::TestError::Failed;
1167 -
        let case super::SymbolData::Value { type: valType, .. } = xSym.data
1436 +
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1168 1437
            else throw testing::TestError::Failed;
1169 1438
        try testing::expect(valType == super::Type::I32);
1170 -
    } {
1171 -
        let mut a = testResolver();
1172 -
        let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }";
1173 -
        let result = try resolveProgramStr(&mut a, program);
1174 -
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1175 -
    }
1176 -
}
1177 -
1178 -
@test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) {
1179 -
    let mut a = testResolver();
1180 -
    let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }";
1181 -
    let result = try resolveProgramStr(&mut a, program);
1182 -
    try expectNoErrors(&result);
1183 -
1184 -
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1185 -
    let case ast::NodeValue::For(loopNode) = forStmt.value
1186 -
        else throw testing::TestError::Failed;
1187 1439
1188 -
    let scope = super::scopeFor(&a, forStmt)
1189 -
        else throw testing::TestError::Failed;
1190 -
    let sym = super::findSymbolInScope(scope, "x")
1191 -
        else throw testing::TestError::Failed;
1192 -
    let case super::SymbolData::Value { type: valType, .. } = sym.data
1193 -
        else throw testing::TestError::Failed;
1194 -
    try testing::expect(valType == super::Type::I32);
1195 -
1196 -
    let bindingTy = super::typeFor(&a, loopNode.binding)
1197 -
        else throw testing::TestError::Failed;
1198 -
    try testing::expect(bindingTy == super::Type::I32);
1440 +
        let bindingTy = super::typeFor(&a, loopNode.binding)
1441 +
            else throw testing::TestError::Failed;
1442 +
        try testing::expect(bindingTy == super::Type::I32);
1443 +
    }
1199 1444
}
1200 1445
1201 1446
@test unsafe fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) {
1202 -
    let mut a = testResolver();
1203 -
    let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }";
1204 -
    let result = try resolveProgramStr(&mut a, program);
1205 -
    try expectNoErrors(&result);
1447 +
    let mut testArena75 = testArena();
1448 +
    let testStorage75: 'test75 = &mut testArena75 in {
1449 +
        let mut a = testResolver(testStorage75);
1450 +
        let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }";
1451 +
        let result = try resolveProgramStr(&mut a, program);
1452 +
        try expectNoErrors(&result);
1206 1453
1207 -
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1208 -
    let case ast::NodeValue::For(loopNode) = forStmt.value
1209 -
        else throw testing::TestError::Failed;
1454 +
        let forStmt = try parser::tests::getBlockLastStmt(result.root);
1455 +
        let case ast::NodeValue::For(loopNode) = forStmt.value
1456 +
            else throw testing::TestError::Failed;
1210 1457
1211 -
    let scope = super::scopeFor(&a, forStmt)
1212 -
        else throw testing::TestError::Failed;
1213 -
    let valueSym = super::findSymbolInScope(scope, "value")
1214 -
        else throw testing::TestError::Failed;
1215 -
    let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data
1216 -
        else throw testing::TestError::Failed;
1217 -
    try testing::expect(valueValType == super::Type::Bool);
1218 -
    let indexSym = super::findSymbolInScope(scope, "idx")
1219 -
        else throw testing::TestError::Failed;
1220 -
    let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data
1221 -
        else throw testing::TestError::Failed;
1222 -
    try testing::expect(indexValType == super::Type::U32);
1458 +
        let scope = super::scopeFor(&a, forStmt)
1459 +
            else throw testing::TestError::Failed;
1460 +
        let valueSym = super::findSymbolInScope(scope, "value")
1461 +
            else throw testing::TestError::Failed;
1462 +
        let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data
1463 +
            else throw testing::TestError::Failed;
1464 +
        try testing::expect(valueValType == super::Type::Bool);
1465 +
        let indexSym = super::findSymbolInScope(scope, "idx")
1466 +
            else throw testing::TestError::Failed;
1467 +
        let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data
1468 +
            else throw testing::TestError::Failed;
1469 +
        try testing::expect(indexValType == super::Type::U32);
1223 1470
1224 -
    let indexNode = loopNode.index
1225 -
        else throw testing::TestError::Failed;
1226 -
    let indexTy = super::typeFor(&a, indexNode)
1227 -
        else throw testing::TestError::Failed;
1228 -
    try testing::expect(indexTy == super::Type::U32);
1471 +
        let indexNode = loopNode.index
1472 +
            else throw testing::TestError::Failed;
1473 +
        let indexTy = super::typeFor(&a, indexNode)
1474 +
            else throw testing::TestError::Failed;
1475 +
        try testing::expect(indexTy == super::Type::U32);
1476 +
    }
1229 1477
}
1230 1478
1231 1479
@test unsafe fn testResolveForSliceIterable() throws (testing::TestError) {
1232 -
    let mut a = testResolver();
1233 -
    let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }";
1234 -
    let result = try resolveProgramStr(&mut a, program);
1235 -
    try expectNoErrors(&result);
1480 +
    let mut testArena76 = testArena();
1481 +
    let testStorage76: 'test76 = &mut testArena76 in {
1482 +
        let mut a = testResolver(testStorage76);
1483 +
        let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }";
1484 +
        let result = try resolveProgramStr(&mut a, program);
1485 +
        try expectNoErrors(&result);
1236 1486
1237 -
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1238 -
    let case ast::NodeValue::For(loopNode) = forStmt.value
1239 -
        else throw testing::TestError::Failed;
1487 +
        let forStmt = try parser::tests::getBlockLastStmt(result.root);
1488 +
        let case ast::NodeValue::For(loopNode) = forStmt.value
1489 +
            else throw testing::TestError::Failed;
1240 1490
1241 -
    let bindingTy = super::typeFor(&a, loopNode.binding)
1242 -
        else throw testing::TestError::Failed;
1243 -
    try testing::expect(bindingTy == super::Type::I32);
1491 +
        let bindingTy = super::typeFor(&a, loopNode.binding)
1492 +
            else throw testing::TestError::Failed;
1493 +
        try testing::expect(bindingTy == super::Type::I32);
1494 +
    }
1244 1495
}
1245 1496
1246 1497
@test unsafe fn testResolveForRequiresIterable() throws (testing::TestError) {
1247 -
    let mut a = testResolver();
1248 -
    let result = try resolveProgramStr(&mut a, "for x in true { x; }");
1249 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedIterable);
1498 +
    let mut testArena77 = testArena();
1499 +
    let testStorage77: 'test77 = &mut testArena77 in {
1500 +
        let mut a = testResolver(testStorage77);
1501 +
        let result = try resolveProgramStr(&mut a, "for x in true { x; }");
1502 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedIterable);
1503 +
    }
1250 1504
}
1251 1505
1252 1506
@test unsafe fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) {
1253 -
    let mut a = testResolver();
1254 -
    let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }");
1255 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
1507 +
    let mut testArena78 = testArena();
1508 +
    let testStorage78: 'test78 = &mut testArena78 in {
1509 +
        let mut a = testResolver(testStorage78);
1510 +
        let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }");
1511 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
1512 +
    }
1256 1513
}
1257 1514
1258 1515
@test unsafe fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) {
1259 -
    let mut a = testResolver();
1260 -
    let program = "let val: i32 = 0; match val { case true => {} }";
1261 -
    let result = try resolveProgramStr(&mut a, program);
1262 -
    let err = try expectError(&result);
1263 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1516 +
    let mut testArena79 = testArena();
1517 +
    let testStorage79: 'test79 = &mut testArena79 in {
1518 +
        let mut a = testResolver(testStorage79);
1519 +
        let program = "let val: i32 = 0; match val { case true => {} }";
1520 +
        let result = try resolveProgramStr(&mut a, program);
1521 +
        let err = try expectError(&result);
1522 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1523 +
    }
1264 1524
}
1265 1525
1266 1526
@test unsafe fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) {
1267 -
    let mut a = testResolver();
1268 -
    let program = "union First { A }  union Second { B } fn run(val: First) { match val { case Second::B => {} } }";
1269 -
    let result = try resolveProgramStr(&mut a, program);
1270 -
    let err = try expectError(&result);
1527 +
    let mut testArena80 = testArena();
1528 +
    let testStorage80: 'test80 = &mut testArena80 in {
1529 +
        let mut a = testResolver(testStorage80);
1530 +
        let program = "union First { A }  union Second { B } fn run(val: First) { match val { case Second::B => {} } }";
1531 +
        let result = try resolveProgramStr(&mut a, program);
1532 +
        let err = try expectError(&result);
1271 1533
1272 -
    let firstTy = try getTypeInScopeOf(&a, result.root, "First");
1273 -
    let secondTy = try getTypeInScopeOf(&a, result.root, "Second");
1274 -
    try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy));
1534 +
        let firstTy = try getTypeInScopeOf(&a, result.root, "First");
1535 +
        let secondTy = try getTypeInScopeOf(&a, result.root, "Second");
1536 +
        try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy));
1537 +
    }
1275 1538
}
1276 1539
1277 1540
@test unsafe fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) {
1278 -
    let mut a = testResolver();
1279 -
    let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1280 -
    let result = try resolveProgramStr(&mut a, program);
1281 -
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some"));
1541 +
    let mut testArena81 = testArena();
1542 +
    let testStorage81: 'test81 = &mut testArena81 in {
1543 +
        let mut a = testResolver(testStorage81);
1544 +
        let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1545 +
        let result = try resolveProgramStr(&mut a, program);
1546 +
        try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some"));
1547 +
    }
1282 1548
}
1283 1549
1284 1550
@test unsafe fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) {
1285 -
    let mut a = testResolver();
1286 -
    let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1287 -
    let result = try resolveProgramStr(&mut a, program);
1288 -
    try expectNoErrors(&result);
1551 +
    let mut testArena82 = testArena();
1552 +
    let testStorage82: 'test82 = &mut testArena82 in {
1553 +
        let mut a = testResolver(testStorage82);
1554 +
        let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1555 +
        let result = try resolveProgramStr(&mut a, program);
1556 +
        try expectNoErrors(&result);
1557 +
    }
1289 1558
}
1290 1559
1291 1560
@test unsafe fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) {
1292 -
    let mut a = testResolver();
1293 -
    let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }";
1294 -
    let result = try resolveProgramStr(&mut a, program);
1295 -
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None"));
1561 +
    let mut testArena83 = testArena();
1562 +
    let testStorage83: 'test83 = &mut testArena83 in {
1563 +
        let mut a = testResolver(testStorage83);
1564 +
        let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }";
1565 +
        let result = try resolveProgramStr(&mut a, program);
1566 +
        try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None"));
1567 +
    }
1296 1568
}
1297 1569
1298 1570
@test unsafe fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) {
1299 -
    let mut a = testResolver();
1300 -
    let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }";
1301 -
    let result = try resolveProgramStr(&mut a, program);
1302 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
1571 +
    let mut testArena84 = testArena();
1572 +
    let testStorage84: 'test84 = &mut testArena84 in {
1573 +
        let mut a = testResolver(testStorage84);
1574 +
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }";
1575 +
        let result = try resolveProgramStr(&mut a, program);
1576 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
1577 +
    }
1303 1578
}
1304 1579
1305 1580
@test unsafe fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) {
1306 1581
    {
1307 -
        let mut a = testResolver();
1308 -
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }";
1309 -
        let result = try resolveProgramStr(&mut a, program);
1310 -
        try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None"));
1582 +
        let mut testArena85 = testArena();
1583 +
        let testStorage85: 'test85 = &mut testArena85 in {
1584 +
            let mut a = testResolver(testStorage85);
1585 +
            let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }";
1586 +
            let result = try resolveProgramStr(&mut a, program);
1587 +
            try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None"));
1588 +
        }
1311 1589
    } {
1312 -
        let mut a = testResolver();
1313 -
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }";
1314 -
        let result = try resolveProgramStr(&mut a, program);
1315 -
        try expectNoErrors(&result);
1590 +
        let mut testArena86 = testArena();
1591 +
        let testStorage86: 'test86 = &mut testArena86 in {
1592 +
            let mut a = testResolver(testStorage86);
1593 +
            let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }";
1594 +
            let result = try resolveProgramStr(&mut a, program);
1595 +
            try expectNoErrors(&result);
1596 +
        }
1316 1597
    }
1317 1598
}
1318 1599
1319 1600
@test unsafe fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) {
1320 -
    let mut a = testResolver();
1321 -
    let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }";
1322 -
    let result = try resolveProgramStr(&mut a, program);
1323 -
    try expectNoErrors(&result);
1601 +
    let mut testArena87 = testArena();
1602 +
    let testStorage87: 'test87 = &mut testArena87 in {
1603 +
        let mut a = testResolver(testStorage87);
1604 +
        let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }";
1605 +
        let result = try resolveProgramStr(&mut a, program);
1606 +
        try expectNoErrors(&result);
1607 +
    }
1324 1608
}
1325 1609
1326 1610
@test unsafe fn testResolveMatchUnionBindingScope() throws (testing::TestError) {
1327 -
    let mut a = testResolver();
1328 -
    let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }";
1329 -
    let result = try resolveProgramStr(&mut a, program);
1330 -
    try expectNoErrors(&result);
1611 +
    let mut testArena88 = testArena();
1612 +
    let testStorage88: 'test88 = &mut testArena88 in {
1613 +
        let mut a = testResolver(testStorage88);
1614 +
        let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }";
1615 +
        let result = try resolveProgramStr(&mut a, program);
1616 +
        try expectNoErrors(&result);
1331 1617
1332 -
    let fnBlock = try getFnBody(&a, result.root, "f");
1333 -
    try testing::expect(fnBlock.statements.len > 0);
1618 +
        let fnBlock = try getFnBody(&a, result.root, "f");
1619 +
        try testing::expect(fnBlock.statements.len > 0);
1334 1620
1335 -
    let matchNode = fnBlock.statements[0];
1336 -
    let case ast::NodeValue::Match(sw) = matchNode.value
1337 -
        else throw testing::TestError::Failed;
1338 -
    let caseNode = sw.prongs[0];
1621 +
        let matchNode = fnBlock.statements[0];
1622 +
        let case ast::NodeValue::Match(sw) = matchNode.value
1623 +
            else throw testing::TestError::Failed;
1624 +
        let caseNode = sw.prongs[0];
1339 1625
1340 -
    let scope = super::scopeFor(&a, caseNode)
1341 -
        else throw testing::TestError::Failed;
1342 -
    let payloadSym = super::findSymbolInScope(scope, "x")
1343 -
        else throw testing::TestError::Failed;
1344 -
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
1345 -
        else throw testing::TestError::Failed;
1346 -
    try testing::expect(payloadValType == super::Type::I32);
1626 +
        let scope = super::scopeFor(&a, caseNode)
1627 +
            else throw testing::TestError::Failed;
1628 +
        let payloadSym = super::findSymbolInScope(scope, "x")
1629 +
            else throw testing::TestError::Failed;
1630 +
        let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
1631 +
            else throw testing::TestError::Failed;
1632 +
        try testing::expect(payloadValType == super::Type::I32);
1633 +
    }
1347 1634
}
1348 1635
1349 1636
@test unsafe fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) {
1350 -
    let mut a = testResolver();
1351 -
    let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }";
1352 -
    let result = try resolveProgramStr(&mut a, program);
1353 -
    let err = try expectError(&result);
1354 -
    let optionTy = try getTypeInScopeOf(&a, result.root, "Opt");
1355 -
    try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool);
1637 +
    let mut testArena89 = testArena();
1638 +
    let testStorage89: 'test89 = &mut testArena89 in {
1639 +
        let mut a = testResolver(testStorage89);
1640 +
        let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }";
1641 +
        let result = try resolveProgramStr(&mut a, program);
1642 +
        let err = try expectError(&result);
1643 +
        let optionTy = try getTypeInScopeOf(&a, result.root, "Opt");
1644 +
        try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool);
1645 +
    }
1356 1646
}
1357 1647
1358 1648
@test unsafe fn testResolveMatchGuardForms() throws (testing::TestError) {
1359 -
    let mut a = testResolver();
1360 -
    let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }";
1361 -
    let result = try resolveProgramStr(&mut a, program);
1362 -
    try expectNoErrors(&result);
1649 +
    let mut testArena90 = testArena();
1650 +
    let testStorage90: 'test90 = &mut testArena90 in {
1651 +
        let mut a = testResolver(testStorage90);
1652 +
        let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }";
1653 +
        let result = try resolveProgramStr(&mut a, program);
1654 +
        try expectNoErrors(&result);
1655 +
    }
1363 1656
}
1364 1657
1365 1658
/// Test that a binding prong binds the subject to the identifier.
1366 1659
@test unsafe fn testResolveMatchBindingProng() throws (testing::TestError) {
1367 -
    let mut a = testResolver();
1368 -
    let program = "fn f(value: i32) -> i32 { match value { x => return x } }";
1369 -
    let result = try resolveProgramStr(&mut a, program);
1370 -
    try expectNoErrors(&result);
1660 +
    let mut testArena91 = testArena();
1661 +
    let testStorage91: 'test91 = &mut testArena91 in {
1662 +
        let mut a = testResolver(testStorage91);
1663 +
        let program = "fn f(value: i32) -> i32 { match value { x => return x } }";
1664 +
        let result = try resolveProgramStr(&mut a, program);
1665 +
        try expectNoErrors(&result);
1666 +
    }
1371 1667
}
1372 1668
1373 1669
/// Test that a binding prong with guard can use the bound variable.
1374 1670
@test unsafe fn testResolveMatchBindingProngGuard() throws (testing::TestError) {
1375 -
    let mut a = testResolver();
1376 -
    let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }";
1377 -
    let result = try resolveProgramStr(&mut a, program);
1378 -
    try expectNoErrors(&result);
1671 +
    let mut testArena92 = testArena();
1672 +
    let testStorage92: 'test92 = &mut testArena92 in {
1673 +
        let mut a = testResolver(testStorage92);
1674 +
        let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }";
1675 +
        let result = try resolveProgramStr(&mut a, program);
1676 +
        try expectNoErrors(&result);
1677 +
    }
1379 1678
}
1380 1679
1381 1680
/// Test that a binding prong covers all union variants for exhaustiveness.
1382 1681
@test unsafe fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) {
1383 -
    let mut a = testResolver();
1384 -
    let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }";
1385 -
    let result = try resolveProgramStr(&mut a, program);
1386 -
    try expectNoErrors(&result);
1682 +
    let mut testArena93 = testArena();
1683 +
    let testStorage93: 'test93 = &mut testArena93 in {
1684 +
        let mut a = testResolver(testStorage93);
1685 +
        let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }";
1686 +
        let result = try resolveProgramStr(&mut a, program);
1687 +
        try expectNoErrors(&result);
1688 +
    }
1387 1689
}
1388 1690
1389 1691
/// Test that `case x =>` fails if `x` is not in scope, since bare identifiers
1390 1692
/// in case patterns are values to compare against, not bindings.
1391 1693
@test unsafe fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) {
1392 -
    let mut a = testResolver();
1393 -
    let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }";
1394 -
    let result = try resolveProgramStr(&mut a, program);
1395 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1694 +
    let mut testArena94 = testArena();
1695 +
    let testStorage94: 'test94 = &mut testArena94 in {
1696 +
        let mut a = testResolver(testStorage94);
1697 +
        let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }";
1698 +
        let result = try resolveProgramStr(&mut a, program);
1699 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1700 +
    }
1396 1701
}
1397 1702
1398 1703
/// Test matching on optionals: exhaustiveness and type unwrapping.
1399 1704
@test unsafe fn testResolveMatchOptional() throws (testing::TestError) {
1400 1705
    {
1401 1706
        // Exhaustive: binding + nil case.
1402 -
        let mut a = testResolver();
1403 -
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }";
1404 -
        let result = try resolveProgramStr(&mut a, program);
1405 -
        try expectNoErrors(&result);
1707 +
        let mut testArena95 = testArena();
1708 +
        let testStorage95: 'test95 = &mut testArena95 in {
1709 +
            let mut a = testResolver(testStorage95);
1710 +
            let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }";
1711 +
            let result = try resolveProgramStr(&mut a, program);
1712 +
            try expectNoErrors(&result);
1713 +
        }
1406 1714
    } {
1407 1715
        // Missing nil case.
1408 -
        let mut a = testResolver();
1409 -
        let program = "fn f(opt: ?i32) { match opt { v => {} } }";
1410 -
        let result = try resolveProgramStr(&mut a, program);
1411 -
        try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil);
1716 +
        let mut testArena96 = testArena();
1717 +
        let testStorage96: 'test96 = &mut testArena96 in {
1718 +
            let mut a = testResolver(testStorage96);
1719 +
            let program = "fn f(opt: ?i32) { match opt { v => {} } }";
1720 +
            let result = try resolveProgramStr(&mut a, program);
1721 +
            try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil);
1722 +
        }
1412 1723
    } {
1413 1724
        // Missing value case.
1414 -
        let mut a = testResolver();
1415 -
        let program = "fn f(opt: ?i32) { match opt { case nil => {} } }";
1416 -
        let result = try resolveProgramStr(&mut a, program);
1417 -
        try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue);
1725 +
        let mut testArena97 = testArena();
1726 +
        let testStorage97: 'test97 = &mut testArena97 in {
1727 +
            let mut a = testResolver(testStorage97);
1728 +
            let program = "fn f(opt: ?i32) { match opt { case nil => {} } }";
1729 +
            let result = try resolveProgramStr(&mut a, program);
1730 +
            try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue);
1731 +
        }
1418 1732
    } {
1419 1733
        // Else covers both cases.
1420 -
        let mut a = testResolver();
1421 -
        let program = "fn f(opt: ?i32) { match opt { else => {} } }";
1422 -
        let result = try resolveProgramStr(&mut a, program);
1423 -
        try expectNoErrors(&result);
1734 +
        let mut testArena98 = testArena();
1735 +
        let testStorage98: 'test98 = &mut testArena98 in {
1736 +
            let mut a = testResolver(testStorage98);
1737 +
            let program = "fn f(opt: ?i32) { match opt { else => {} } }";
1738 +
            let result = try resolveProgramStr(&mut a, program);
1739 +
            try expectNoErrors(&result);
1740 +
        }
1424 1741
    } {
1425 1742
        // Binding unwraps the inner type.
1426 -
        let mut a = testResolver();
1427 -
        let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }";
1428 -
        let result = try resolveProgramStr(&mut a, program);
1429 -
        try expectNoErrors(&result);
1743 +
        let mut testArena99 = testArena();
1744 +
        let testStorage99: 'test99 = &mut testArena99 in {
1745 +
            let mut a = testResolver(testStorage99);
1746 +
            let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }";
1747 +
            let result = try resolveProgramStr(&mut a, program);
1748 +
            try expectNoErrors(&result);
1749 +
        }
1430 1750
    }
1431 1751
}
1432 1752
1433 1753
/// Test that match on non-union types requires exhaustiveness.
1434 1754
@test unsafe fn testResolveMatchGenericExhaustive() throws (testing::TestError) {
1435 1755
    {
1436 1756
        // Match on i32 without catch-all should error.
1437 -
        let mut a = testResolver();
1438 -
        let program = "fn f(x: i32) { match x { case 1 => {} } }";
1439 -
        let result = try resolveProgramStr(&mut a, program);
1440 -
        try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive);
1757 +
        let mut testArena100 = testArena();
1758 +
        let testStorage100: 'test100 = &mut testArena100 in {
1759 +
            let mut a = testResolver(testStorage100);
1760 +
            let program = "fn f(x: i32) { match x { case 1 => {} } }";
1761 +
            let result = try resolveProgramStr(&mut a, program);
1762 +
            try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive);
1763 +
        }
1441 1764
    } {
1442 1765
        // Match on i32 with else is fine.
1443 -
        let mut a = testResolver();
1444 -
        let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }";
1445 -
        let result = try resolveProgramStr(&mut a, program);
1446 -
        try expectNoErrors(&result);
1766 +
        let mut testArena101 = testArena();
1767 +
        let testStorage101: 'test101 = &mut testArena101 in {
1768 +
            let mut a = testResolver(testStorage101);
1769 +
            let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }";
1770 +
            let result = try resolveProgramStr(&mut a, program);
1771 +
            try expectNoErrors(&result);
1772 +
        }
1447 1773
    } {
1448 1774
        // Match on i32 with binding catch-all is fine.
1449 -
        let mut a = testResolver();
1450 -
        let program = "fn f(x: i32) { match x { y => {} } }";
1451 -
        let result = try resolveProgramStr(&mut a, program);
1452 -
        try expectNoErrors(&result);
1775 +
        let mut testArena102 = testArena();
1776 +
        let testStorage102: 'test102 = &mut testArena102 in {
1777 +
            let mut a = testResolver(testStorage102);
1778 +
            let program = "fn f(x: i32) { match x { y => {} } }";
1779 +
            let result = try resolveProgramStr(&mut a, program);
1780 +
            try expectNoErrors(&result);
1781 +
        }
1453 1782
    } {
1454 1783
        // Match on i32 with wildcard catch-all is fine.
1455 -
        let mut a = testResolver();
1456 -
        let program = "fn f(x: i32) { match x { case _ => {} } }";
1457 -
        let result = try resolveProgramStr(&mut a, program);
1458 -
        try expectNoErrors(&result);
1784 +
        let mut testArena103 = testArena();
1785 +
        let testStorage103: 'test103 = &mut testArena103 in {
1786 +
            let mut a = testResolver(testStorage103);
1787 +
            let program = "fn f(x: i32) { match x { case _ => {} } }";
1788 +
            let result = try resolveProgramStr(&mut a, program);
1789 +
            try expectNoErrors(&result);
1790 +
        }
1459 1791
    }
1460 1792
}
1461 1793
1462 1794
/// Test that match on bool requires both true and false cases.
1463 1795
@test unsafe fn testResolveMatchBoolExhaustive() throws (testing::TestError) {
1464 1796
    {
1465 1797
        // Match on bool with both cases is fine.
1466 -
        let mut a = testResolver();
1467 -
        let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }";
1468 -
        let result = try resolveProgramStr(&mut a, program);
1469 -
        try expectNoErrors(&result);
1798 +
        let mut testArena104 = testArena();
1799 +
        let testStorage104: 'test104 = &mut testArena104 in {
1800 +
            let mut a = testResolver(testStorage104);
1801 +
            let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }";
1802 +
            let result = try resolveProgramStr(&mut a, program);
1803 +
            try expectNoErrors(&result);
1804 +
        }
1470 1805
    } {
1471 1806
        // Match on bool missing true should error.
1472 -
        let mut a = testResolver();
1473 -
        let program = "fn f(x: bool) { match x { case false => {} } }";
1474 -
        let result = try resolveProgramStr(&mut a, program);
1475 -
        try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true));
1807 +
        let mut testArena105 = testArena();
1808 +
        let testStorage105: 'test105 = &mut testArena105 in {
1809 +
            let mut a = testResolver(testStorage105);
1810 +
            let program = "fn f(x: bool) { match x { case false => {} } }";
1811 +
            let result = try resolveProgramStr(&mut a, program);
1812 +
            try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true));
1813 +
        }
1476 1814
    } {
1477 1815
        // Match on bool missing false should error.
1478 -
        let mut a = testResolver();
1479 -
        let program = "fn f(x: bool) { match x { case true => {} } }";
1480 -
        let result = try resolveProgramStr(&mut a, program);
1481 -
        try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false));
1816 +
        let mut testArena106 = testArena();
1817 +
        let testStorage106: 'test106 = &mut testArena106 in {
1818 +
            let mut a = testResolver(testStorage106);
1819 +
            let program = "fn f(x: bool) { match x { case true => {} } }";
1820 +
            let result = try resolveProgramStr(&mut a, program);
1821 +
            try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false));
1822 +
        }
1482 1823
    } {
1483 1824
        // Match on bool with else is fine.
1484 -
        let mut a = testResolver();
1485 -
        let program = "fn f(x: bool) { match x { else => {} } }";
1486 -
        let result = try resolveProgramStr(&mut a, program);
1487 -
        try expectNoErrors(&result);
1825 +
        let mut testArena107 = testArena();
1826 +
        let testStorage107: 'test107 = &mut testArena107 in {
1827 +
            let mut a = testResolver(testStorage107);
1828 +
            let program = "fn f(x: bool) { match x { else => {} } }";
1829 +
            let result = try resolveProgramStr(&mut a, program);
1830 +
            try expectNoErrors(&result);
1831 +
        }
1488 1832
    } {
1489 1833
        // Match on bool with binding catch-all is fine.
1490 -
        let mut a = testResolver();
1491 -
        let program = "fn f(x: bool) { match x { b => {} } }";
1492 -
        let result = try resolveProgramStr(&mut a, program);
1493 -
        try expectNoErrors(&result);
1834 +
        let mut testArena108 = testArena();
1835 +
        let testStorage108: 'test108 = &mut testArena108 in {
1836 +
            let mut a = testResolver(testStorage108);
1837 +
            let program = "fn f(x: bool) { match x { b => {} } }";
1838 +
            let result = try resolveProgramStr(&mut a, program);
1839 +
            try expectNoErrors(&result);
1840 +
        }
1494 1841
    }
1495 1842
}
1496 1843
1497 1844
@test unsafe fn testResolveBreakRequiresLoop() throws (testing::TestError) {
1498 1845
    {
1499 -
        let mut a = testResolver();
1500 -
        let result = try resolveProgramStr(&mut a, "break;");
1501 -
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1846 +
        let mut testArena109 = testArena();
1847 +
        let testStorage109: 'test109 = &mut testArena109 in {
1848 +
            let mut a = testResolver(testStorage109);
1849 +
            let result = try resolveProgramStr(&mut a, "break;");
1850 +
            try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1851 +
        }
1502 1852
    } {
1503 -
        let mut a = testResolver();
1504 -
        let result = try resolveProgramStr(&mut a, "loop { break }");
1505 -
        try expectNoErrors(&result);
1853 +
        let mut testArena110 = testArena();
1854 +
        let testStorage110: 'test110 = &mut testArena110 in {
1855 +
            let mut a = testResolver(testStorage110);
1856 +
            let result = try resolveProgramStr(&mut a, "loop { break }");
1857 +
            try expectNoErrors(&result);
1858 +
        }
1506 1859
    }
1507 1860
}
1508 1861
1509 1862
@test unsafe fn testResolveContinueRequiresLoop() throws (testing::TestError) {
1510 1863
    {
1511 -
        let mut a = testResolver();
1512 -
        let result = try resolveProgramStr(&mut a, "continue;");
1513 -
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1864 +
        let mut testArena111 = testArena();
1865 +
        let testStorage111: 'test111 = &mut testArena111 in {
1866 +
            let mut a = testResolver(testStorage111);
1867 +
            let result = try resolveProgramStr(&mut a, "continue;");
1868 +
            try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1869 +
        }
1514 1870
    } {
1515 -
        let mut a = testResolver();
1516 -
        let result = try resolveProgramStr(&mut a, "while true { continue }");
1517 -
        try expectNoErrors(&result);
1871 +
        let mut testArena112 = testArena();
1872 +
        let testStorage112: 'test112 = &mut testArena112 in {
1873 +
            let mut a = testResolver(testStorage112);
1874 +
            let result = try resolveProgramStr(&mut a, "while true { continue }");
1875 +
            try expectNoErrors(&result);
1876 +
        }
1518 1877
    }
1519 1878
}
1520 1879
1521 1880
@test unsafe fn testResolveFnTypeVoidNoParams() throws (testing::TestError) {
1522 -
    let mut a = testResolver();
1523 -
    let result = try resolveProgramStr(&mut a, "fn f() {} f();");
1524 -
    try expectNoErrors(&result);
1525 -
1526 -
    let blockNode = result.root;
1527 -
    let case ast::NodeValue::Block(block) = blockNode.value
1528 -
        else throw testing::TestError::Failed;
1529 -
    let fnNode = try getBlockStmt(blockNode, 0);
1530 -
    let callStmt = try getBlockStmt(blockNode, 1);
1531 -
1532 -
    { // Verify the function symbol captures an empty parameter list and void return.
1533 -
        let sym = super::symbolFor(&a, fnNode)
1534 -
            else throw testing::TestError::Failed;
1535 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1536 -
            else throw testing::TestError::Failed;
1537 -
        try testing::expect(fnTy.paramTypes.len == 0);
1538 -
        try testing::expect(*fnTy.returnType == super::Type::Void);
1539 -
    }
1540 -
    { // Checking that the type of the call matches the function return type.
1541 -
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1881 +
    let mut testArena113 = testArena();
1882 +
    let testStorage113: 'test113 = &mut testArena113 in {
1883 +
        let mut a = testResolver(testStorage113);
1884 +
        let result = try resolveProgramStr(&mut a, "fn f() {} f();");
1885 +
        try expectNoErrors(&result);
1542 1886
1543 -
        let fnSym = super::symbolFor(&a, fnNode)
1544 -
            else throw testing::TestError::Failed;
1545 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1887 +
        let blockNode = result.root;
1888 +
        let case ast::NodeValue::Block(block) = blockNode.value
1546 1889
            else throw testing::TestError::Failed;
1547 -
        try expectType(&a, callExpr, *fnTy.returnType);
1890 +
        let fnNode = try getBlockStmt(blockNode, 0);
1891 +
        let callStmt = try getBlockStmt(blockNode, 1);
1892 +
1893 +
        { // Verify the function symbol captures an empty parameter list and void return.
1894 +
            let sym = super::symbolFor(&a, fnNode)
1895 +
                else throw testing::TestError::Failed;
1896 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1897 +
                else throw testing::TestError::Failed;
1898 +
            try testing::expect(fnTy.paramTypes.len == 0);
1899 +
            try testing::expect(*fnTy.returnType == super::Type::Void);
1900 +
        }
1901 +
        { // Checking that the type of the call matches the function return type.
1902 +
            let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1903 +
1904 +
            let fnSym = super::symbolFor(&a, fnNode)
1905 +
                else throw testing::TestError::Failed;
1906 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1907 +
                else throw testing::TestError::Failed;
1908 +
            try expectType(&a, callExpr, *fnTy.returnType);
1909 +
        }
1548 1910
    }
1549 1911
}
1550 1912
1551 1913
@test unsafe fn testResolveFnTypeReturnsValue() throws (testing::TestError) {
1552 -
    let mut a = testResolver();
1553 -
    let program = "fn f() -> i32 { return 1; } f();";
1554 -
    let result = try resolveProgramStr(&mut a, program);
1555 -
    try expectNoErrors(&result);
1556 -
1557 -
    let blockNode = result.root;
1558 -
    let case ast::NodeValue::Block(block) = blockNode.value
1559 -
        else throw testing::TestError::Failed;
1560 -
    let fnNode = try getBlockStmt(blockNode, 0);
1561 -
    let callStmt = try getBlockStmt(blockNode, 1);
1562 -
1563 -
    { // Function returns i32 with no parameters.
1564 -
        let sym = super::symbolFor(&a, fnNode)
1565 -
            else throw testing::TestError::Failed;
1566 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1567 -
            else throw testing::TestError::Failed;
1568 -
        try testing::expect(fnTy.paramTypes.len == 0);
1569 -
        try testing::expect(*fnTy.returnType == super::Type::I32);
1570 -
    }
1571 -
    { // Call expression should inherit the function's return type.
1572 -
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32);
1914 +
    let mut testArena114 = testArena();
1915 +
    let testStorage114: 'test114 = &mut testArena114 in {
1916 +
        let mut a = testResolver(testStorage114);
1917 +
        let program = "fn f() -> i32 { return 1; } f();";
1918 +
        let result = try resolveProgramStr(&mut a, program);
1919 +
        try expectNoErrors(&result);
1573 1920
1574 -
        let fnSym = super::symbolFor(&a, fnNode)
1921 +
        let blockNode = result.root;
1922 +
        let case ast::NodeValue::Block(block) = blockNode.value
1575 1923
            else throw testing::TestError::Failed;
1576 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1577 -
            else throw testing::TestError::Failed;
1578 -
        try expectType(&a, callExpr, *fnTy.returnType);
1924 +
        let fnNode = try getBlockStmt(blockNode, 0);
1925 +
        let callStmt = try getBlockStmt(blockNode, 1);
1926 +
1927 +
        { // Function returns i32 with no parameters.
1928 +
            let sym = super::symbolFor(&a, fnNode)
1929 +
                else throw testing::TestError::Failed;
1930 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1931 +
                else throw testing::TestError::Failed;
1932 +
            try testing::expect(fnTy.paramTypes.len == 0);
1933 +
            try testing::expect(*fnTy.returnType == super::Type::I32);
1934 +
        }
1935 +
        { // Call expression should inherit the function's return type.
1936 +
            let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32);
1937 +
1938 +
            let fnSym = super::symbolFor(&a, fnNode)
1939 +
                else throw testing::TestError::Failed;
1940 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1941 +
                else throw testing::TestError::Failed;
1942 +
            try expectType(&a, callExpr, *fnTy.returnType);
1943 +
        }
1579 1944
    }
1580 1945
}
1581 1946
1582 1947
@test unsafe fn testResolveFnTypeSingleParam() throws (testing::TestError) {
1583 -
    let mut a = testResolver();
1584 -
    let program = "fn f(x: i8) {} let x: i8 = 1; f(x);";
1585 -
    let result = try resolveProgramStr(&mut a, program);
1586 -
    try expectNoErrors(&result);
1587 -
1588 -
    let blockNode = result.root;
1589 -
    let case ast::NodeValue::Block(block) = blockNode.value
1590 -
        else throw testing::TestError::Failed;
1591 -
    let fnNode = try getBlockStmt(blockNode, 0);
1592 -
    let callStmt = try getBlockStmt(blockNode, 2);
1593 -
1594 -
    { // Single parameter propagates nominal type onto the symbol and parameter node.
1595 -
        let sym = super::symbolFor(&a, fnNode)
1596 -
            else throw testing::TestError::Failed;
1597 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1598 -
            else throw testing::TestError::Failed;
1599 -
        try testing::expect(fnTy.paramTypes.len == 1);
1600 -
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1601 -
        try testing::expect(*fnTy.returnType == super::Type::Void);
1948 +
    let mut testArena115 = testArena();
1949 +
    let testStorage115: 'test115 = &mut testArena115 in {
1950 +
        let mut a = testResolver(testStorage115);
1951 +
        let program = "fn f(x: i8) {} let x: i8 = 1; f(x);";
1952 +
        let result = try resolveProgramStr(&mut a, program);
1953 +
        try expectNoErrors(&result);
1602 1954
1603 -
        let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
1955 +
        let blockNode = result.root;
1956 +
        let case ast::NodeValue::Block(block) = blockNode.value
1604 1957
            else throw testing::TestError::Failed;
1605 -
        try testing::expect(fnDecl.sig.params.len == 1);
1606 -
1607 -
        let paramNode = fnDecl.sig.params[0];
1608 -
        try expectType(&a, paramNode, super::Type::I8);
1609 -
    }
1610 -
    { // Call should resolve to void, matching the function's return type.
1611 -
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1612 -
        let fnSym = super::symbolFor(&a, fnNode)
1613 -
            else throw testing::TestError::Failed;
1614 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1615 -
            else throw testing::TestError::Failed;
1616 -
        try expectType(&a, callExpr, *fnTy.returnType);
1958 +
        let fnNode = try getBlockStmt(blockNode, 0);
1959 +
        let callStmt = try getBlockStmt(blockNode, 2);
1960 +
1961 +
        { // Single parameter propagates nominal type onto the symbol and parameter node.
1962 +
            let sym = super::symbolFor(&a, fnNode)
1963 +
                else throw testing::TestError::Failed;
1964 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1965 +
                else throw testing::TestError::Failed;
1966 +
            try testing::expect(fnTy.paramTypes.len == 1);
1967 +
            try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1968 +
            try testing::expect(*fnTy.returnType == super::Type::Void);
1969 +
1970 +
            let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
1971 +
                else throw testing::TestError::Failed;
1972 +
            try testing::expect(fnDecl.sig.params.len == 1);
1973 +
1974 +
            let paramNode = fnDecl.sig.params[0];
1975 +
            try expectType(&a, paramNode, super::Type::I8);
1976 +
        }
1977 +
        { // Call should resolve to void, matching the function's return type.
1978 +
            let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1979 +
            let fnSym = super::symbolFor(&a, fnNode)
1980 +
                else throw testing::TestError::Failed;
1981 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1982 +
                else throw testing::TestError::Failed;
1983 +
            try expectType(&a, callExpr, *fnTy.returnType);
1984 +
        }
1617 1985
    }
1618 1986
}
1619 1987
1620 1988
@test unsafe fn testResolveFnTypeMultipleParams() throws (testing::TestError) {
1621 -
    let mut a = testResolver();
1622 -
    let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);";
1623 -
    let result = try resolveProgramStr(&mut a, program);
1624 -
    try expectNoErrors(&result);
1625 -
1626 -
    let blockNode = result.root;
1627 -
    let case ast::NodeValue::Block(block) = blockNode.value
1628 -
        else throw testing::TestError::Failed;
1629 -
    let fnNode = try getBlockStmt(blockNode, 0);
1630 -
    let callStmt = try getBlockStmt(blockNode, 3);
1631 -
1632 -
    { // Ensure multi-parameter signatures record both argument types.
1633 -
        let sym = super::symbolFor(&a, fnNode)
1634 -
            else throw testing::TestError::Failed;
1635 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1636 -
            else throw testing::TestError::Failed;
1637 -
        try testing::expect(fnTy.paramTypes.len == 2);
1638 -
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1639 -
        try testing::expect(*fnTy.paramTypes[1] == super::Type::I32);
1640 -
        try testing::expect(*fnTy.returnType == super::Type::Void);
1641 -
1642 -
        let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
1643 -
            else throw testing::TestError::Failed;
1644 -
        try testing::expect(fnDecl.sig.params.len == 2);
1989 +
    let mut testArena116 = testArena();
1990 +
    let testStorage116: 'test116 = &mut testArena116 in {
1991 +
        let mut a = testResolver(testStorage116);
1992 +
        let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);";
1993 +
        let result = try resolveProgramStr(&mut a, program);
1994 +
        try expectNoErrors(&result);
1645 1995
1646 -
        let firstParam = fnDecl.sig.params[0];
1647 -
        let secondParam = fnDecl.sig.params[1];
1648 -
        try expectType(&a, firstParam, super::Type::I8);
1649 -
        try expectType(&a, secondParam, super::Type::I32);
1650 -
    }
1651 -
    { // Call expression should again mirror the function return type.
1652 -
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1653 -
        let fnSym = super::symbolFor(&a, fnNode)
1654 -
            else throw testing::TestError::Failed;
1655 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1996 +
        let blockNode = result.root;
1997 +
        let case ast::NodeValue::Block(block) = blockNode.value
1656 1998
            else throw testing::TestError::Failed;
1657 -
        try expectType(&a, callExpr, *fnTy.returnType);
1999 +
        let fnNode = try getBlockStmt(blockNode, 0);
2000 +
        let callStmt = try getBlockStmt(blockNode, 3);
2001 +
2002 +
        { // Ensure multi-parameter signatures record both argument types.
2003 +
            let sym = super::symbolFor(&a, fnNode)
2004 +
                else throw testing::TestError::Failed;
2005 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
2006 +
                else throw testing::TestError::Failed;
2007 +
            try testing::expect(fnTy.paramTypes.len == 2);
2008 +
            try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
2009 +
            try testing::expect(*fnTy.paramTypes[1] == super::Type::I32);
2010 +
            try testing::expect(*fnTy.returnType == super::Type::Void);
2011 +
2012 +
            let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
2013 +
                else throw testing::TestError::Failed;
2014 +
            try testing::expect(fnDecl.sig.params.len == 2);
2015 +
2016 +
            let firstParam = fnDecl.sig.params[0];
2017 +
            let secondParam = fnDecl.sig.params[1];
2018 +
            try expectType(&a, firstParam, super::Type::I8);
2019 +
            try expectType(&a, secondParam, super::Type::I32);
2020 +
        }
2021 +
        { // Call expression should again mirror the function return type.
2022 +
            let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
2023 +
            let fnSym = super::symbolFor(&a, fnNode)
2024 +
                else throw testing::TestError::Failed;
2025 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
2026 +
                else throw testing::TestError::Failed;
2027 +
            try expectType(&a, callExpr, *fnTy.returnType);
2028 +
        }
1658 2029
    }
1659 2030
}
1660 2031
1661 2032
@test unsafe fn testResolveFnRecursiveCall() throws (testing::TestError) {
1662 -
    let mut a = testResolver();
1663 -
    let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }";
1664 -
    let result = try resolveProgramStr(&mut a, program);
1665 -
    try expectNoErrors(&result);
1666 -
1667 -
    let blockNode = result.root;
1668 -
    let case ast::NodeValue::Block(block) = blockNode.value
1669 -
        else throw testing::TestError::Failed;
1670 -
    let fnNode = try getBlockStmt(blockNode, 0);
2033 +
    let mut testArena117 = testArena();
2034 +
    let testStorage117: 'test117 = &mut testArena117 in {
2035 +
        let mut a = testResolver(testStorage117);
2036 +
        let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }";
2037 +
        let result = try resolveProgramStr(&mut a, program);
2038 +
        try expectNoErrors(&result);
1671 2039
1672 -
    { // Function symbol should be visible for recursive calls within its own body.
1673 -
        let sym = super::symbolFor(&a, fnNode)
1674 -
            else throw testing::TestError::Failed;
1675 -
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
2040 +
        let blockNode = result.root;
2041 +
        let case ast::NodeValue::Block(block) = blockNode.value
1676 2042
            else throw testing::TestError::Failed;
1677 -
        try testing::expect(fnTy.paramTypes.len == 1);
1678 -
        try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool);
1679 -
        try testing::expect(*fnTy.returnType == super::Type::Bool);
2043 +
        let fnNode = try getBlockStmt(blockNode, 0);
2044 +
2045 +
        { // Function symbol should be visible for recursive calls within its own body.
2046 +
            let sym = super::symbolFor(&a, fnNode)
2047 +
                else throw testing::TestError::Failed;
2048 +
            let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
2049 +
                else throw testing::TestError::Failed;
2050 +
            try testing::expect(fnTy.paramTypes.len == 1);
2051 +
            try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool);
2052 +
            try testing::expect(*fnTy.returnType == super::Type::Bool);
2053 +
        }
1680 2054
    }
1681 2055
}
1682 2056
1683 2057
@test unsafe fn testResolveFnCallMissingArgument() throws (testing::TestError) {
1684 -
    let mut a = testResolver();
1685 -
    let program = "fn f(x: i8) {} f();";
1686 -
    let result = try resolveProgramStr(&mut a, program);
1687 -
    // Expect an error when a required parameter is omitted.
1688 -
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1689 -
        expected: 1,
1690 -
        actual: 0,
1691 -
    }));
2058 +
    let mut testArena118 = testArena();
2059 +
    let testStorage118: 'test118 = &mut testArena118 in {
2060 +
        let mut a = testResolver(testStorage118);
2061 +
        let program = "fn f(x: i8) {} f();";
2062 +
        let result = try resolveProgramStr(&mut a, program);
2063 +
        // Expect an error when a required parameter is omitted.
2064 +
        try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
2065 +
            expected: 1,
2066 +
            actual: 0,
2067 +
        }));
2068 +
    }
1692 2069
}
1693 2070
1694 2071
@test unsafe fn testResolveFnCallExtraArgument() throws (testing::TestError) {
1695 -
    let mut a = testResolver();
1696 -
    let program = "fn f() {} f(1);";
1697 -
    let result = try resolveProgramStr(&mut a, program);
1698 -
    // Passing more arguments than declared should fail.
1699 -
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1700 -
        expected: 0,
1701 -
        actual: 1,
1702 -
    }));
2072 +
    let mut testArena119 = testArena();
2073 +
    let testStorage119: 'test119 = &mut testArena119 in {
2074 +
        let mut a = testResolver(testStorage119);
2075 +
        let program = "fn f() {} f(1);";
2076 +
        let result = try resolveProgramStr(&mut a, program);
2077 +
        // Passing more arguments than declared should fail.
2078 +
        try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
2079 +
            expected: 0,
2080 +
            actual: 1,
2081 +
        }));
2082 +
    }
1703 2083
}
1704 2084
1705 2085
@test unsafe fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) {
1706 -
    let mut a = testResolver();
1707 -
    let program = "fn f(x: i8) {} f(true);";
1708 -
    let result = try resolveProgramStr(&mut a, program);
1709 -
    let err = try expectError(&result);
1710 -
    // The argument type (bool) should not match the parameter type (i8).
1711 -
    try expectTypeMismatch(err, super::Type::I8, super::Type::Bool);
2086 +
    let mut testArena120 = testArena();
2087 +
    let testStorage120: 'test120 = &mut testArena120 in {
2088 +
        let mut a = testResolver(testStorage120);
2089 +
        let program = "fn f(x: i8) {} f(true);";
2090 +
        let result = try resolveProgramStr(&mut a, program);
2091 +
        let err = try expectError(&result);
2092 +
        // The argument type (bool) should not match the parameter type (i8).
2093 +
        try expectTypeMismatch(err, super::Type::I8, super::Type::Bool);
2094 +
    }
1712 2095
}
1713 2096
1714 2097
@test unsafe fn testResolveFnReturnTypeMismatch() throws (testing::TestError) {
1715 -
    let mut a = testResolver();
1716 -
    let program = "fn f() -> i32 { return true; }";
1717 -
    let result = try resolveProgramStr(&mut a, program);
1718 -
    let err = try expectError(&result);
1719 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2098 +
    let mut testArena121 = testArena();
2099 +
    let testStorage121: 'test121 = &mut testArena121 in {
2100 +
        let mut a = testResolver(testStorage121);
2101 +
        let program = "fn f() -> i32 { return true; }";
2102 +
        let result = try resolveProgramStr(&mut a, program);
2103 +
        let err = try expectError(&result);
2104 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2105 +
    }
1720 2106
}
1721 2107
1722 2108
@test unsafe fn testResolveFnReturnVoid() throws (testing::TestError) {
1723 2109
    {
1724 -
        let mut a = testResolver();
1725 -
        let result = try resolveProgramStr(&mut a, "fn f() { return; }");
1726 -
        try expectNoErrors(&result);
2110 +
        let mut testArena122 = testArena();
2111 +
        let testStorage122: 'test122 = &mut testArena122 in {
2112 +
            let mut a = testResolver(testStorage122);
2113 +
            let result = try resolveProgramStr(&mut a, "fn f() { return; }");
2114 +
            try expectNoErrors(&result);
2115 +
        }
1727 2116
    } {
1728 -
        let mut a = testResolver();
1729 -
        let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }");
1730 -
        let err = try expectError(&result);
1731 -
        try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2117 +
        let mut testArena123 = testArena();
2118 +
        let testStorage123: 'test123 = &mut testArena123 in {
2119 +
            let mut a = testResolver(testStorage123);
2120 +
            let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }");
2121 +
            let err = try expectError(&result);
2122 +
            try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2123 +
        }
1732 2124
    }
1733 2125
}
1734 2126
1735 2127
@test unsafe fn testResolveFnMissingReturn() throws (testing::TestError) {
1736 2128
    {
1737 -
        let mut a = testResolver();
1738 -
        let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}");
1739 -
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2129 +
        let mut testArena124 = testArena();
2130 +
        let testStorage124: 'test124 = &mut testArena124 in {
2131 +
            let mut a = testResolver(testStorage124);
2132 +
            let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}");
2133 +
            try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2134 +
        }
1740 2135
    } {
1741 -
        let mut a = testResolver();
1742 -
        let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }";
1743 -
        let result = try resolveProgramStr(&mut a, program);
1744 -
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2136 +
        let mut testArena125 = testArena();
2137 +
        let testStorage125: 'test125 = &mut testArena125 in {
2138 +
            let mut a = testResolver(testStorage125);
2139 +
            let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }";
2140 +
            let result = try resolveProgramStr(&mut a, program);
2141 +
            try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2142 +
        }
1745 2143
    }
1746 2144
}
1747 2145
1748 2146
/// Never-returning functions require divergence on every path.
1749 2147
@test unsafe fn testResolveNeverReturn() throws (testing::TestError) {
1759 2157
        "fn f() -> ! { while true {} }",
1760 2158
        "fn f(flag: bool) -> ! { if flag { panic; } else { while true {} } }",
1761 2159
        "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! throws (i32) { try f(); }",
1762 2160
        "fn f() -> ! throws (i32) { throw 1; } fn g() -> i32 { try f() catch { return 2; }; }",
1763 2161
    ] {
1764 -
        let mut a = testResolver();
1765 -
        let result = try resolveProgramStr(&mut a, program);
1766 -
        try expectNoErrors(&result);
2162 +
        let mut testArena126 = testArena();
2163 +
        let testStorage126: 'test126 = &mut testArena126 in {
2164 +
            let mut a = testResolver(testStorage126);
2165 +
            let result = try resolveProgramStr(&mut a, program);
2166 +
            try expectNoErrors(&result);
2167 +
        }
1767 2168
    }
1768 2169
}
1769 2170
1770 2171
/// A never-returning signature rejects any normal completion path.
1771 2172
@test unsafe fn testResolveNeverFallthrough() throws (testing::TestError) {
1774 2175
        "fn f(flag: bool) -> ! { if flag { panic; } }",
1775 2176
        "fn f() -> ! { while true { break; } }",
1776 2177
        "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch {}; }",
1777 2178
        "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch e as i32 {}; }",
1778 2179
    ] {
1779 -
        let mut a = testResolver();
1780 -
        let result = try resolveProgramStr(&mut a, program);
1781 -
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2180 +
        let mut testArena127 = testArena();
2181 +
        let testStorage127: 'test127 = &mut testArena127 in {
2182 +
            let mut a = testResolver(testStorage127);
2183 +
            let result = try resolveProgramStr(&mut a, program);
2184 +
            try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2185 +
        }
1782 2186
    }
1783 2187
}
1784 2188
1785 2189
/// No value, including undefined, can construct a never return value.
1786 2190
@test unsafe fn testResolveNeverValue() throws (testing::TestError) {
1788 2192
        "fn f() -> ! { return; }",
1789 2193
        "fn f() -> ! { return 1; }",
1790 2194
        "unsafe fn f() -> ! { return undefined; }",
1791 2195
        "fn ordinary() {} fn f() { let callback: fn() -> ! = ordinary; }",
1792 2196
    ] {
1793 -
        let mut a = testResolver();
1794 -
        let result = try resolveProgramStr(&mut a, program);
1795 -
        let error = try expectError(&result);
2197 +
        let mut testArena128 = testArena();
2198 +
        let testStorage128: 'test128 = &mut testArena128 in {
2199 +
            let mut a = testResolver(testStorage128);
2200 +
            let result = try resolveProgramStr(&mut a, program);
2201 +
            let error = try expectError(&result);
2202 +
        }
1796 2203
    }
1797 2204
}
1798 2205
1799 2206
/// Caught errors preserve the caller's exact-use ownership obligations.
1800 2207
@test unsafe fn testResolveNeverCaughtOwnership() throws (testing::TestError) {
1801 2208
    for program in &[
1802 2209
        "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { try fail() catch {}; }",
1803 2210
        "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { let absent = try? fail(); }",
1804 2211
    ] {
1805 -
        let mut a = testResolver();
1806 -
        let result = try resolveProgramStr(&mut a, program);
1807 -
        let error = try expectError(&result);
1808 -
        let case super::ErrorKind::LinearNotConsumed(_) = error.kind
1809 -
            else throw testing::TestError::Failed;
2212 +
        let mut testArena129 = testArena();
2213 +
        let testStorage129: 'test129 = &mut testArena129 in {
2214 +
            let mut a = testResolver(testStorage129);
2215 +
            let result = try resolveProgramStr(&mut a, program);
2216 +
            let error = try expectError(&result);
2217 +
            let case super::ErrorKind::LinearNotConsumed(_) = error.kind
2218 +
                else throw testing::TestError::Failed;
2219 +
        }
1810 2220
    }
1811 2221
}
1812 2222
1813 2223
@test unsafe fn testResolveFnAllPathsReturn() throws (testing::TestError) {
1814 -
    let mut a = testResolver();
1815 -
    let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }";
1816 -
    let result = try resolveProgramStr(&mut a, program);
1817 -
    try expectNoErrors(&result);
2224 +
    let mut testArena130 = testArena();
2225 +
    let testStorage130: 'test130 = &mut testArena130 in {
2226 +
        let mut a = testResolver(testStorage130);
2227 +
        let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }";
2228 +
        let result = try resolveProgramStr(&mut a, program);
2229 +
        try expectNoErrors(&result);
2230 +
    }
1818 2231
}
1819 2232
1820 2233
/// Test that match statements with returns in all branches don't require a
1821 2234
/// return at the end of the function.
1822 2235
@test unsafe fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) {
1823 2236
    {
1824 2237
        // Union match with all variants returning.
1825 -
        let mut a = testResolver();
1826 -
        let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }";
1827 -
        let result = try resolveProgramStr(&mut a, program);
1828 -
        try expectNoErrors(&result);
2238 +
        let mut testArena131 = testArena();
2239 +
        let testStorage131: 'test131 = &mut testArena131 in {
2240 +
            let mut a = testResolver(testStorage131);
2241 +
            let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }";
2242 +
            let result = try resolveProgramStr(&mut a, program);
2243 +
            try expectNoErrors(&result);
2244 +
        }
1829 2245
    } {
1830 2246
        // Match with default case where all branches return.
1831 -
        let mut a = testResolver();
1832 -
        let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }";
1833 -
        let result = try resolveProgramStr(&mut a, program);
1834 -
        try expectNoErrors(&result);
2247 +
        let mut testArena132 = testArena();
2248 +
        let testStorage132: 'test132 = &mut testArena132 in {
2249 +
            let mut a = testResolver(testStorage132);
2250 +
            let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }";
2251 +
            let result = try resolveProgramStr(&mut a, program);
2252 +
            try expectNoErrors(&result);
2253 +
        }
1835 2254
    } {
1836 2255
        // Match where not all branches return should error.
1837 -
        let mut a = testResolver();
1838 -
        let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }";
1839 -
        let result = try resolveProgramStr(&mut a, program);
1840 -
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2256 +
        let mut testArena133 = testArena();
2257 +
        let testStorage133: 'test133 = &mut testArena133 in {
2258 +
            let mut a = testResolver(testStorage133);
2259 +
            let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }";
2260 +
            let result = try resolveProgramStr(&mut a, program);
2261 +
            try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
2262 +
        }
1841 2263
    }
1842 2264
}
1843 2265
1844 2266
@test unsafe fn testResolveAssign() throws (testing::TestError) {
1845 2267
    {
1846 -
        let mut a = testResolver();
1847 -
        let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;");
1848 -
        try expectNoErrors(&result);
2268 +
        let mut testArena134 = testArena();
2269 +
        let testStorage134: 'test134 = &mut testArena134 in {
2270 +
            let mut a = testResolver(testStorage134);
2271 +
            let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;");
2272 +
            try expectNoErrors(&result);
2273 +
        }
1849 2274
    } {
1850 -
        let mut a = testResolver();
1851 -
        let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;");
1852 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2275 +
        let mut testArena135 = testArena();
2276 +
        let testStorage135: 'test135 = &mut testArena135 in {
2277 +
            let mut a = testResolver(testStorage135);
2278 +
            let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;");
2279 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2280 +
        }
1853 2281
    } {
1854 -
        let mut a = testResolver();
1855 -
        let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;");
1856 -
        let err = try expectError(&result);
1857 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2282 +
        let mut testArena136 = testArena();
2283 +
        let testStorage136: 'test136 = &mut testArena136 in {
2284 +
            let mut a = testResolver(testStorage136);
2285 +
            let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;");
2286 +
            let err = try expectError(&result);
2287 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2288 +
        }
1858 2289
    } {
1859 -
        let mut a = testResolver();
1860 -
        let result = try resolveProgramStr(&mut a, "set x = 1;");
1861 -
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
2290 +
        let mut testArena137 = testArena();
2291 +
        let testStorage137: 'test137 = &mut testArena137 in {
2292 +
            let mut a = testResolver(testStorage137);
2293 +
            let result = try resolveProgramStr(&mut a, "set x = 1;");
2294 +
            try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
2295 +
        }
1862 2296
    } {
1863 -
        let mut a = testResolver();
1864 -
        let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;");
1865 -
        try expectNoErrors(&result);
2297 +
        let mut testArena138 = testArena();
2298 +
        let testStorage138: 'test138 = &mut testArena138 in {
2299 +
            let mut a = testResolver(testStorage138);
2300 +
            let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;");
2301 +
            try expectNoErrors(&result);
2302 +
        }
1866 2303
    } {
1867 -
        let mut a = testResolver();
1868 -
        let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;");
1869 -
        try expectNoErrors(&result);
2304 +
        let mut testArena139 = testArena();
2305 +
        let testStorage139: 'test139 = &mut testArena139 in {
2306 +
            let mut a = testResolver(testStorage139);
2307 +
            let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;");
2308 +
            try expectNoErrors(&result);
2309 +
        }
1870 2310
    }
1871 2311
}
1872 2312
1873 2313
@test unsafe fn testResolveAssignSubscript() throws (testing::TestError) {
1874 2314
    {
1875 -
        let mut a = testResolver();
1876 -
        let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
1877 -
        let result = try resolveProgramStr(&mut a, program);
1878 -
        try expectNoErrors(&result);
2315 +
        let mut testArena140 = testArena();
2316 +
        let testStorage140: 'test140 = &mut testArena140 in {
2317 +
            let mut a = testResolver(testStorage140);
2318 +
            let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
2319 +
            let result = try resolveProgramStr(&mut a, program);
2320 +
            try expectNoErrors(&result);
2321 +
        }
1879 2322
    }
1880 2323
    {
1881 -
        let mut a = testResolver();
1882 -
        let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;";
1883 -
        let result = try resolveProgramStr(&mut a, program);
1884 -
        try expectNoErrors(&result);
2324 +
        let mut testArena141 = testArena();
2325 +
        let testStorage141: 'test141 = &mut testArena141 in {
2326 +
            let mut a = testResolver(testStorage141);
2327 +
            let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;";
2328 +
            let result = try resolveProgramStr(&mut a, program);
2329 +
            try expectNoErrors(&result);
2330 +
        }
1885 2331
    }
1886 2332
    {
1887 -
        let mut a = testResolver();
1888 -
        let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;";
1889 -
        let result = try resolveProgramStr(&mut a, program);
1890 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2333 +
        let mut testArena142 = testArena();
2334 +
        let testStorage142: 'test142 = &mut testArena142 in {
2335 +
            let mut a = testResolver(testStorage142);
2336 +
            let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;";
2337 +
            let result = try resolveProgramStr(&mut a, program);
2338 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2339 +
        }
1891 2340
    }
1892 2341
    {
1893 -
        let mut a = testResolver();
1894 -
        let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
1895 -
        let result = try resolveProgramStr(&mut a, program);
1896 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2342 +
        let mut testArena143 = testArena();
2343 +
        let testStorage143: 'test143 = &mut testArena143 in {
2344 +
            let mut a = testResolver(testStorage143);
2345 +
            let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
2346 +
            let result = try resolveProgramStr(&mut a, program);
2347 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2348 +
        }
1897 2349
    }
1898 2350
    {
1899 -
        let mut a = testResolver();
1900 -
        let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;";
1901 -
        let result = try resolveProgramStr(&mut a, program);
1902 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2351 +
        let mut testArena144 = testArena();
2352 +
        let testStorage144: 'test144 = &mut testArena144 in {
2353 +
            let mut a = testResolver(testStorage144);
2354 +
            let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;";
2355 +
            let result = try resolveProgramStr(&mut a, program);
2356 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2357 +
        }
1903 2358
    }
1904 2359
}
1905 2360
1906 2361
@test unsafe fn testResolveAssignIntegerLits() throws (testing::TestError) {
1907 2362
    try expectAnalyzeOk("let x: i8 = 127;");
1942 2397
    try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8);
1943 2398
}
1944 2399
1945 2400
@test unsafe fn testNilCoercions() throws (testing::TestError) {
1946 2401
    {
1947 -
        let mut a = testResolver();
1948 -
        let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;");
1949 -
        try expectNoErrors(&result);
2402 +
        let mut testArena145 = testArena();
2403 +
        let testStorage145: 'test145 = &mut testArena145 in {
2404 +
            let mut a = testResolver(testStorage145);
2405 +
            let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;");
2406 +
            try expectNoErrors(&result);
2407 +
        }
1950 2408
    } {
1951 -
        let mut a = testResolver();
1952 -
        let program = "fn g(opt: ?i32) {} fn f() { g(nil); }";
1953 -
        let result = try resolveProgramStr(&mut a, program);
1954 -
        try expectNoErrors(&result);
2409 +
        let mut testArena146 = testArena();
2410 +
        let testStorage146: 'test146 = &mut testArena146 in {
2411 +
            let mut a = testResolver(testStorage146);
2412 +
            let program = "fn g(opt: ?i32) {} fn f() { g(nil); }";
2413 +
            let result = try resolveProgramStr(&mut a, program);
2414 +
            try expectNoErrors(&result);
2415 +
        }
1955 2416
    } {
1956 -
        let mut a = testResolver();
1957 -
        let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }";
1958 -
        let result = try resolveProgramStr(&mut a, program);
1959 -
        try expectNoErrors(&result);
2417 +
        let mut testArena147 = testArena();
2418 +
        let testStorage147: 'test147 = &mut testArena147 in {
2419 +
            let mut a = testResolver(testStorage147);
2420 +
            let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }";
2421 +
            let result = try resolveProgramStr(&mut a, program);
2422 +
            try expectNoErrors(&result);
2423 +
        }
1960 2424
    }
1961 2425
}
1962 2426
1963 2427
@test unsafe fn testOptionalComparedWithNil() throws (testing::TestError) {
1964 -
    let mut a = testResolver();
1965 -
    let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;";
1966 -
    let result = try resolveBlockStr(&mut a, program);
1967 -
    try expectNoErrors(&result);
2428 +
    let mut testArena148 = testArena();
2429 +
    let testStorage148: 'test148 = &mut testArena148 in {
2430 +
        let mut a = testResolver(testStorage148);
2431 +
        let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;";
2432 +
        let result = try resolveBlockStr(&mut a, program);
2433 +
        try expectNoErrors(&result);
1968 2434
1969 -
    for i in 1..7 {
1970 -
        let stmt = try getBlockStmt(result.root, i);
1971 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2435 +
        for i in 1..7 {
2436 +
            let stmt = try getBlockStmt(result.root, i);
2437 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
2438 +
        }
1972 2439
    }
1973 2440
}
1974 2441
1975 2442
@test unsafe fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) {
1976 -
    let mut a = testResolver();
1977 -
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };";
1978 -
    let result = try resolveProgramStr(&mut a, program);
1979 -
    try expectNoErrors(&result);
2443 +
    let mut testArena149 = testArena();
2444 +
    let testStorage149: 'test149 = &mut testArena149 in {
2445 +
        let mut a = testResolver(testStorage149);
2446 +
        let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };";
2447 +
        let result = try resolveProgramStr(&mut a, program);
2448 +
        try expectNoErrors(&result);
2449 +
    }
1980 2450
}
1981 2451
1982 2452
@test unsafe fn testResolveRecordLiteralMissingField() throws (testing::TestError) {
1983 -
    let mut a = testResolver();
1984 -
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };";
1985 -
    let result = try resolveProgramStr(&mut a, program);
1986 -
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
2453 +
    let mut testArena150 = testArena();
2454 +
    let testStorage150: 'test150 = &mut testArena150 in {
2455 +
        let mut a = testResolver(testStorage150);
2456 +
        let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };";
2457 +
        let result = try resolveProgramStr(&mut a, program);
2458 +
        try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
2459 +
    }
1987 2460
}
1988 2461
1989 2462
@test unsafe fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) {
1990 -
    let mut a = testResolver();
1991 -
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };";
1992 -
    let result = try resolveProgramStr(&mut a, program);
1993 -
    let err = try expectError(&result);
1994 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2463 +
    let mut testArena151 = testArena();
2464 +
    let testStorage151: 'test151 = &mut testArena151 in {
2465 +
        let mut a = testResolver(testStorage151);
2466 +
        let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };";
2467 +
        let result = try resolveProgramStr(&mut a, program);
2468 +
        let err = try expectError(&result);
2469 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1995 2470
1996 -
    let errNode = err.node
1997 -
        else throw testing::TestError::Failed;
1998 -
    let case ast::NodeValue::Bool(_) = errNode.value
1999 -
        else throw testing::TestError::Failed;
2471 +
        let errNode = err.node
2472 +
            else throw testing::TestError::Failed;
2473 +
        let case ast::NodeValue::Bool(_) = errNode.value
2474 +
            else throw testing::TestError::Failed;
2475 +
    }
2000 2476
}
2001 2477
2002 2478
@test unsafe fn testResolveRecordLiteralExtraField() throws (testing::TestError) {
2003 -
    let mut a = testResolver();
2004 -
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };";
2005 -
    let result = try resolveProgramStr(&mut a, program);
2006 -
    let err = try expectError(&result);
2007 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2008 -
        else throw testing::TestError::Failed;
2479 +
    let mut testArena152 = testArena();
2480 +
    let testStorage152: 'test152 = &mut testArena152 in {
2481 +
        let mut a = testResolver(testStorage152);
2482 +
        let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };";
2483 +
        let result = try resolveProgramStr(&mut a, program);
2484 +
        let err = try expectError(&result);
2485 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2486 +
            else throw testing::TestError::Failed;
2487 +
    }
2009 2488
}
2010 2489
2011 2490
/// Test that anonymous record literals with labels can be passed to functions expecting named records.
2012 2491
@test unsafe fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) {
2013 -
    let mut a = testResolver();
2014 -
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });";
2015 -
    let result = try resolveProgramStr(&mut a, program);
2016 -
    try expectNoErrors(&result);
2492 +
    let mut testArena153 = testArena();
2493 +
    let testStorage153: 'test153 = &mut testArena153 in {
2494 +
        let mut a = testResolver(testStorage153);
2495 +
        let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });";
2496 +
        let result = try resolveProgramStr(&mut a, program);
2497 +
        try expectNoErrors(&result);
2498 +
    }
2017 2499
}
2018 2500
2019 2501
/// Test that anonymous record with wrong field name causes out of order error.
2020 2502
@test unsafe fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) {
2021 -
    let mut a = testResolver();
2022 -
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });";
2023 -
    let result = try resolveProgramStr(&mut a, program);
2024 -
    let err = try expectError(&result);
2025 -
    let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind
2026 -
        else throw testing::TestError::Failed;
2503 +
    let mut testArena154 = testArena();
2504 +
    let testStorage154: 'test154 = &mut testArena154 in {
2505 +
        let mut a = testResolver(testStorage154);
2506 +
        let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });";
2507 +
        let result = try resolveProgramStr(&mut a, program);
2508 +
        let err = try expectError(&result);
2509 +
        let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind
2510 +
            else throw testing::TestError::Failed;
2511 +
    }
2027 2512
}
2028 2513
2029 2514
/// Test that anonymous record with wrong field type causes type mismatch.
2030 2515
@test unsafe fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) {
2031 -
    let mut a = testResolver();
2032 -
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });";
2033 -
    let result = try resolveProgramStr(&mut a, program);
2034 -
    let err = try expectError(&result);
2035 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
2036 -
        else throw testing::TestError::Failed;
2516 +
    let mut testArena155 = testArena();
2517 +
    let testStorage155: 'test155 = &mut testArena155 in {
2518 +
        let mut a = testResolver(testStorage155);
2519 +
        let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });";
2520 +
        let result = try resolveProgramStr(&mut a, program);
2521 +
        let err = try expectError(&result);
2522 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
2523 +
            else throw testing::TestError::Failed;
2524 +
    }
2037 2525
}
2038 2526
2039 2527
/// Test that anonymous record with missing field causes a missing field error.
2040 2528
@test unsafe fn testResolveAnonRecordMissingField() throws (testing::TestError) {
2041 -
    let mut a = testResolver();
2042 -
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });";
2043 -
    let result = try resolveProgramStr(&mut a, program);
2044 -
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
2529 +
    let mut testArena156 = testArena();
2530 +
    let testStorage156: 'test156 = &mut testArena156 in {
2531 +
        let mut a = testResolver(testStorage156);
2532 +
        let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });";
2533 +
        let result = try resolveProgramStr(&mut a, program);
2534 +
        try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
2535 +
    }
2045 2536
}
2046 2537
2047 2538
/// Test that anonymous record with extra field causes a count mismatch error.
2048 2539
@test unsafe fn testResolveAnonRecordExtraField() throws (testing::TestError) {
2049 -
    let mut a = testResolver();
2050 -
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });";
2051 -
    let result = try resolveProgramStr(&mut a, program);
2052 -
    let err = try expectError(&result);
2053 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2054 -
        else throw testing::TestError::Failed;
2540 +
    let mut testArena157 = testArena();
2541 +
    let testStorage157: 'test157 = &mut testArena157 in {
2542 +
        let mut a = testResolver(testStorage157);
2543 +
        let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });";
2544 +
        let result = try resolveProgramStr(&mut a, program);
2545 +
        let err = try expectError(&result);
2546 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2547 +
            else throw testing::TestError::Failed;
2548 +
    }
2055 2549
}
2056 2550
2057 2551
/// Test that anonymous record fields can be coerced (e.g., i32 to optional).
2058 2552
@test unsafe fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) {
2059 -
    let mut a = testResolver();
2060 -
    let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });";
2061 -
    let result = try resolveProgramStr(&mut a, program);
2062 -
    try expectNoErrors(&result);
2553 +
    let mut testArena158 = testArena();
2554 +
    let testStorage158: 'test158 = &mut testArena158 in {
2555 +
        let mut a = testResolver(testStorage158);
2556 +
        let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });";
2557 +
        let result = try resolveProgramStr(&mut a, program);
2558 +
        try expectNoErrors(&result);
2559 +
    }
2063 2560
}
2064 2561
2065 2562
/// Test that arrays of anonymous records with labeled fields are allowed.
2066 2563
@test unsafe fn testResolveAnonRecordArray() throws (testing::TestError) {
2067 -
    let mut a = testResolver();
2068 -
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];";
2069 -
    let result = try resolveProgramStr(&mut a, program);
2070 -
    try expectNoErrors(&result);
2564 +
    let mut testArena159 = testArena();
2565 +
    let testStorage159: 'test159 = &mut testArena159 in {
2566 +
        let mut a = testResolver(testStorage159);
2567 +
        let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];";
2568 +
        let result = try resolveProgramStr(&mut a, program);
2569 +
        try expectNoErrors(&result);
2570 +
    }
2071 2571
}
2072 2572
2073 2573
/// Test that arrays of anonymous records with extra fields cause count mismatch.
2074 2574
@test unsafe fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) {
2075 -
    let mut a = testResolver();
2076 -
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];";
2077 -
    let result = try resolveProgramStr(&mut a, program);
2078 -
    let err = try expectError(&result);
2079 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2080 -
        else throw testing::TestError::Failed;
2575 +
    let mut testArena160 = testArena();
2576 +
    let testStorage160: 'test160 = &mut testArena160 in {
2577 +
        let mut a = testResolver(testStorage160);
2578 +
        let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];";
2579 +
        let result = try resolveProgramStr(&mut a, program);
2580 +
        let err = try expectError(&result);
2581 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2582 +
            else throw testing::TestError::Failed;
2583 +
    }
2081 2584
}
2082 2585
2083 2586
/// Test that unlabeled record declarations are analyzed correctly.
2084 2587
@test unsafe fn testResolveUnlabeledRecordDecl() throws (testing::TestError) {
2085 -
    let mut a = testResolver();
2086 -
    let program = "record R(i32, bool);";
2087 -
    let result = try resolveProgramStr(&mut a, program);
2088 -
    try expectNoErrors(&result);
2089 -
2090 -
    // Verify the type symbol was created with labeled=false.
2091 -
    let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2092 -
    let case super::NominalType::Record(recordType) = *nominalTy
2093 -
        else throw testing::TestError::Failed;
2094 -
    try testing::expect(not recordType.labeled);
2095 -
    try testing::expect(recordType.fields.len == 2);
2096 -
    try testing::expect(recordType.fields[0].name == nil);
2097 -
    try testing::expect(recordType.fields[1].name == nil);
2588 +
    let mut testArena161 = testArena();
2589 +
    let testStorage161: 'test161 = &mut testArena161 in {
2590 +
        let mut a = testResolver(testStorage161);
2591 +
        let program = "record R(i32, bool);";
2592 +
        let result = try resolveProgramStr(&mut a, program);
2593 +
        try expectNoErrors(&result);
2594 +
2595 +
        // Verify the type symbol was created with labeled=false.
2596 +
        let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2597 +
        let case super::NominalType::Record(recordType) = *nominalTy
2598 +
            else throw testing::TestError::Failed;
2599 +
        try testing::expect(not recordType.labeled);
2600 +
        try testing::expect(recordType.fields.len == 2);
2601 +
        try testing::expect(recordType.fields[0].name == nil);
2602 +
        try testing::expect(recordType.fields[1].name == nil);
2603 +
    }
2098 2604
}
2099 2605
2100 2606
@test unsafe fn testResolveLabeledRecordDecl() throws (testing::TestError) {
2101 -
    let mut a = testResolver();
2102 -
    let program = "record R { x: i32, y: i32 }";
2103 -
    let result = try resolveProgramStr(&mut a, program);
2104 -
    try expectNoErrors(&result);
2607 +
    let mut testArena162 = testArena();
2608 +
    let testStorage162: 'test162 = &mut testArena162 in {
2609 +
        let mut a = testResolver(testStorage162);
2610 +
        let program = "record R { x: i32, y: i32 }";
2611 +
        let result = try resolveProgramStr(&mut a, program);
2612 +
        try expectNoErrors(&result);
2105 2613
2106 -
    let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2107 -
    let case super::NominalType::Record(recordType) = *nominalTy
2108 -
        else throw testing::TestError::Failed;
2109 -
    try testing::expect(recordType.labeled);
2110 -
    try testing::expect(recordType.fields.len == 2);
2111 -
    try testing::expect(recordType.fields[0].name <> nil);
2112 -
    try testing::expect(recordType.fields[1].name <> nil);
2614 +
        let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2615 +
        let case super::NominalType::Record(recordType) = *nominalTy
2616 +
            else throw testing::TestError::Failed;
2617 +
        try testing::expect(recordType.labeled);
2618 +
        try testing::expect(recordType.fields.len == 2);
2619 +
        try testing::expect(recordType.fields[0].name <> nil);
2620 +
        try testing::expect(recordType.fields[1].name <> nil);
2621 +
    }
2113 2622
}
2114 2623
2115 2624
@test unsafe fn testResolveRecordFieldAccessValid() throws (testing::TestError) {
2116 -
    let mut a = testResolver();
2117 -
    let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;";
2118 -
    let result = try resolveProgramStr(&mut a, program);
2119 -
    try expectNoErrors(&result);
2625 +
    let mut testArena163 = testArena();
2626 +
    let testStorage163: 'test163 = &mut testArena163 in {
2627 +
        let mut a = testResolver(testStorage163);
2628 +
        let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;";
2629 +
        let result = try resolveProgramStr(&mut a, program);
2630 +
        try expectNoErrors(&result);
2120 2631
2121 -
    let fieldStmt = try getBlockStmt(result.root, 2);
2122 -
    try expectExprStmtType(&a, fieldStmt, super::Type::U8);
2632 +
        let fieldStmt = try getBlockStmt(result.root, 2);
2633 +
        try expectExprStmtType(&a, fieldStmt, super::Type::U8);
2634 +
    }
2123 2635
}
2124 2636
2125 2637
@test unsafe fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) {
2126 -
    let mut a = testResolver();
2127 -
    let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;";
2128 -
    let result = try resolveProgramStr(&mut a, program);
2129 -
    try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y"));
2638 +
    let mut testArena164 = testArena();
2639 +
    let testStorage164: 'test164 = &mut testArena164 in {
2640 +
        let mut a = testResolver(testStorage164);
2641 +
        let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;";
2642 +
        let result = try resolveProgramStr(&mut a, program);
2643 +
        try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y"));
2644 +
    }
2130 2645
}
2131 2646
2132 2647
@test unsafe fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) {
2133 -
    let mut a = testResolver();
2134 -
    let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;";
2135 -
    let result = try resolveProgramStr(&mut a, program);
2136 -
    try expectNoErrors(&result);
2648 +
    let mut testArena165 = testArena();
2649 +
    let testStorage165: 'test165 = &mut testArena165 in {
2650 +
        let mut a = testResolver(testStorage165);
2651 +
        let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;";
2652 +
        let result = try resolveProgramStr(&mut a, program);
2653 +
        try expectNoErrors(&result);
2137 2654
2138 -
    let stmt = try getBlockStmt(result.root, 2);
2139 -
    try expectExprStmtType(&a, stmt, super::Type::I32);
2655 +
        let stmt = try getBlockStmt(result.root, 2);
2656 +
        try expectExprStmtType(&a, stmt, super::Type::I32);
2657 +
    }
2140 2658
}
2141 2659
2142 2660
@test unsafe fn testResolveRecordFieldAccessChained() throws (testing::TestError) {
2143 -
    let mut a = testResolver();
2144 -
    let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;";
2145 -
    let result = try resolveProgramStr(&mut a, program);
2146 -
    try expectNoErrors(&result);
2661 +
    let mut testArena166 = testArena();
2662 +
    let testStorage166: 'test166 = &mut testArena166 in {
2663 +
        let mut a = testResolver(testStorage166);
2664 +
        let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;";
2665 +
        let result = try resolveProgramStr(&mut a, program);
2666 +
        try expectNoErrors(&result);
2147 2667
2148 -
    let stmt = try getBlockStmt(result.root, 4);
2149 -
    try expectExprStmtType(&a, stmt, super::Type::I32);
2668 +
        let stmt = try getBlockStmt(result.root, 4);
2669 +
        try expectExprStmtType(&a, stmt, super::Type::I32);
2670 +
    }
2150 2671
}
2151 2672
2152 2673
@test unsafe fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) {
2153 -
    let mut a = testResolver();
2154 -
    let program = "let x: i32 = 42; x.field;";
2155 -
    let result = try resolveBlockStr(&mut a, program);
2156 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2674 +
    let mut testArena167 = testArena();
2675 +
    let testStorage167: 'test167 = &mut testArena167 in {
2676 +
        let mut a = testResolver(testStorage167);
2677 +
        let program = "let x: i32 = 42; x.field;";
2678 +
        let result = try resolveBlockStr(&mut a, program);
2679 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2680 +
    }
2157 2681
}
2158 2682
2159 2683
@test unsafe fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) {
2160 -
    let mut a = testResolver();
2161 -
    let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;";
2162 -
    let result = try resolveProgramStr(&mut a, program);
2163 -
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
2684 +
    let mut testArena168 = testArena();
2685 +
    let testStorage168: 'test168 = &mut testArena168 in {
2686 +
        let mut a = testResolver(testStorage168);
2687 +
        let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;";
2688 +
        let result = try resolveProgramStr(&mut a, program);
2689 +
        try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
2690 +
    }
2164 2691
}
2165 2692
2166 2693
@test unsafe fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) {
2167 -
    let mut a = testResolver();
2168 -
    let program = "let b: bool = true; b.field;";
2169 -
    let result = try resolveProgramStr(&mut a, program);
2170 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2694 +
    let mut testArena169 = testArena();
2695 +
    let testStorage169: 'test169 = &mut testArena169 in {
2696 +
        let mut a = testResolver(testStorage169);
2697 +
        let program = "let b: bool = true; b.field;";
2698 +
        let result = try resolveProgramStr(&mut a, program);
2699 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2700 +
    }
2171 2701
}
2172 2702
2173 2703
@test unsafe fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) {
2174 -
    let mut a = testResolver();
2175 -
    let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;";
2176 -
    let result = try resolveProgramStr(&mut a, program);
2177 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2704 +
    let mut testArena170 = testArena();
2705 +
    let testStorage170: 'test170 = &mut testArena170 in {
2706 +
        let mut a = testResolver(testStorage170);
2707 +
        let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;";
2708 +
        let result = try resolveProgramStr(&mut a, program);
2709 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2710 +
    }
2178 2711
}
2179 2712
2180 2713
/// Records may reference themselves through pointers without causing resolution errors.
2181 2714
@test unsafe fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) {
2182 -
    let mut a = testResolver();
2183 -
    let program = "record A { next: *A }";
2184 -
    let result = try resolveProgramStr(&mut a, program);
2185 -
    try expectNoErrors(&result);
2715 +
    let mut testArena171 = testArena();
2716 +
    let testStorage171: 'test171 = &mut testArena171 in {
2717 +
        let mut a = testResolver(testStorage171);
2718 +
        let program = "record A { next: *A }";
2719 +
        let result = try resolveProgramStr(&mut a, program);
2720 +
        try expectNoErrors(&result);
2721 +
    }
2186 2722
}
2187 2723
2188 2724
/// Mutually recursive records should resolve without infinite loops.
2189 2725
@test unsafe fn testResolveRecordMutuallyRecursive() throws (testing::TestError) {
2190 -
    let mut a = testResolver();
2191 -
    let program = "record A { b: *B } record B { a: *A }";
2192 -
    let result = try resolveProgramStr(&mut a, program);
2193 -
    try expectNoErrors(&result);
2726 +
    let mut testArena172 = testArena();
2727 +
    let testStorage172: 'test172 = &mut testArena172 in {
2728 +
        let mut a = testResolver(testStorage172);
2729 +
        let program = "record A { b: *B } record B { a: *A }";
2730 +
        let result = try resolveProgramStr(&mut a, program);
2731 +
        try expectNoErrors(&result);
2732 +
    }
2194 2733
}
2195 2734
2196 2735
/// Unions may reference themselves through pointers without causing resolution errors.
2197 2736
@test unsafe fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) {
2198 -
    let mut a = testResolver();
2199 -
    let program = "union List { Cons(*List), Nil }";
2200 -
    let result = try resolveProgramStr(&mut a, program);
2201 -
    try expectNoErrors(&result);
2737 +
    let mut testArena173 = testArena();
2738 +
    let testStorage173: 'test173 = &mut testArena173 in {
2739 +
        let mut a = testResolver(testStorage173);
2740 +
        let program = "union List { Cons(*List), Nil }";
2741 +
        let result = try resolveProgramStr(&mut a, program);
2742 +
        try expectNoErrors(&result);
2743 +
    }
2202 2744
}
2203 2745
2204 2746
/// Mutually recursive unions should resolve without infinite loops.
2205 2747
@test unsafe fn testResolveUnionMutuallyRecursive() throws (testing::TestError) {
2206 -
    let mut a = testResolver();
2207 -
    let program = "union A { HasB(*B), None } union B { HasA(*A), None }";
2208 -
    let result = try resolveProgramStr(&mut a, program);
2209 -
    try expectNoErrors(&result);
2748 +
    let mut testArena174 = testArena();
2749 +
    let testStorage174: 'test174 = &mut testArena174 in {
2750 +
        let mut a = testResolver(testStorage174);
2751 +
        let program = "union A { HasB(*B), None } union B { HasA(*A), None }";
2752 +
        let result = try resolveProgramStr(&mut a, program);
2753 +
        try expectNoErrors(&result);
2754 +
    }
2210 2755
}
2211 2756
2212 2757
/// Unions with record payloads containing slice references to self should resolve.
2213 2758
/// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`.
2214 2759
@test unsafe fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) {
2215 -
    let mut a = testResolver();
2216 -
    let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }";
2217 -
    let result = try resolveProgramStr(&mut a, program);
2218 -
    try expectNoErrors(&result);
2760 +
    let mut testArena175 = testArena();
2761 +
    let testStorage175: 'test175 = &mut testArena175 in {
2762 +
        let mut a = testResolver(testStorage175);
2763 +
        let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }";
2764 +
        let result = try resolveProgramStr(&mut a, program);
2765 +
        try expectNoErrors(&result);
2766 +
    }
2219 2767
}
2220 2768
2221 2769
@test unsafe fn testUndefinedCoercions() throws (testing::TestError) {
2222 2770
    {
2223 -
        let mut a = testResolver();
2224 -
        let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }");
2225 -
        try expectNoErrors(&result);
2771 +
        let mut testArena176 = testArena();
2772 +
        let testStorage176: 'test176 = &mut testArena176 in {
2773 +
            let mut a = testResolver(testStorage176);
2774 +
            let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }");
2775 +
            try expectNoErrors(&result);
2776 +
        }
2226 2777
    } {
2227 -
        let mut a = testResolver();
2228 -
        let program = "unsafe { let mut value: i32 = 0; set value = undefined; }";
2229 -
        let result = try resolveProgramStr(&mut a, program);
2230 -
        try expectNoErrors(&result);
2778 +
        let mut testArena177 = testArena();
2779 +
        let testStorage177: 'test177 = &mut testArena177 in {
2780 +
            let mut a = testResolver(testStorage177);
2781 +
            let program = "unsafe { let mut value: i32 = 0; set value = undefined; }";
2782 +
            let result = try resolveProgramStr(&mut a, program);
2783 +
            try expectNoErrors(&result);
2784 +
        }
2231 2785
    } {
2232 -
        let mut a = testResolver();
2233 -
        let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }";
2234 -
        let result = try resolveProgramStr(&mut a, program);
2235 -
        try expectNoErrors(&result);
2786 +
        let mut testArena178 = testArena();
2787 +
        let testStorage178: 'test178 = &mut testArena178 in {
2788 +
            let mut a = testResolver(testStorage178);
2789 +
            let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }";
2790 +
            let result = try resolveProgramStr(&mut a, program);
2791 +
            try expectNoErrors(&result);
2792 +
        }
2236 2793
    } {
2237 -
        let mut a = testResolver();
2238 -
        let program = "unsafe fn fetch() -> i32 { return undefined; }";
2239 -
        let result = try resolveProgramStr(&mut a, program);
2240 -
        try expectNoErrors(&result);
2794 +
        let mut testArena179 = testArena();
2795 +
        let testStorage179: 'test179 = &mut testArena179 in {
2796 +
            let mut a = testResolver(testStorage179);
2797 +
            let program = "unsafe fn fetch() -> i32 { return undefined; }";
2798 +
            let result = try resolveProgramStr(&mut a, program);
2799 +
            try expectNoErrors(&result);
2800 +
        }
2241 2801
    }
2242 2802
}
2243 2803
2244 2804
@test unsafe fn testResolveBlockVoid() throws (testing::TestError) {
2245 -
    let mut a = testResolver();
2246 -
    let result = try resolveProgramStr(&mut a, "{ 42; }");
2247 -
    try expectNoErrors(&result);
2805 +
    let mut testArena180 = testArena();
2806 +
    let testStorage180: 'test180 = &mut testArena180 in {
2807 +
        let mut a = testResolver(testStorage180);
2808 +
        let result = try resolveProgramStr(&mut a, "{ 42; }");
2809 +
        try expectNoErrors(&result);
2248 2810
2249 -
    let block = try getBlockStmt(result.root, 0);
2250 -
    try expectType(&a, block, super::Type::Void);
2811 +
        let block = try getBlockStmt(result.root, 0);
2812 +
        try expectType(&a, block, super::Type::Void);
2813 +
    }
2251 2814
}
2252 2815
2253 2816
@test unsafe fn testResolveBlockNever() throws (testing::TestError) {
2254 -
    let mut a = testResolver();
2255 -
    let result = try resolveProgramStr(&mut a, "{ panic; }");
2256 -
    try expectNoErrors(&result);
2817 +
    let mut testArena181 = testArena();
2818 +
    let testStorage181: 'test181 = &mut testArena181 in {
2819 +
        let mut a = testResolver(testStorage181);
2820 +
        let result = try resolveProgramStr(&mut a, "{ panic; }");
2821 +
        try expectNoErrors(&result);
2257 2822
2258 -
    let block = try getBlockStmt(result.root, 0);
2259 -
    try expectType(&a, block, super::Type::Never);
2823 +
        let block = try getBlockStmt(result.root, 0);
2824 +
        try expectType(&a, block, super::Type::Never);
2825 +
    }
2260 2826
}
2261 2827
2262 2828
@test unsafe fn testResolveIfAllBranchesNever() throws (testing::TestError) {
2263 -
    let mut a = testResolver();
2264 -
    let program = "if true { panic; } else { panic; }";
2265 -
    let result = try resolveProgramStr(&mut a, program);
2266 -
    try expectNoErrors(&result);
2829 +
    let mut testArena182 = testArena();
2830 +
    let testStorage182: 'test182 = &mut testArena182 in {
2831 +
        let mut a = testResolver(testStorage182);
2832 +
        let program = "if true { panic; } else { panic; }";
2833 +
        let result = try resolveProgramStr(&mut a, program);
2834 +
        try expectNoErrors(&result);
2267 2835
2268 -
    let stmt = try getBlockStmt(result.root, 0);
2269 -
    try expectType(&a, stmt, super::Type::Never);
2836 +
        let stmt = try getBlockStmt(result.root, 0);
2837 +
        try expectType(&a, stmt, super::Type::Never);
2838 +
    }
2270 2839
}
2271 2840
2272 2841
@test unsafe fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) {
2273 -
    let mut a = testResolver();
2274 -
    let program = "if true { panic; } else {}";
2275 -
    let result = try resolveProgramStr(&mut a, program);
2276 -
    try expectNoErrors(&result);
2842 +
    let mut testArena183 = testArena();
2843 +
    let testStorage183: 'test183 = &mut testArena183 in {
2844 +
        let mut a = testResolver(testStorage183);
2845 +
        let program = "if true { panic; } else {}";
2846 +
        let result = try resolveProgramStr(&mut a, program);
2847 +
        try expectNoErrors(&result);
2277 2848
2278 -
    let stmt = try getBlockStmt(result.root, 0);
2279 -
    try expectType(&a, stmt, super::Type::Void);
2849 +
        let stmt = try getBlockStmt(result.root, 0);
2850 +
        try expectType(&a, stmt, super::Type::Void);
2851 +
    }
2280 2852
}
2281 2853
2282 2854
@test unsafe fn testResolveLetElse() throws (testing::TestError) {
2283 -
    let mut a = testResolver();
2284 -
    let program = "let opt: ?i32 = 42; let value = opt else panic; value;";
2285 -
    let result = try resolveProgramStr(&mut a, program);
2286 -
    try expectNoErrors(&result);
2855 +
    let mut testArena184 = testArena();
2856 +
    let testStorage184: 'test184 = &mut testArena184 in {
2857 +
        let mut a = testResolver(testStorage184);
2858 +
        let program = "let opt: ?i32 = 42; let value = opt else panic; value;";
2859 +
        let result = try resolveProgramStr(&mut a, program);
2860 +
        try expectNoErrors(&result);
2287 2861
2288 -
    let blockNode = result.root;
2289 -
    let case ast::NodeValue::Block(block) = blockNode.value
2290 -
        else throw testing::TestError::Failed;
2291 -
    let letElseNode = try getBlockStmt(blockNode, 1);
2292 -
    let valueStmt = try getBlockStmt(blockNode, 2);
2862 +
        let blockNode = result.root;
2863 +
        let case ast::NodeValue::Block(block) = blockNode.value
2864 +
            else throw testing::TestError::Failed;
2865 +
        let letElseNode = try getBlockStmt(blockNode, 1);
2866 +
        let valueStmt = try getBlockStmt(blockNode, 2);
2293 2867
2294 -
    { // Ensure the bound identifier receives the inner optional type.
2295 -
        let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32);
2868 +
        { // Ensure the bound identifier receives the inner optional type.
2869 +
            let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32);
2296 2870
2297 -
        let sym = super::symbolFor(&a, valueExpr)
2298 -
            else throw testing::TestError::Failed;
2299 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
2300 -
            else throw testing::TestError::Failed;
2301 -
        try testing::expect(valType == super::Type::I32);
2871 +
            let sym = super::symbolFor(&a, valueExpr)
2872 +
                else throw testing::TestError::Failed;
2873 +
            let case super::SymbolData::Value { type: valType, .. } = sym.data
2874 +
                else throw testing::TestError::Failed;
2875 +
            try testing::expect(valType == super::Type::I32);
2876 +
        }
2877 +
        // The let-else statement itself should be typed as void.
2878 +
        try expectType(&a, letElseNode, super::Type::Void);
2302 2879
    }
2303 -
    // The let-else statement itself should be typed as void.
2304 -
    try expectType(&a, letElseNode, super::Type::Void);
2305 2880
}
2306 2881
2307 2882
@test unsafe fn testResolveLetElseDefaultValue() throws (testing::TestError) {
2308 -
    let mut a = testResolver();
2309 -
    let program = "let opt: ?i32 = nil; let value = opt else 42; value;";
2310 -
    let result = try resolveProgramStr(&mut a, program);
2311 -
    try expectNoErrors(&result);
2883 +
    let mut testArena185 = testArena();
2884 +
    let testStorage185: 'test185 = &mut testArena185 in {
2885 +
        let mut a = testResolver(testStorage185);
2886 +
        let program = "let opt: ?i32 = nil; let value = opt else 42; value;";
2887 +
        let result = try resolveProgramStr(&mut a, program);
2888 +
        try expectNoErrors(&result);
2889 +
    }
2312 2890
}
2313 2891
2314 2892
@test unsafe fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) {
2315 -
    let mut a = testResolver();
2316 -
    let program = "let opt: ?i32 = nil; let value = opt else {}; value;";
2317 -
    let result = try resolveProgramStr(&mut a, program);
2318 -
    let err = try expectError(&result);
2319 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2893 +
    let mut testArena186 = testArena();
2894 +
    let testStorage186: 'test186 = &mut testArena186 in {
2895 +
        let mut a = testResolver(testStorage186);
2896 +
        let program = "let opt: ?i32 = nil; let value = opt else {}; value;";
2897 +
        let result = try resolveProgramStr(&mut a, program);
2898 +
        let err = try expectError(&result);
2899 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2900 +
    }
2320 2901
}
2321 2902
2322 2903
@test unsafe fn testResolveLetElseRequiresOptional() throws (testing::TestError) {
2323 -
    let mut a = testResolver();
2324 -
    let program = "let x: i32 = 42; let value = x else panic;";
2325 -
    let result = try resolveProgramStr(&mut a, program);
2326 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedOptional);
2904 +
    let mut testArena187 = testArena();
2905 +
    let testStorage187: 'test187 = &mut testArena187 in {
2906 +
        let mut a = testResolver(testStorage187);
2907 +
        let program = "let x: i32 = 42; let value = x else panic;";
2908 +
        let result = try resolveProgramStr(&mut a, program);
2909 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedOptional);
2910 +
    }
2327 2911
}
2328 2912
2329 2913
/// Test that `if let mut` produces a mutable binding.
2330 2914
@test unsafe fn testResolveIfLetMut() throws (testing::TestError) {
2331 -
    let mut a = testResolver();
2332 -
    let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }";
2333 -
    let result = try resolveProgramStr(&mut a, program);
2334 -
    try expectNoErrors(&result);
2915 +
    let mut testArena188 = testArena();
2916 +
    let testStorage188: 'test188 = &mut testArena188 in {
2917 +
        let mut a = testResolver(testStorage188);
2918 +
        let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }";
2919 +
        let result = try resolveProgramStr(&mut a, program);
2920 +
        try expectNoErrors(&result);
2921 +
    }
2335 2922
}
2336 2923
2337 2924
/// Test that `if let` (without mut) rejects assignment.
2338 2925
@test unsafe fn testResolveIfLetImmutable() throws (testing::TestError) {
2339 -
    let mut a = testResolver();
2340 -
    let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }";
2341 -
    let result = try resolveProgramStr(&mut a, program);
2342 -
    let err = try expectError(&result);
2343 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2926 +
    let mut testArena189 = testArena();
2927 +
    let testStorage189: 'test189 = &mut testArena189 in {
2928 +
        let mut a = testResolver(testStorage189);
2929 +
        let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }";
2930 +
        let result = try resolveProgramStr(&mut a, program);
2931 +
        let err = try expectError(&result);
2932 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2933 +
    }
2344 2934
}
2345 2935
2346 2936
/// Test that `let mut ... else` produces a mutable binding.
2347 2937
@test unsafe fn testResolveLetMutElse() throws (testing::TestError) {
2348 -
    let mut a = testResolver();
2349 -
    let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;";
2350 -
    let result = try resolveProgramStr(&mut a, program);
2351 -
    try expectNoErrors(&result);
2938 +
    let mut testArena190 = testArena();
2939 +
    let testStorage190: 'test190 = &mut testArena190 in {
2940 +
        let mut a = testResolver(testStorage190);
2941 +
        let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;";
2942 +
        let result = try resolveProgramStr(&mut a, program);
2943 +
        try expectNoErrors(&result);
2944 +
    }
2352 2945
}
2353 2946
2354 2947
/// Test that `let ... else` (without mut) rejects assignment.
2355 2948
@test unsafe fn testResolveLetElseImmutable() throws (testing::TestError) {
2356 -
    let mut a = testResolver();
2357 -
    let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;";
2358 -
    let result = try resolveProgramStr(&mut a, program);
2359 -
    let err = try expectError(&result);
2360 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2949 +
    let mut testArena191 = testArena();
2950 +
    let testStorage191: 'test191 = &mut testArena191 in {
2951 +
        let mut a = testResolver(testStorage191);
2952 +
        let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;";
2953 +
        let result = try resolveProgramStr(&mut a, program);
2954 +
        let err = try expectError(&result);
2955 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2956 +
    }
2361 2957
}
2362 2958
2363 2959
@test unsafe fn testResolveLetCaseElse() throws (testing::TestError) {
2364 2960
    {
2365 -
        let mut a = testResolver();
2366 -
        let program = "let case _ = 1 else panic;";
2367 -
        let result = try resolveProgramStr(&mut a, program);
2368 -
        try expectNoErrors(&result);
2961 +
        let mut testArena192 = testArena();
2962 +
        let testStorage192: 'test192 = &mut testArena192 in {
2963 +
            let mut a = testResolver(testStorage192);
2964 +
            let program = "let case _ = 1 else panic;";
2965 +
            let result = try resolveProgramStr(&mut a, program);
2966 +
            try expectNoErrors(&result);
2967 +
        }
2369 2968
    } {
2370 -
        let mut a = testResolver();
2371 -
        let program = "let case _ = true else false;";
2372 -
        let result = try resolveProgramStr(&mut a, program);
2373 -
        try expectNoErrors(&result);
2969 +
        let mut testArena193 = testArena();
2970 +
        let testStorage193: 'test193 = &mut testArena193 in {
2971 +
            let mut a = testResolver(testStorage193);
2972 +
            let program = "let case _ = true else false;";
2973 +
            let result = try resolveProgramStr(&mut a, program);
2974 +
            try expectNoErrors(&result);
2975 +
        }
2374 2976
    }
2375 2977
}
2376 2978
2377 2979
@test unsafe fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) {
2378 -
    let mut a = testResolver();
2379 -
    let program = "let case _ = 1 else {};";
2380 -
    let result = try resolveProgramStr(&mut a, program);
2381 -
    let err = try expectError(&result);
2382 -
    try expectTypeMismatch(err, super::Type::Int, super::Type::Void);
2980 +
    let mut testArena194 = testArena();
2981 +
    let testStorage194: 'test194 = &mut testArena194 in {
2982 +
        let mut a = testResolver(testStorage194);
2983 +
        let program = "let case _ = 1 else {};";
2984 +
        let result = try resolveProgramStr(&mut a, program);
2985 +
        let err = try expectError(&result);
2986 +
        try expectTypeMismatch(err, super::Type::Int, super::Type::Void);
2987 +
    }
2383 2988
}
2384 2989
2385 2990
@test unsafe fn testResolveTryValidPropagation() throws (testing::TestError) {
2386 -
    let mut a = testResolver();
2387 -
    let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }";
2388 -
    let result = try resolveProgramStr(&mut a, program);
2389 -
    try expectNoErrors(&result);
2991 +
    let mut testArena195 = testArena();
2992 +
    let testStorage195: 'test195 = &mut testArena195 in {
2993 +
        let mut a = testResolver(testStorage195);
2994 +
        let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }";
2995 +
        let result = try resolveProgramStr(&mut a, program);
2996 +
        try expectNoErrors(&result);
2997 +
    }
2390 2998
}
2391 2999
2392 3000
@test unsafe fn testResolveTryRequiresThrowsClause() throws (testing::TestError) {
2393 -
    let mut a = testResolver();
2394 -
    let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }";
2395 -
    let result = try resolveProgramStr(&mut a, program);
2396 -
    try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows);
3001 +
    let mut testArena196 = testArena();
3002 +
    let testStorage196: 'test196 = &mut testArena196 in {
3003 +
        let mut a = testResolver(testStorage196);
3004 +
        let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }";
3005 +
        let result = try resolveProgramStr(&mut a, program);
3006 +
        try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows);
3007 +
    }
2397 3008
}
2398 3009
2399 3010
@test unsafe fn testResolveTryIncompatibleError() throws (testing::TestError) {
2400 -
    let mut a = testResolver();
2401 -
    let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }";
2402 -
    let result = try resolveProgramStr(&mut a, program);
2403 -
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
3011 +
    let mut testArena197 = testArena();
3012 +
    let testStorage197: 'test197 = &mut testArena197 in {
3013 +
        let mut a = testResolver(testStorage197);
3014 +
        let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }";
3015 +
        let result = try resolveProgramStr(&mut a, program);
3016 +
        try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
3017 +
    }
2404 3018
}
2405 3019
2406 3020
@test unsafe fn testResolveTryNonThrowing() throws (testing::TestError) {
2407 -
    let mut a = testResolver();
2408 -
    let program = "fn safe() {} fn caller() throws (i32) { try safe() }";
2409 -
    let result = try resolveProgramStr(&mut a, program);
2410 -
    try expectErrorKind(&result, super::ErrorKind::TryNonThrowing);
3021 +
    let mut testArena198 = testArena();
3022 +
    let testStorage198: 'test198 = &mut testArena198 in {
3023 +
        let mut a = testResolver(testStorage198);
3024 +
        let program = "fn safe() {} fn caller() throws (i32) { try safe() }";
3025 +
        let result = try resolveProgramStr(&mut a, program);
3026 +
        try expectErrorKind(&result, super::ErrorKind::TryNonThrowing);
3027 +
    }
2411 3028
}
2412 3029
2413 3030
@test unsafe fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) {
2414 -
    let mut a = testResolver();
2415 -
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }";
2416 -
    let result = try resolveProgramStr(&mut a, program);
2417 -
    try expectNoErrors(&result);
3031 +
    let mut testArena199 = testArena();
3032 +
    let testStorage199: 'test199 = &mut testArena199 in {
3033 +
        let mut a = testResolver(testStorage199);
3034 +
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }";
3035 +
        let result = try resolveProgramStr(&mut a, program);
3036 +
        try expectNoErrors(&result);
3037 +
    }
2418 3038
}
2419 3039
2420 3040
@test unsafe fn testResolveTryCatchBlockDiverges() throws (testing::TestError) {
2421 -
    let mut a = testResolver();
2422 -
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }";
2423 -
    let result = try resolveProgramStr(&mut a, program);
2424 -
    try expectNoErrors(&result);
3041 +
    let mut testArena200 = testArena();
3042 +
    let testStorage200: 'test200 = &mut testArena200 in {
3043 +
        let mut a = testResolver(testStorage200);
3044 +
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }";
3045 +
        let result = try resolveProgramStr(&mut a, program);
3046 +
        try expectNoErrors(&result);
3047 +
    }
2425 3048
}
2426 3049
2427 3050
@test unsafe fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) {
2428 -
    let mut a = testResolver();
2429 -
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }";
2430 -
    let result = try resolveProgramStr(&mut a, program);
2431 -
    let err = try expectError(&result);
2432 -
    try expectTypeMismatch(err, super::Type::U32, super::Type::Void);
3051 +
    let mut testArena201 = testArena();
3052 +
    let testStorage201: 'test201 = &mut testArena201 in {
3053 +
        let mut a = testResolver(testStorage201);
3054 +
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }";
3055 +
        let result = try resolveProgramStr(&mut a, program);
3056 +
        let err = try expectError(&result);
3057 +
        try expectTypeMismatch(err, super::Type::U32, super::Type::Void);
3058 +
    }
2433 3059
}
2434 3060
2435 3061
@test unsafe fn testResolveCallMissingTry() throws (testing::TestError) {
2436 -
    let mut a = testResolver();
2437 -
    let program = "fn fallible() throws (i32) {} fn caller() { fallible() }";
2438 -
    let result = try resolveProgramStr(&mut a, program);
2439 -
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
3062 +
    let mut testArena202 = testArena();
3063 +
    let testStorage202: 'test202 = &mut testArena202 in {
3064 +
        let mut a = testResolver(testStorage202);
3065 +
        let program = "fn fallible() throws (i32) {} fn caller() { fallible() }";
3066 +
        let result = try resolveProgramStr(&mut a, program);
3067 +
        try expectErrorKind(&result, super::ErrorKind::MissingTry);
3068 +
    }
2440 3069
}
2441 3070
2442 3071
/// Test that `try?` converts errors to optionals without requiring caller to throw.
2443 3072
@test unsafe fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) {
2444 3073
    // `try?` should wrap the return type in optional and not require caller to throw.
2445 3074
    {
2446 -
        let mut a = testResolver();
2447 -
        let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }";
2448 -
        let result = try resolveProgramStr(&mut a, program);
2449 -
        try expectNoErrors(&result);
3075 +
        let mut testArena203 = testArena();
3076 +
        let testStorage203: 'test203 = &mut testArena203 in {
3077 +
            let mut a = testResolver(testStorage203);
3078 +
            let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }";
3079 +
            let result = try resolveProgramStr(&mut a, program);
3080 +
            try expectNoErrors(&result);
3081 +
        }
2450 3082
    }
2451 3083
    // `try?` works in non-throwing function.
2452 3084
    {
2453 -
        let mut a = testResolver();
2454 -
        let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }";
2455 -
        let result = try resolveProgramStr(&mut a, program);
2456 -
        try expectNoErrors(&result);
3085 +
        let mut testArena204 = testArena();
3086 +
        let testStorage204: 'test204 = &mut testArena204 in {
3087 +
            let mut a = testResolver(testStorage204);
3088 +
            let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }";
3089 +
            let result = try resolveProgramStr(&mut a, program);
3090 +
            try expectNoErrors(&result);
3091 +
        }
2457 3092
    }
2458 3093
    // `try?` can be used in if-let patterns.
2459 3094
    {
2460 -
        let mut a = testResolver();
2461 -
        let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }";
2462 -
        let result = try resolveProgramStr(&mut a, program);
2463 -
        try expectNoErrors(&result);
3095 +
        let mut testArena205 = testArena();
3096 +
        let testStorage205: 'test205 = &mut testArena205 in {
3097 +
            let mut a = testResolver(testStorage205);
3098 +
            let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }";
3099 +
            let result = try resolveProgramStr(&mut a, program);
3100 +
            try expectNoErrors(&result);
3101 +
        }
2464 3102
    }
2465 3103
}
2466 3104
2467 3105
@test unsafe fn testResolveThrowValid() throws (testing::TestError) {
2468 -
    let mut a = testResolver();
2469 -
    let program = "fn fail() throws (i32) { throw 1; }";
2470 -
    let result = try resolveProgramStr(&mut a, program);
2471 -
    try expectNoErrors(&result);
3106 +
    let mut testArena206 = testArena();
3107 +
    let testStorage206: 'test206 = &mut testArena206 in {
3108 +
        let mut a = testResolver(testStorage206);
3109 +
        let program = "fn fail() throws (i32) { throw 1; }";
3110 +
        let result = try resolveProgramStr(&mut a, program);
3111 +
        try expectNoErrors(&result);
3112 +
    }
2472 3113
}
2473 3114
2474 3115
@test unsafe fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) {
2475 -
    let mut a = testResolver();
2476 -
    let program = "fn fail() { throw 1; }";
2477 -
    let result = try resolveProgramStr(&mut a, program);
2478 -
    try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows);
3116 +
    let mut testArena207 = testArena();
3117 +
    let testStorage207: 'test207 = &mut testArena207 in {
3118 +
        let mut a = testResolver(testStorage207);
3119 +
        let program = "fn fail() { throw 1; }";
3120 +
        let result = try resolveProgramStr(&mut a, program);
3121 +
        try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows);
3122 +
    }
2479 3123
}
2480 3124
2481 3125
@test unsafe fn testResolveThrowIncompatibleError() throws (testing::TestError) {
2482 -
    let mut a = testResolver();
2483 -
    let program = "fn fail() throws (i32) { throw true; }";
2484 -
    let result = try resolveProgramStr(&mut a, program);
2485 -
    try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError);
3126 +
    let mut testArena208 = testArena();
3127 +
    let testStorage208: 'test208 = &mut testArena208 in {
3128 +
        let mut a = testResolver(testStorage208);
3129 +
        let program = "fn fail() throws (i32) { throw true; }";
3130 +
        let result = try resolveProgramStr(&mut a, program);
3131 +
        try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError);
3132 +
    }
2486 3133
}
2487 3134
2488 3135
// Binary operation tests //////////////////////////////////////////////////////
2489 3136
2490 3137
@test unsafe fn testResolveBinaryOpArithmetic() throws (testing::TestError) {
2491 3138
    {
2492 -
        let mut a = testResolver();
2493 -
        let result = try resolveExprStr(&mut a, "4 + 4");
2494 -
        try expectNoErrors(&result);
2495 -
        try expectType(&a, result.root, super::Type::Int);
3139 +
        let mut testArena209 = testArena();
3140 +
        let testStorage209: 'test209 = &mut testArena209 in {
3141 +
            let mut a = testResolver(testStorage209);
3142 +
            let result = try resolveExprStr(&mut a, "4 + 4");
3143 +
            try expectNoErrors(&result);
3144 +
            try expectType(&a, result.root, super::Type::Int);
3145 +
        }
2496 3146
    } {
2497 -
        let mut a = testResolver();
2498 -
        let result = try resolveExprStr(&mut a, "10 - 3");
2499 -
        try expectNoErrors(&result);
2500 -
        try expectType(&a, result.root, super::Type::Int);
3147 +
        let mut testArena210 = testArena();
3148 +
        let testStorage210: 'test210 = &mut testArena210 in {
3149 +
            let mut a = testResolver(testStorage210);
3150 +
            let result = try resolveExprStr(&mut a, "10 - 3");
3151 +
            try expectNoErrors(&result);
3152 +
            try expectType(&a, result.root, super::Type::Int);
3153 +
        }
2501 3154
    } {
2502 -
        let mut a = testResolver();
2503 -
        let result = try resolveExprStr(&mut a, "5 * 6");
2504 -
        try expectNoErrors(&result);
2505 -
        try expectType(&a, result.root, super::Type::Int);
3155 +
        let mut testArena211 = testArena();
3156 +
        let testStorage211: 'test211 = &mut testArena211 in {
3157 +
            let mut a = testResolver(testStorage211);
3158 +
            let result = try resolveExprStr(&mut a, "5 * 6");
3159 +
            try expectNoErrors(&result);
3160 +
            try expectType(&a, result.root, super::Type::Int);
3161 +
        }
2506 3162
    } {
2507 -
        let mut a = testResolver();
2508 -
        let result = try resolveExprStr(&mut a, "20 / 4");
2509 -
        try expectNoErrors(&result);
2510 -
        try expectType(&a, result.root, super::Type::Int);
3163 +
        let mut testArena212 = testArena();
3164 +
        let testStorage212: 'test212 = &mut testArena212 in {
3165 +
            let mut a = testResolver(testStorage212);
3166 +
            let result = try resolveExprStr(&mut a, "20 / 4");
3167 +
            try expectNoErrors(&result);
3168 +
            try expectType(&a, result.root, super::Type::Int);
3169 +
        }
2511 3170
    } {
2512 -
        let mut a = testResolver();
2513 -
        let result = try resolveExprStr(&mut a, "17 % 5");
2514 -
        try expectNoErrors(&result);
2515 -
        try expectType(&a, result.root, super::Type::Int);
3171 +
        let mut testArena213 = testArena();
3172 +
        let testStorage213: 'test213 = &mut testArena213 in {
3173 +
            let mut a = testResolver(testStorage213);
3174 +
            let result = try resolveExprStr(&mut a, "17 % 5");
3175 +
            try expectNoErrors(&result);
3176 +
            try expectType(&a, result.root, super::Type::Int);
3177 +
        }
2516 3178
    } {
2517 -
        let mut a = testResolver();
2518 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;");
2519 -
        try expectNoErrors(&result);
2520 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2521 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3179 +
        let mut testArena214 = testArena();
3180 +
        let testStorage214: 'test214 = &mut testArena214 in {
3181 +
            let mut a = testResolver(testStorage214);
3182 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;");
3183 +
            try expectNoErrors(&result);
3184 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3185 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3186 +
        }
2522 3187
    } {
2523 -
        let mut a = testResolver();
2524 -
        let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4");
2525 -
        try expectNoErrors(&result);
2526 -
        try expectType(&a, result.root, super::Type::Int);
3188 +
        let mut testArena215 = testArena();
3189 +
        let testStorage215: 'test215 = &mut testArena215 in {
3190 +
            let mut a = testResolver(testStorage215);
3191 +
            let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4");
3192 +
            try expectNoErrors(&result);
3193 +
            try expectType(&a, result.root, super::Type::Int);
3194 +
        }
2527 3195
    } {
2528 -
        let mut a = testResolver();
2529 -
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;");
2530 -
        try expectNoErrors(&result);
2531 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2532 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3196 +
        let mut testArena216 = testArena();
3197 +
        let testStorage216: 'test216 = &mut testArena216 in {
3198 +
            let mut a = testResolver(testStorage216);
3199 +
            let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;");
3200 +
            try expectNoErrors(&result);
3201 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3202 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3203 +
        }
2533 3204
    } {
2534 -
        let mut a = testResolver();
2535 -
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;");
2536 -
        try expectNoErrors(&result);
2537 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2538 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3205 +
        let mut testArena217 = testArena();
3206 +
        let testStorage217: 'test217 = &mut testArena217 in {
3207 +
            let mut a = testResolver(testStorage217);
3208 +
            let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;");
3209 +
            try expectNoErrors(&result);
3210 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3211 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3212 +
        }
2539 3213
    } {
2540 -
        let mut a = testResolver();
2541 -
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;");
2542 -
        try expectNoErrors(&result);
2543 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2544 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3214 +
        let mut testArena218 = testArena();
3215 +
        let testStorage218: 'test218 = &mut testArena218 in {
3216 +
            let mut a = testResolver(testStorage218);
3217 +
            let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;");
3218 +
            try expectNoErrors(&result);
3219 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3220 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3221 +
        }
2545 3222
    }
2546 3223
}
2547 3224
2548 3225
@test unsafe fn testResolveBinaryOpComparison() throws (testing::TestError) {
2549 3226
    {
2550 -
        let mut a = testResolver();
2551 -
        let result = try resolveExprStr(&mut a, "5 == 5");
2552 -
        try expectNoErrors(&result);
2553 -
        try expectType(&a, result.root, super::Type::Bool);
3227 +
        let mut testArena219 = testArena();
3228 +
        let testStorage219: 'test219 = &mut testArena219 in {
3229 +
            let mut a = testResolver(testStorage219);
3230 +
            let result = try resolveExprStr(&mut a, "5 == 5");
3231 +
            try expectNoErrors(&result);
3232 +
            try expectType(&a, result.root, super::Type::Bool);
3233 +
        }
2554 3234
    } {
2555 -
        let mut a = testResolver();
2556 -
        let result = try resolveExprStr(&mut a, "5 <> 10");
2557 -
        try expectNoErrors(&result);
2558 -
        try expectType(&a, result.root, super::Type::Bool);
3235 +
        let mut testArena220 = testArena();
3236 +
        let testStorage220: 'test220 = &mut testArena220 in {
3237 +
            let mut a = testResolver(testStorage220);
3238 +
            let result = try resolveExprStr(&mut a, "5 <> 10");
3239 +
            try expectNoErrors(&result);
3240 +
            try expectType(&a, result.root, super::Type::Bool);
3241 +
        }
2559 3242
    } {
2560 -
        let mut a = testResolver();
2561 -
        let result = try resolveExprStr(&mut a, "5 < 10");
2562 -
        try expectNoErrors(&result);
2563 -
        try expectType(&a, result.root, super::Type::Bool);
3243 +
        let mut testArena221 = testArena();
3244 +
        let testStorage221: 'test221 = &mut testArena221 in {
3245 +
            let mut a = testResolver(testStorage221);
3246 +
            let result = try resolveExprStr(&mut a, "5 < 10");
3247 +
            try expectNoErrors(&result);
3248 +
            try expectType(&a, result.root, super::Type::Bool);
3249 +
        }
2564 3250
    } {
2565 -
        let mut a = testResolver();
2566 -
        let result = try resolveExprStr(&mut a, "10 > 5");
2567 -
        try expectNoErrors(&result);
2568 -
        try expectType(&a, result.root, super::Type::Bool);
3251 +
        let mut testArena222 = testArena();
3252 +
        let testStorage222: 'test222 = &mut testArena222 in {
3253 +
            let mut a = testResolver(testStorage222);
3254 +
            let result = try resolveExprStr(&mut a, "10 > 5");
3255 +
            try expectNoErrors(&result);
3256 +
            try expectType(&a, result.root, super::Type::Bool);
3257 +
        }
2569 3258
    } {
2570 -
        let mut a = testResolver();
2571 -
        let result = try resolveExprStr(&mut a, "5 <= 5");
2572 -
        try expectNoErrors(&result);
2573 -
        try expectType(&a, result.root, super::Type::Bool);
3259 +
        let mut testArena223 = testArena();
3260 +
        let testStorage223: 'test223 = &mut testArena223 in {
3261 +
            let mut a = testResolver(testStorage223);
3262 +
            let result = try resolveExprStr(&mut a, "5 <= 5");
3263 +
            try expectNoErrors(&result);
3264 +
            try expectType(&a, result.root, super::Type::Bool);
3265 +
        }
2574 3266
    } {
2575 -
        let mut a = testResolver();
2576 -
        let result = try resolveExprStr(&mut a, "10 >= 5");
2577 -
        try expectNoErrors(&result);
2578 -
        try expectType(&a, result.root, super::Type::Bool);
3267 +
        let mut testArena224 = testArena();
3268 +
        let testStorage224: 'test224 = &mut testArena224 in {
3269 +
            let mut a = testResolver(testStorage224);
3270 +
            let result = try resolveExprStr(&mut a, "10 >= 5");
3271 +
            try expectNoErrors(&result);
3272 +
            try expectType(&a, result.root, super::Type::Bool);
3273 +
        }
2579 3274
    } {
2580 -
        let mut a = testResolver();
2581 -
        let result = try resolveExprStr(&mut a, "true == false");
2582 -
        try expectNoErrors(&result);
2583 -
        try expectType(&a, result.root, super::Type::Bool);
3275 +
        let mut testArena225 = testArena();
3276 +
        let testStorage225: 'test225 = &mut testArena225 in {
3277 +
            let mut a = testResolver(testStorage225);
3278 +
            let result = try resolveExprStr(&mut a, "true == false");
3279 +
            try expectNoErrors(&result);
3280 +
            try expectType(&a, result.root, super::Type::Bool);
3281 +
        }
2584 3282
    } {
2585 -
        let mut a = testResolver();
2586 -
        let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4");
2587 -
        try expectNoErrors(&result);
2588 -
        try expectType(&a, result.root, super::Type::Bool);
3283 +
        let mut testArena226 = testArena();
3284 +
        let testStorage226: 'test226 = &mut testArena226 in {
3285 +
            let mut a = testResolver(testStorage226);
3286 +
            let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4");
3287 +
            try expectNoErrors(&result);
3288 +
            try expectType(&a, result.root, super::Type::Bool);
3289 +
        }
2589 3290
    } {
2590 -
        let mut a = testResolver();
2591 -
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;");
2592 -
        try expectNoErrors(&result);
2593 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2594 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
3291 +
        let mut testArena227 = testArena();
3292 +
        let testStorage227: 'test227 = &mut testArena227 in {
3293 +
            let mut a = testResolver(testStorage227);
3294 +
            let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;");
3295 +
            try expectNoErrors(&result);
3296 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3297 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
3298 +
        }
2595 3299
    } {
2596 -
        let mut a = testResolver();
2597 -
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;");
2598 -
        try expectNoErrors(&result);
2599 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2600 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
3300 +
        let mut testArena228 = testArena();
3301 +
        let testStorage228: 'test228 = &mut testArena228 in {
3302 +
            let mut a = testResolver(testStorage228);
3303 +
            let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;");
3304 +
            try expectNoErrors(&result);
3305 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3306 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
3307 +
        }
2601 3308
    }
2602 3309
}
2603 3310
2604 3311
@test unsafe fn testResolveBinaryOpLogical() throws (testing::TestError) {
2605 3312
    {
2606 -
        let mut a = testResolver();
2607 -
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;");
2608 -
        try expectNoErrors(&result);
2609 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2610 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
3313 +
        let mut testArena229 = testArena();
3314 +
        let testStorage229: 'test229 = &mut testArena229 in {
3315 +
            let mut a = testResolver(testStorage229);
3316 +
            let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;");
3317 +
            try expectNoErrors(&result);
3318 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3319 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
3320 +
        }
2611 3321
    } {
2612 -
        let mut a = testResolver();
2613 -
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;");
2614 -
        try expectNoErrors(&result);
2615 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2616 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
3322 +
        let mut testArena230 = testArena();
3323 +
        let testStorage230: 'test230 = &mut testArena230 in {
3324 +
            let mut a = testResolver(testStorage230);
3325 +
            let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;");
3326 +
            try expectNoErrors(&result);
3327 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3328 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
3329 +
        }
2617 3330
    } {
2618 -
        let mut a = testResolver();
2619 -
        let result = try resolveExprStr(&mut a, "true and false");
2620 -
        try expectNoErrors(&result);
2621 -
        try expectType(&a, result.root, super::Type::Bool);
3331 +
        let mut testArena231 = testArena();
3332 +
        let testStorage231: 'test231 = &mut testArena231 in {
3333 +
            let mut a = testResolver(testStorage231);
3334 +
            let result = try resolveExprStr(&mut a, "true and false");
3335 +
            try expectNoErrors(&result);
3336 +
            try expectType(&a, result.root, super::Type::Bool);
3337 +
        }
2622 3338
    }
2623 3339
}
2624 3340
2625 3341
@test unsafe fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) {
2626 3342
    {
2627 -
        let mut a = testResolver();
2628 -
        let result = try resolveProgramStr(&mut a, "4 + true");
2629 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3343 +
        let mut testArena232 = testArena();
3344 +
        let testStorage232: 'test232 = &mut testArena232 in {
3345 +
            let mut a = testResolver(testStorage232);
3346 +
            let result = try resolveProgramStr(&mut a, "4 + true");
3347 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3348 +
        }
2630 3349
    } {
2631 -
        let mut a = testResolver();
2632 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;");
2633 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3350 +
        let mut testArena233 = testArena();
3351 +
        let testStorage233: 'test233 = &mut testArena233 in {
3352 +
            let mut a = testResolver(testStorage233);
3353 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;");
3354 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3355 +
        }
2634 3356
    } {
2635 -
        let mut a = testResolver();
2636 -
        let result = try resolveProgramStr(&mut a, "10 - false");
2637 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3357 +
        let mut testArena234 = testArena();
3358 +
        let testStorage234: 'test234 = &mut testArena234 in {
3359 +
            let mut a = testResolver(testStorage234);
3360 +
            let result = try resolveProgramStr(&mut a, "10 - false");
3361 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3362 +
        }
2638 3363
    } {
2639 -
        let mut a = testResolver();
2640 -
        let result = try resolveProgramStr(&mut a, "5 * true");
2641 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3364 +
        let mut testArena235 = testArena();
3365 +
        let testStorage235: 'test235 = &mut testArena235 in {
3366 +
            let mut a = testResolver(testStorage235);
3367 +
            let result = try resolveProgramStr(&mut a, "5 * true");
3368 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3369 +
        }
2642 3370
    } {
2643 -
        let mut a = testResolver();
2644 -
        let result = try resolveProgramStr(&mut a, "20 / false");
2645 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3371 +
        let mut testArena236 = testArena();
3372 +
        let testStorage236: 'test236 = &mut testArena236 in {
3373 +
            let mut a = testResolver(testStorage236);
3374 +
            let result = try resolveProgramStr(&mut a, "20 / false");
3375 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3376 +
        }
2646 3377
    } {
2647 -
        let mut a = testResolver();
2648 -
        let result = try resolveProgramStr(&mut a, "17 % true");
2649 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3378 +
        let mut testArena237 = testArena();
3379 +
        let testStorage237: 'test237 = &mut testArena237 in {
3380 +
            let mut a = testResolver(testStorage237);
3381 +
            let result = try resolveProgramStr(&mut a, "17 % true");
3382 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3383 +
        }
2650 3384
    } {
2651 -
        let mut a = testResolver();
2652 -
        let result = try resolveProgramStr(&mut a, "1 + (true * 3)");
2653 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3385 +
        let mut testArena238 = testArena();
3386 +
        let testStorage238: 'test238 = &mut testArena238 in {
3387 +
            let mut a = testResolver(testStorage238);
3388 +
            let result = try resolveProgramStr(&mut a, "1 + (true * 3)");
3389 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3390 +
        }
2654 3391
    }
2655 3392
}
2656 3393
2657 3394
@test unsafe fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) {
2658 3395
    {
2659 -
        let mut a = testResolver();
2660 -
        let result = try resolveProgramStr(&mut a, "42 and true");
2661 -
        let err = try expectError(&result);
2662 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3396 +
        let mut testArena239 = testArena();
3397 +
        let testStorage239: 'test239 = &mut testArena239 in {
3398 +
            let mut a = testResolver(testStorage239);
3399 +
            let result = try resolveProgramStr(&mut a, "42 and true");
3400 +
            let err = try expectError(&result);
3401 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3402 +
        }
2663 3403
    } {
2664 -
        let mut a = testResolver();
2665 -
        let result = try resolveProgramStr(&mut a, "true or 5");
2666 -
        let err = try expectError(&result);
2667 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3404 +
        let mut testArena240 = testArena();
3405 +
        let testStorage240: 'test240 = &mut testArena240 in {
3406 +
            let mut a = testResolver(testStorage240);
3407 +
            let result = try resolveProgramStr(&mut a, "true or 5");
3408 +
            let err = try expectError(&result);
3409 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3410 +
        }
2668 3411
    } {
2669 -
        let mut a = testResolver();
2670 -
        let result = try resolveProgramStr(&mut a, "1 and 2");
2671 -
        let err = try expectError(&result);
2672 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3412 +
        let mut testArena241 = testArena();
3413 +
        let testStorage241: 'test241 = &mut testArena241 in {
3414 +
            let mut a = testResolver(testStorage241);
3415 +
            let result = try resolveProgramStr(&mut a, "1 and 2");
3416 +
            let err = try expectError(&result);
3417 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3418 +
        }
2673 3419
    }
2674 3420
}
2675 3421
2676 3422
@test unsafe fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) {
2677 -
    let mut a = testResolver();
2678 -
    let result = try resolveProgramStr(&mut a, "true < false");
2679 -
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3423 +
    let mut testArena242 = testArena();
3424 +
    let testStorage242: 'test242 = &mut testArena242 in {
3425 +
        let mut a = testResolver(testStorage242);
3426 +
        let result = try resolveProgramStr(&mut a, "true < false");
3427 +
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3428 +
    }
2680 3429
}
2681 3430
2682 3431
// Unary operation tests ///////////////////////////////////////////////////////
2683 3432
2684 3433
@test unsafe fn testResolveUnaryOpNot() throws (testing::TestError) {
2685 3434
    {
2686 -
        let mut a = testResolver();
2687 -
        let result = try resolveExprStr(&mut a, "not true");
2688 -
        try expectNoErrors(&result);
2689 -
        try expectType(&a, result.root, super::Type::Bool);
3435 +
        let mut testArena243 = testArena();
3436 +
        let testStorage243: 'test243 = &mut testArena243 in {
3437 +
            let mut a = testResolver(testStorage243);
3438 +
            let result = try resolveExprStr(&mut a, "not true");
3439 +
            try expectNoErrors(&result);
3440 +
            try expectType(&a, result.root, super::Type::Bool);
3441 +
        }
2690 3442
    } {
2691 -
        let mut a = testResolver();
2692 -
        let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;");
2693 -
        try expectNoErrors(&result);
2694 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2695 -
        try expectExprStmtType(&a, stmt, super::Type::Bool);
3443 +
        let mut testArena244 = testArena();
3444 +
        let testStorage244: 'test244 = &mut testArena244 in {
3445 +
            let mut a = testResolver(testStorage244);
3446 +
            let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;");
3447 +
            try expectNoErrors(&result);
3448 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3449 +
            try expectExprStmtType(&a, stmt, super::Type::Bool);
3450 +
        }
2696 3451
    } {
2697 -
        let mut a = testResolver();
2698 -
        let result = try resolveExprStr(&mut a, "not (true and false)");
2699 -
        try expectNoErrors(&result);
2700 -
        try expectType(&a, result.root, super::Type::Bool);
3452 +
        let mut testArena245 = testArena();
3453 +
        let testStorage245: 'test245 = &mut testArena245 in {
3454 +
            let mut a = testResolver(testStorage245);
3455 +
            let result = try resolveExprStr(&mut a, "not (true and false)");
3456 +
            try expectNoErrors(&result);
3457 +
            try expectType(&a, result.root, super::Type::Bool);
3458 +
        }
2701 3459
    } {
2702 -
        let mut a = testResolver();
2703 -
        let result = try resolveProgramStr(&mut a, "not 42");
2704 -
        let err = try expectError(&result);
2705 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3460 +
        let mut testArena246 = testArena();
3461 +
        let testStorage246: 'test246 = &mut testArena246 in {
3462 +
            let mut a = testResolver(testStorage246);
3463 +
            let result = try resolveProgramStr(&mut a, "not 42");
3464 +
            let err = try expectError(&result);
3465 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
3466 +
        }
2706 3467
    } {
2707 -
        let mut a = testResolver();
2708 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;");
2709 -
        let err = try expectError(&result);
2710 -
        try expectTypeMismatch(err, super::Type::Bool, super::Type::I32);
3468 +
        let mut testArena247 = testArena();
3469 +
        let testStorage247: 'test247 = &mut testArena247 in {
3470 +
            let mut a = testResolver(testStorage247);
3471 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;");
3472 +
            let err = try expectError(&result);
3473 +
            try expectTypeMismatch(err, super::Type::Bool, super::Type::I32);
3474 +
        }
2711 3475
    }
2712 3476
}
2713 3477
2714 3478
@test unsafe fn testResolveUnaryOpNeg() throws (testing::TestError) {
2715 3479
    {
2716 -
        let mut a = testResolver();
2717 -
        let result = try resolveExprStr(&mut a, "-42");
2718 -
        try expectNoErrors(&result);
2719 -
        try expectType(&a, result.root, super::Type::Int);
3480 +
        let mut testArena248 = testArena();
3481 +
        let testStorage248: 'test248 = &mut testArena248 in {
3482 +
            let mut a = testResolver(testStorage248);
3483 +
            let result = try resolveExprStr(&mut a, "-42");
3484 +
            try expectNoErrors(&result);
3485 +
            try expectType(&a, result.root, super::Type::Int);
3486 +
        }
2720 3487
    } {
2721 -
        let mut a = testResolver();
2722 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;");
2723 -
        try expectNoErrors(&result);
2724 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2725 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3488 +
        let mut testArena249 = testArena();
3489 +
        let testStorage249: 'test249 = &mut testArena249 in {
3490 +
            let mut a = testResolver(testStorage249);
3491 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;");
3492 +
            try expectNoErrors(&result);
3493 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3494 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3495 +
        }
2726 3496
    } {
2727 -
        let mut a = testResolver();
2728 -
        let result = try resolveExprStr(&mut a, "-(5 + 3)");
2729 -
        try expectNoErrors(&result);
2730 -
        try expectType(&a, result.root, super::Type::Int);
3497 +
        let mut testArena250 = testArena();
3498 +
        let testStorage250: 'test250 = &mut testArena250 in {
3499 +
            let mut a = testResolver(testStorage250);
3500 +
            let result = try resolveExprStr(&mut a, "-(5 + 3)");
3501 +
            try expectNoErrors(&result);
3502 +
            try expectType(&a, result.root, super::Type::Int);
3503 +
        }
2731 3504
    } {
2732 -
        let mut a = testResolver();
2733 -
        let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;");
2734 -
        try expectNoErrors(&result);
2735 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2736 -
        try expectExprStmtType(&a, stmt, super::Type::I8);
3505 +
        let mut testArena251 = testArena();
3506 +
        let testStorage251: 'test251 = &mut testArena251 in {
3507 +
            let mut a = testResolver(testStorage251);
3508 +
            let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;");
3509 +
            try expectNoErrors(&result);
3510 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3511 +
            try expectExprStmtType(&a, stmt, super::Type::I8);
3512 +
        }
2737 3513
    } {
2738 -
        let mut a = testResolver();
2739 -
        let result = try resolveProgramStr(&mut a, "-true");
2740 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3514 +
        let mut testArena252 = testArena();
3515 +
        let testStorage252: 'test252 = &mut testArena252 in {
3516 +
            let mut a = testResolver(testStorage252);
3517 +
            let result = try resolveProgramStr(&mut a, "-true");
3518 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3519 +
        }
2741 3520
    } {
2742 -
        let mut a = testResolver();
2743 -
        let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;");
2744 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3521 +
        let mut testArena253 = testArena();
3522 +
        let testStorage253: 'test253 = &mut testArena253 in {
3523 +
            let mut a = testResolver(testStorage253);
3524 +
            let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;");
3525 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3526 +
        }
2745 3527
    }
2746 3528
}
2747 3529
2748 3530
@test unsafe fn testResolveUnaryOpBitNot() throws (testing::TestError) {
2749 3531
    {
2750 -
        let mut a = testResolver();
2751 -
        let result = try resolveExprStr(&mut a, "~42");
2752 -
        try expectNoErrors(&result);
2753 -
        try expectType(&a, result.root, super::Type::Int);
3532 +
        let mut testArena254 = testArena();
3533 +
        let testStorage254: 'test254 = &mut testArena254 in {
3534 +
            let mut a = testResolver(testStorage254);
3535 +
            let result = try resolveExprStr(&mut a, "~42");
3536 +
            try expectNoErrors(&result);
3537 +
            try expectType(&a, result.root, super::Type::Int);
3538 +
        }
2754 3539
    } {
2755 -
        let mut a = testResolver();
2756 -
        let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;");
2757 -
        try expectNoErrors(&result);
2758 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2759 -
        try expectExprStmtType(&a, stmt, super::Type::U32);
3540 +
        let mut testArena255 = testArena();
3541 +
        let testStorage255: 'test255 = &mut testArena255 in {
3542 +
            let mut a = testResolver(testStorage255);
3543 +
            let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;");
3544 +
            try expectNoErrors(&result);
3545 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3546 +
            try expectExprStmtType(&a, stmt, super::Type::U32);
3547 +
        }
2760 3548
    } {
2761 -
        let mut a = testResolver();
2762 -
        let result = try resolveExprStr(&mut a, "~(0xFF)");
2763 -
        try expectNoErrors(&result);
2764 -
        try expectType(&a, result.root, super::Type::Int);
3549 +
        let mut testArena256 = testArena();
3550 +
        let testStorage256: 'test256 = &mut testArena256 in {
3551 +
            let mut a = testResolver(testStorage256);
3552 +
            let result = try resolveExprStr(&mut a, "~(0xFF)");
3553 +
            try expectNoErrors(&result);
3554 +
            try expectType(&a, result.root, super::Type::Int);
3555 +
        }
2765 3556
    } {
2766 -
        let mut a = testResolver();
2767 -
        let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;");
2768 -
        try expectNoErrors(&result);
2769 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2770 -
        try expectExprStmtType(&a, stmt, super::Type::I8);
3557 +
        let mut testArena257 = testArena();
3558 +
        let testStorage257: 'test257 = &mut testArena257 in {
3559 +
            let mut a = testResolver(testStorage257);
3560 +
            let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;");
3561 +
            try expectNoErrors(&result);
3562 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3563 +
            try expectExprStmtType(&a, stmt, super::Type::I8);
3564 +
        }
2771 3565
    } {
2772 -
        let mut a = testResolver();
2773 -
        let result = try resolveProgramStr(&mut a, "~true");
2774 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3566 +
        let mut testArena258 = testArena();
3567 +
        let testStorage258: 'test258 = &mut testArena258 in {
3568 +
            let mut a = testResolver(testStorage258);
3569 +
            let result = try resolveProgramStr(&mut a, "~true");
3570 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3571 +
        }
2775 3572
    } {
2776 -
        let mut a = testResolver();
2777 -
        let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;");
2778 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3573 +
        let mut testArena259 = testArena();
3574 +
        let testStorage259: 'test259 = &mut testArena259 in {
3575 +
            let mut a = testResolver(testStorage259);
3576 +
            let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;");
3577 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
3578 +
        }
2779 3579
    }
2780 3580
}
2781 3581
2782 3582
@test unsafe fn testResolveUnaryOpNested() throws (testing::TestError) {
2783 3583
    {
2784 -
        let mut a = testResolver();
2785 -
        let result = try resolveExprStr(&mut a, "not not true");
2786 -
        try expectNoErrors(&result);
2787 -
        try expectType(&a, result.root, super::Type::Bool);
3584 +
        let mut testArena260 = testArena();
3585 +
        let testStorage260: 'test260 = &mut testArena260 in {
3586 +
            let mut a = testResolver(testStorage260);
3587 +
            let result = try resolveExprStr(&mut a, "not not true");
3588 +
            try expectNoErrors(&result);
3589 +
            try expectType(&a, result.root, super::Type::Bool);
3590 +
        }
2788 3591
    } {
2789 -
        let mut a = testResolver();
2790 -
        let result = try resolveExprStr(&mut a, "--42");
2791 -
        try expectNoErrors(&result);
2792 -
        try expectType(&a, result.root, super::Type::Int);
3592 +
        let mut testArena261 = testArena();
3593 +
        let testStorage261: 'test261 = &mut testArena261 in {
3594 +
            let mut a = testResolver(testStorage261);
3595 +
            let result = try resolveExprStr(&mut a, "--42");
3596 +
            try expectNoErrors(&result);
3597 +
            try expectType(&a, result.root, super::Type::Int);
3598 +
        }
2793 3599
    } {
2794 -
        let mut a = testResolver();
2795 -
        let result = try resolveExprStr(&mut a, "~~0xFF");
2796 -
        try expectNoErrors(&result);
2797 -
        try expectType(&a, result.root, super::Type::Int);
3600 +
        let mut testArena262 = testArena();
3601 +
        let testStorage262: 'test262 = &mut testArena262 in {
3602 +
            let mut a = testResolver(testStorage262);
3603 +
            let result = try resolveExprStr(&mut a, "~~0xFF");
3604 +
            try expectNoErrors(&result);
3605 +
            try expectType(&a, result.root, super::Type::Int);
3606 +
        }
2798 3607
    } {
2799 -
        let mut a = testResolver();
2800 -
        let result = try resolveExprStr(&mut a, "-(~42)");
2801 -
        try expectNoErrors(&result);
2802 -
        try expectType(&a, result.root, super::Type::Int);
3608 +
        let mut testArena263 = testArena();
3609 +
        let testStorage263: 'test263 = &mut testArena263 in {
3610 +
            let mut a = testResolver(testStorage263);
3611 +
            let result = try resolveExprStr(&mut a, "-(~42)");
3612 +
            try expectNoErrors(&result);
3613 +
            try expectType(&a, result.root, super::Type::Int);
3614 +
        }
2803 3615
    }
2804 3616
}
2805 3617
2806 3618
// test fn testNormalPointerArithmetic() throws (testing::TestError) {
2807 3619
//     mut a = testResolver();
2811 3623
2812 3624
// Dereference tests //////////////////////////////////////////////////////////
2813 3625
2814 3626
@test unsafe fn testResolveDeref() throws (testing::TestError) {
2815 3627
    {
2816 -
        let mut a = testResolver();
2817 -
        let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;");
2818 -
        try expectNoErrors(&result);
2819 -
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2820 -
        try expectExprStmtType(&a, stmt, super::Type::I32);
3628 +
        let mut testArena264 = testArena();
3629 +
        let testStorage264: 'test264 = &mut testArena264 in {
3630 +
            let mut a = testResolver(testStorage264);
3631 +
            let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;");
3632 +
            try expectNoErrors(&result);
3633 +
            let stmt = try parser::tests::getBlockLastStmt(result.root);
3634 +
            try expectExprStmtType(&a, stmt, super::Type::I32);
3635 +
        }
2821 3636
    } {
2822 -
        let mut a = testResolver();
2823 -
        let result = try resolveExprStr(&mut a, "*42");
2824 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
3637 +
        let mut testArena265 = testArena();
3638 +
        let testStorage265: 'test265 = &mut testArena265 in {
3639 +
            let mut a = testResolver(testStorage265);
3640 +
            let result = try resolveExprStr(&mut a, "*42");
3641 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
3642 +
        }
2825 3643
    } {
2826 -
        let mut a = testResolver();
2827 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;");
2828 -
        try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
3644 +
        let mut testArena266 = testArena();
3645 +
        let testStorage266: 'test266 = &mut testArena266 in {
3646 +
            let mut a = testResolver(testStorage266);
3647 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;");
3648 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
3649 +
        }
2829 3650
    }
2830 3651
}
2831 3652
2832 3653
@test unsafe fn testResolveAssignDeref() throws (testing::TestError) {
2833 3654
    {
2834 -
        let mut a = testResolver();
2835 -
        let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;";
2836 -
        let result = try resolveProgramStr(&mut a, program);
2837 -
        try expectNoErrors(&result);
3655 +
        let mut testArena267 = testArena();
3656 +
        let testStorage267: 'test267 = &mut testArena267 in {
3657 +
            let mut a = testResolver(testStorage267);
3658 +
            let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;";
3659 +
            let result = try resolveProgramStr(&mut a, program);
3660 +
            try expectNoErrors(&result);
3661 +
        }
2838 3662
    } {
2839 -
        let mut a = testResolver();
2840 -
        let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;";
2841 -
        let result = try resolveProgramStr(&mut a, program);
2842 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3663 +
        let mut testArena268 = testArena();
3664 +
        let testStorage268: 'test268 = &mut testArena268 in {
3665 +
            let mut a = testResolver(testStorage268);
3666 +
            let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;";
3667 +
            let result = try resolveProgramStr(&mut a, program);
3668 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3669 +
        }
2843 3670
    } {
2844 -
        let mut a = testResolver();
2845 -
        let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;";
2846 -
        let result = try resolveProgramStr(&mut a, program);
2847 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3671 +
        let mut testArena269 = testArena();
3672 +
        let testStorage269: 'test269 = &mut testArena269 in {
3673 +
            let mut a = testResolver(testStorage269);
3674 +
            let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;";
3675 +
            let result = try resolveProgramStr(&mut a, program);
3676 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3677 +
        }
2848 3678
    } {
2849 -
        let mut a = testResolver();
2850 -
        let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;";
2851 -
        let result = try resolveProgramStr(&mut a, program);
2852 -
        try expectNoErrors(&result);
3679 +
        let mut testArena270 = testArena();
3680 +
        let testStorage270: 'test270 = &mut testArena270 in {
3681 +
            let mut a = testResolver(testStorage270);
3682 +
            let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;";
3683 +
            let result = try resolveProgramStr(&mut a, program);
3684 +
            try expectNoErrors(&result);
3685 +
        }
2853 3686
    }
2854 3687
}
2855 3688
2856 3689
// Type inference tests ///////////////////////////////////////////////////////
2857 3690
2858 3691
@test unsafe fn testResolveBasicTypeInference() throws (testing::TestError) {
2859 3692
    {
2860 3693
        // Boolean literals are unambiguous.
2861 -
        let mut a = testResolver();
2862 -
        let result = try resolveProgramStr(&mut a, "let x = true; x;");
2863 -
        try expectNoErrors(&result);
2864 -
2865 -
        let xStmt = try parser::tests::getBlockLastStmt(result.root);
2866 -
        try expectExprStmtType(&a, xStmt, super::Type::Bool);
3694 +
        let mut testArena271 = testArena();
3695 +
        let testStorage271: 'test271 = &mut testArena271 in {
3696 +
            let mut a = testResolver(testStorage271);
3697 +
            let result = try resolveProgramStr(&mut a, "let x = true; x;");
3698 +
            try expectNoErrors(&result);
3699 +
3700 +
            let xStmt = try parser::tests::getBlockLastStmt(result.root);
3701 +
            try expectExprStmtType(&a, xStmt, super::Type::Bool);
3702 +
        }
2867 3703
    } {
2868 3704
        // Integer literals are ambiguous.
2869 -
        let mut a = testResolver();
2870 -
        let result = try resolveProgramStr(&mut a, "let x = 34;");
2871 -
        try expectErrorKind(&result, super::ErrorKind::CannotInferType);
3705 +
        let mut testArena272 = testArena();
3706 +
        let testStorage272: 'test272 = &mut testArena272 in {
3707 +
            let mut a = testResolver(testStorage272);
3708 +
            let result = try resolveProgramStr(&mut a, "let x = 34;");
3709 +
            try expectErrorKind(&result, super::ErrorKind::CannotInferType);
3710 +
        }
2872 3711
    }
2873 3712
}
2874 3713
2875 3714
// Union tests /////////////////////////////////////////////////////////////////
2876 3715
2877 3716
@test unsafe fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) {
2878 -
    let mut a = testResolver();
2879 -
    let program = "union Status { Ok, Error } Status::Ok;";
2880 -
    let result = try resolveProgramStr(&mut a, program);
3717 +
    let mut testArena273 = testArena();
3718 +
    let testStorage273: 'test273 = &mut testArena273 in {
3719 +
        let mut a = testResolver(testStorage273);
3720 +
        let program = "union Status { Ok, Error } Status::Ok;";
3721 +
        let result = try resolveProgramStr(&mut a, program);
2881 3722
2882 -
    let ty = try getTypeInScopeOf(&a, result.root, "Status");
2883 -
    let case super::NominalType::Union(unionType) = *ty
2884 -
        else throw testing::TestError::Failed;
2885 -
    try testing::expect(unionType.variants.len == 2);
2886 -
    try testing::expect(mem::eq(unionType.variants[0].name, "Ok"));
2887 -
    try testing::expect(mem::eq(unionType.variants[1].name, "Error"));
2888 -
    if getUnionVariantPayload(ty, "Ok") <> super::Type::Void {
2889 -
        throw testing::TestError::Failed;
3723 +
        let ty = try getTypeInScopeOf(&a, result.root, "Status");
3724 +
        let case super::NominalType::Union(unionType) = *ty
3725 +
            else throw testing::TestError::Failed;
3726 +
        try testing::expect(unionType.variants.len == 2);
3727 +
        try testing::expect(mem::eq(unionType.variants[0].name, "Ok"));
3728 +
        try testing::expect(mem::eq(unionType.variants[1].name, "Error"));
3729 +
        if getUnionVariantPayload(ty, "Ok") <> super::Type::Void {
3730 +
            throw testing::TestError::Failed;
3731 +
        }
3732 +
        let stmt = try getBlockStmt(result.root, 1);
3733 +
        try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3734 +
        try expectNoErrors(&result);
2890 3735
    }
2891 -
    let stmt = try getBlockStmt(result.root, 1);
2892 -
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2893 -
    try expectNoErrors(&result);
2894 3736
}
2895 3737
2896 3738
@test unsafe fn testResolveUnionVariantWithPayload() throws (testing::TestError) {
2897 -
    let mut a = testResolver();
2898 -
    let program = "union R { Ok(i32), Err(bool) } R::Ok(42);";
2899 -
    let result = try resolveProgramStr(&mut a, program);
2900 -
    try expectNoErrors(&result);
3739 +
    let mut testArena274 = testArena();
3740 +
    let testStorage274: 'test274 = &mut testArena274 in {
3741 +
        let mut a = testResolver(testStorage274);
3742 +
        let program = "union R { Ok(i32), Err(bool) } R::Ok(42);";
3743 +
        let result = try resolveProgramStr(&mut a, program);
3744 +
        try expectNoErrors(&result);
2901 3745
2902 -
    let ty = try getTypeInScopeOf(&a, result.root, "R");
3746 +
        let ty = try getTypeInScopeOf(&a, result.root, "R");
2903 3747
2904 -
    let okPayload = getUnionVariantPayload(ty, "Ok");
2905 -
    try testing::expect(okPayload == super::Type::I32);
3748 +
        let okPayload = getUnionVariantPayload(ty, "Ok");
3749 +
        try testing::expect(okPayload == super::Type::I32);
2906 3750
2907 -
    let errPayload = getUnionVariantPayload(ty, "Err");
2908 -
    try testing::expect(errPayload == super::Type::Bool);
3751 +
        let errPayload = getUnionVariantPayload(ty, "Err");
3752 +
        try testing::expect(errPayload == super::Type::Bool);
2909 3753
2910 -
    let stmt = try getBlockStmt(result.root, 1);
2911 -
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3754 +
        let stmt = try getBlockStmt(result.root, 1);
3755 +
        try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2912 3756
2913 -
    // TODO: Test payload type.
3757 +
        // TODO: Test payload type.
3758 +
    }
2914 3759
}
2915 3760
2916 3761
@test unsafe fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) {
2917 -
    let mut a = testResolver();
2918 -
    let program = "union R { Ok = 7, Err = 11 } R::Ok;";
2919 -
    let result = try resolveProgramStr(&mut a, program);
2920 -
    try expectNoErrors(&result);
3762 +
    let mut testArena275 = testArena();
3763 +
    let testStorage275: 'test275 = &mut testArena275 in {
3764 +
        let mut a = testResolver(testStorage275);
3765 +
        let program = "union R { Ok = 7, Err = 11 } R::Ok;";
3766 +
        let result = try resolveProgramStr(&mut a, program);
3767 +
        try expectNoErrors(&result);
2921 3768
2922 -
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2923 -
    let stmt = try getBlockStmt(result.root, 1);
2924 -
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3769 +
        let ty = try getTypeInScopeOf(&a, result.root, "R");
3770 +
        let stmt = try getBlockStmt(result.root, 1);
3771 +
        try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3772 +
    }
2925 3773
}
2926 3774
2927 3775
@test unsafe fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) {
2928 -
    let mut a = testResolver();
2929 -
    let program = "union R { Ok(i32), Error(bool) } R::Ok(true);";
2930 -
    let result = try resolveProgramStr(&mut a, program);
2931 -
    let err = try expectError(&result);
2932 -
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
3776 +
    let mut testArena276 = testArena();
3777 +
    let testStorage276: 'test276 = &mut testArena276 in {
3778 +
        let mut a = testResolver(testStorage276);
3779 +
        let program = "union R { Ok(i32), Error(bool) } R::Ok(true);";
3780 +
        let result = try resolveProgramStr(&mut a, program);
3781 +
        let err = try expectError(&result);
3782 +
        try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2933 3783
2934 -
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2935 -
    let payload = getUnionVariantPayload(ty, "Ok");
2936 -
    try testing::expect(payload == super::Type::I32);
3784 +
        let ty = try getTypeInScopeOf(&a, result.root, "R");
3785 +
        let payload = getUnionVariantPayload(ty, "Ok");
3786 +
        try testing::expect(payload == super::Type::I32);
2937 3787
2938 -
    let errNode = err.node
2939 -
        else throw testing::TestError::Failed;
2940 -
    let case ast::NodeValue::Bool(_) = errNode.value
2941 -
        else throw testing::TestError::Failed;
3788 +
        let errNode = err.node
3789 +
            else throw testing::TestError::Failed;
3790 +
        let case ast::NodeValue::Bool(_) = errNode.value
3791 +
            else throw testing::TestError::Failed;
3792 +
    }
2942 3793
}
2943 3794
2944 3795
@test unsafe fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) {
2945 -
    let mut a = testResolver();
2946 -
    let program = "union Status { Ok, Error } Status::Ok(42);";
2947 -
    let result = try resolveProgramStr(&mut a, program);
2948 -
    let err = try expectError(&result);
3796 +
    let mut testArena277 = testArena();
3797 +
    let testStorage277: 'test277 = &mut testArena277 in {
3798 +
        let mut a = testResolver(testStorage277);
3799 +
        let program = "union Status { Ok, Error } Status::Ok(42);";
3800 +
        let result = try resolveProgramStr(&mut a, program);
3801 +
        let err = try expectError(&result);
2949 3802
2950 -
    let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind
2951 -
        else throw testing::TestError::Failed;
2952 -
    let node = err.node
2953 -
        else throw testing::TestError::Failed;
2954 -
    let case ast::NodeValue::Call(_) = node.value
2955 -
        else throw testing::TestError::Failed;
3803 +
        let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind
3804 +
            else throw testing::TestError::Failed;
3805 +
        let node = err.node
3806 +
            else throw testing::TestError::Failed;
3807 +
        let case ast::NodeValue::Call(_) = node.value
3808 +
            else throw testing::TestError::Failed;
3809 +
    }
2956 3810
}
2957 3811
2958 3812
@test unsafe fn testResolveUnionVariantUnknown() throws (testing::TestError) {
2959 -
    let mut a = testResolver();
2960 -
    let program = "union Status { Ok, Error } Status::Unknown;";
2961 -
    let result = try resolveProgramStr(&mut a, program);
2962 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
3813 +
    let mut testArena278 = testArena();
3814 +
    let testStorage278: 'test278 = &mut testArena278 in {
3815 +
        let mut a = testResolver(testStorage278);
3816 +
        let program = "union Status { Ok, Error } Status::Unknown;";
3817 +
        let result = try resolveProgramStr(&mut a, program);
3818 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
3819 +
    }
2963 3820
}
2964 3821
2965 3822
@test unsafe fn testResolveScopeAccessUndefinedType() throws (testing::TestError) {
2966 -
    let mut a = testResolver();
2967 -
    let result = try resolveProgramStr(&mut a, "Unknown::X;");
2968 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
3823 +
    let mut testArena279 = testArena();
3824 +
    let testStorage279: 'test279 = &mut testArena279 in {
3825 +
        let mut a = testResolver(testStorage279);
3826 +
        let result = try resolveProgramStr(&mut a, "Unknown::X;");
3827 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
3828 +
    }
2969 3829
}
2970 3830
2971 3831
@test unsafe fn testResolveUnionVariantVoidPayload() throws (testing::TestError) {
2972 -
    let mut a = testResolver();
2973 -
    let program = "union R { Success(i32), Pending } R::Pending;";
2974 -
    let result = try resolveProgramStr(&mut a, program);
2975 -
    try expectNoErrors(&result);
3832 +
    let mut testArena280 = testArena();
3833 +
    let testStorage280: 'test280 = &mut testArena280 in {
3834 +
        let mut a = testResolver(testStorage280);
3835 +
        let program = "union R { Success(i32), Pending } R::Pending;";
3836 +
        let result = try resolveProgramStr(&mut a, program);
3837 +
        try expectNoErrors(&result);
2976 3838
2977 -
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2978 -
    let payload = getUnionVariantPayload(ty, "Pending");
2979 -
    try testing::expect(payload == super::Type::Void);
3839 +
        let ty = try getTypeInScopeOf(&a, result.root, "R");
3840 +
        let payload = getUnionVariantPayload(ty, "Pending");
3841 +
        try testing::expect(payload == super::Type::Void);
2980 3842
2981 -
    let stmt = try getBlockStmt(result.root, 1);
2982 -
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3843 +
        let stmt = try getBlockStmt(result.root, 1);
3844 +
        try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3845 +
    }
2983 3846
}
2984 3847
2985 3848
@test unsafe fn testResolveUnionVariantRecordPayload() throws (testing::TestError) {
2986 -
    let mut a = testResolver();
2987 -
    let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });";
2988 -
    let result = try resolveProgramStr(&mut a, program);
2989 -
    try expectNoErrors(&result);
3849 +
    let mut testArena281 = testArena();
3850 +
    let testStorage281: 'test281 = &mut testArena281 in {
3851 +
        let mut a = testResolver(testStorage281);
3852 +
        let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });";
3853 +
        let result = try resolveProgramStr(&mut a, program);
3854 +
        try expectNoErrors(&result);
2990 3855
2991 -
    let ty = try getTypeInScopeOf(&a, result.root, "S");
2992 -
    let stmt = try getBlockStmt(result.root, 2);
2993 -
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3856 +
        let ty = try getTypeInScopeOf(&a, result.root, "S");
3857 +
        let stmt = try getBlockStmt(result.root, 2);
3858 +
        try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3859 +
    }
2994 3860
}
2995 3861
2996 3862
@test unsafe fn testResolveBuiltinSizeOf() throws (testing::TestError) {
2997 3863
    try resolveAndExpectConstExpr("@sizeOf(u8)", 1);
2998 3864
    try resolveAndExpectConstExpr("@sizeOf(u16)", 2);
3043 3909
    try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1);
3044 3910
    try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4);
3045 3911
}
3046 3912
3047 3913
@test unsafe fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) {
3048 -
    let mut a = testResolver();
3049 -
    let program = "record T { x: u8, y: u32 } @sizeOf(T);";
3050 -
    let result = try resolveProgramStr(&mut a, program);
3051 -
    try expectNoErrors(&result);
3914 +
    let mut testArena282 = testArena();
3915 +
    let testStorage282: 'test282 = &mut testArena282 in {
3916 +
        let mut a = testResolver(testStorage282);
3917 +
        let program = "record T { x: u8, y: u32 } @sizeOf(T);";
3918 +
        let result = try resolveProgramStr(&mut a, program);
3919 +
        try expectNoErrors(&result);
3052 3920
3053 -
    let stmt = try getBlockStmt(result.root, 1);
3054 -
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3055 -
    try expectConstInt(&a, expr, 8);
3921 +
        let stmt = try getBlockStmt(result.root, 1);
3922 +
        let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3923 +
        try expectConstInt(&a, expr, 8);
3924 +
    }
3056 3925
}
3057 3926
3058 3927
@test unsafe fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) {
3059 -
    let mut a = testResolver();
3060 -
    let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);";
3061 -
    let result = try resolveProgramStr(&mut a, program);
3062 -
    try expectNoErrors(&result);
3928 +
    let mut testArena283 = testArena();
3929 +
    let testStorage283: 'test283 = &mut testArena283 in {
3930 +
        let mut a = testResolver(testStorage283);
3931 +
        let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);";
3932 +
        let result = try resolveProgramStr(&mut a, program);
3933 +
        try expectNoErrors(&result);
3063 3934
3064 -
    let stmt = try getBlockStmt(result.root, 1);
3065 -
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3066 -
    try expectConstInt(&a, expr, 8);
3935 +
        let stmt = try getBlockStmt(result.root, 1);
3936 +
        let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3937 +
        try expectConstInt(&a, expr, 8);
3938 +
    }
3067 3939
}
3068 3940
3069 3941
@test unsafe fn testResolveAlignAnnotation() throws (testing::TestError) {
3070 3942
    {
3071 -
        let mut a = testResolver();
3072 -
        let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;");
3073 -
        try expectNoErrors(&result);
3074 -
3075 -
        let stmt = try getBlockStmt(result.root, 0);
3076 -
        let sym = super::symbolFor(&a, stmt)
3077 -
            else throw testing::TestError::Failed;
3078 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
3079 -
            else throw testing::TestError::Failed;
3080 -
        let layout = super::getLayout(&a, sym.node, valType);
3081 -
        try testing::expect(layout.alignment == 8);
3943 +
        let mut testArena284 = testArena();
3944 +
        let testStorage284: 'test284 = &mut testArena284 in {
3945 +
            let mut a = testResolver(testStorage284);
3946 +
            let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;");
3947 +
            try expectNoErrors(&result);
3948 +
3949 +
            let stmt = try getBlockStmt(result.root, 0);
3950 +
            let sym = super::symbolFor(&a, stmt)
3951 +
                else throw testing::TestError::Failed;
3952 +
            let case super::SymbolData::Value { type: valType, .. } = sym.data
3953 +
                else throw testing::TestError::Failed;
3954 +
            let layout = super::getLayout(&a, sym.node, valType);
3955 +
            try testing::expect(layout.alignment == 8);
3956 +
        }
3082 3957
    } {
3083 -
        let mut a = testResolver();
3084 -
        let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;");
3085 -
        let err = try expectError(&result);
3086 -
        let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3087 -
            else throw testing::TestError::Failed;
3088 -
        try testing::expect(val == 3);
3958 +
        let mut testArena285 = testArena();
3959 +
        let testStorage285: 'test285 = &mut testArena285 in {
3960 +
            let mut a = testResolver(testStorage285);
3961 +
            let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;");
3962 +
            let err = try expectError(&result);
3963 +
            let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3964 +
                else throw testing::TestError::Failed;
3965 +
            try testing::expect(val == 3);
3966 +
        }
3089 3967
    } {
3090 -
        let mut a = testResolver();
3091 -
        let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;");
3092 -
        let err = try expectError(&result);
3093 -
        let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3094 -
            else throw testing::TestError::Failed;
3095 -
        try testing::expect(val == 7);
3968 +
        let mut testArena286 = testArena();
3969 +
        let testStorage286: 'test286 = &mut testArena286 in {
3970 +
            let mut a = testResolver(testStorage286);
3971 +
            let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;");
3972 +
            let err = try expectError(&result);
3973 +
            let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3974 +
                else throw testing::TestError::Failed;
3975 +
            try testing::expect(val == 7);
3976 +
        }
3096 3977
    }
3097 3978
}
3098 3979
3099 3980
@test unsafe fn testResolveVoidAssignmentError() throws (testing::TestError) {
3100 3981
    {
3101 -
        let mut a = testResolver();
3102 -
        let program = "fn voidFn() {} let _ = voidFn();";
3103 -
        let result = try resolveProgramStr(&mut a, program);
3104 -
        try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3982 +
        let mut testArena287 = testArena();
3983 +
        let testStorage287: 'test287 = &mut testArena287 in {
3984 +
            let mut a = testResolver(testStorage287);
3985 +
            let program = "fn voidFn() {} let _ = voidFn();";
3986 +
            let result = try resolveProgramStr(&mut a, program);
3987 +
            try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3988 +
        }
3105 3989
    } {
3106 -
        let mut a = testResolver();
3107 -
        let program = "fn voidFn() {} let x = voidFn();";
3108 -
        let result = try resolveProgramStr(&mut a, program);
3109 -
        try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3990 +
        let mut testArena288 = testArena();
3991 +
        let testStorage288: 'test288 = &mut testArena288 in {
3992 +
            let mut a = testResolver(testStorage288);
3993 +
            let program = "fn voidFn() {} let x = voidFn();";
3994 +
            let result = try resolveProgramStr(&mut a, program);
3995 +
            try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3996 +
        }
3110 3997
    }
3111 3998
}
3112 3999
3113 4000
//
3114 4001
// Module Declaration Tests
3115 4002
//
3116 4003
3117 4004
@test unsafe fn testResolveEmptyMod() throws (testing::TestError) {
3118 -
    let mut a = testResolver();
3119 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3120 -
    let mut graph = &mut MODULE_GRAPH;
4005 +
    let mut testArena289 = testArena();
4006 +
    let testStorage289: 'test289 = &mut testArena289 in {
4007 +
        let mut a = testResolver(testStorage289);
4008 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4009 +
        let mut graph = &mut MODULE_GRAPH;
3121 4010
3122 -
    let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena);
3123 -
    let childId = try registerModule(graph, rootId, "child", "{}", &mut arena);
3124 -
    let result = try resolveModuleTree(&mut a, rootId);
3125 -
    try expectNoErrors(&result);
4011 +
        let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena);
4012 +
        let childId = try registerModule(graph, rootId, "child", "{}", &mut arena);
4013 +
        let result = try resolveModuleTree(&mut a, rootId);
4014 +
        try expectNoErrors(&result);
4015 +
    }
3126 4016
}
3127 4017
3128 4018
@test unsafe fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) {
3129 -
    let mut a = testResolver();
3130 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4019 +
    let mut testArena290 = testArena();
4020 +
    let testStorage290: 'test290 = &mut testArena290 in {
4021 +
        let mut a = testResolver(testStorage290);
4022 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3131 4023
3132 -
    // Register root and util modules.
3133 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena);
3134 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena);
4024 +
        // Register root and util modules.
4025 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena);
4026 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena);
3135 4027
3136 -
    // Resolve should fail: the parent module is not in scope.
3137 -
    let result = try resolveModuleTree(&mut a, rootId);
3138 -
    let err = try expectError(&result);
3139 -
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3140 -
        else throw testing::TestError::Failed;
3141 -
    try testing::expect(mem::eq(name, "helper"));
4028 +
        // Resolve should fail: the parent module is not in scope.
4029 +
        let result = try resolveModuleTree(&mut a, rootId);
4030 +
        let err = try expectError(&result);
4031 +
        let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
4032 +
            else throw testing::TestError::Failed;
4033 +
        try testing::expect(mem::eq(name, "helper"));
4034 +
    }
3142 4035
}
3143 4036
3144 4037
@test unsafe fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) {
3145 -
    let mut a = testResolver();
3146 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4038 +
    let mut testArena291 = testArena();
4039 +
    let testStorage291: 'test291 = &mut testArena291 in {
4040 +
        let mut a = testResolver(testStorage291);
4041 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3147 4042
3148 -
    // Register root and util modules.
3149 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena);
3150 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena);
4043 +
        // Register root and util modules.
4044 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena);
4045 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena);
3151 4046
3152 -
    // Resolve should succeed: parent can access child.
3153 -
    let result = try resolveModuleTree(&mut a, rootId);
3154 -
    try expectNoErrors(&result);
4047 +
        // Resolve should succeed: parent can access child.
4048 +
        let result = try resolveModuleTree(&mut a, rootId);
4049 +
        try expectNoErrors(&result);
4050 +
    }
3155 4051
}
3156 4052
3157 4053
@test unsafe fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) {
3158 -
    let mut a = testResolver();
3159 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4054 +
    let mut testArena292 = testArena();
4055 +
    let testStorage292: 'test292 = &mut testArena292 in {
4056 +
        let mut a = testResolver(testStorage292);
4057 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3160 4058
3161 -
    // Register root with two sibling modules.
3162 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3163 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena);
3164 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
4059 +
        // Register root with two sibling modules.
4060 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
4061 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena);
4062 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
3165 4063
3166 -
    // Resolve should fail: siblings can't access each other directly.
3167 -
    let result = try resolveModuleTree(&mut a, rootId);
3168 -
    let err = try expectError(&result);
3169 -
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3170 -
        else throw testing::TestError::Failed;
3171 -
    try testing::expect(mem::eq(name, "patrick"));
4064 +
        // Resolve should fail: siblings can't access each other directly.
4065 +
        let result = try resolveModuleTree(&mut a, rootId);
4066 +
        let err = try expectError(&result);
4067 +
        let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
4068 +
            else throw testing::TestError::Failed;
4069 +
        try testing::expect(mem::eq(name, "patrick"));
4070 +
    }
3172 4071
}
3173 4072
3174 4073
@test unsafe fn testResolveSiblingModulesViaRoot() throws (testing::TestError) {
3175 -
    let mut a = testResolver();
3176 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4074 +
    let mut testArena293 = testArena();
4075 +
    let testStorage293: 'test293 = &mut testArena293 in {
4076 +
        let mut a = testResolver(testStorage293);
4077 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3177 4078
3178 -
    // Register root with two sibling modules.
3179 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3180 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena);
3181 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
4079 +
        // Register root with two sibling modules.
4080 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
4081 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena);
4082 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
3182 4083
3183 -
    // Resolve should succeed: siblings can access each other via root.
3184 -
    let result = try resolveModuleTree(&mut a, rootId);
3185 -
    try expectNoErrors(&result);
4084 +
        // Resolve should succeed: siblings can access each other via root.
4085 +
        let result = try resolveModuleTree(&mut a, rootId);
4086 +
        try expectNoErrors(&result);
4087 +
    }
3186 4088
}
3187 4089
3188 4090
@test unsafe fn testResolveModuleMutualRecursion() throws (testing::TestError) {
3189 -
    let mut a = testResolver();
3190 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4091 +
    let mut testArena294 = testArena();
4092 +
    let testStorage294: 'test294 = &mut testArena294 in {
4093 +
        let mut a = testResolver(testStorage294);
4094 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3191 4095
3192 -
    // Register root with two sibling modules that call each other.
3193 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena);
3194 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena);
3195 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena);
4096 +
        // Register root with two sibling modules that call each other.
4097 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena);
4098 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena);
4099 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena);
3196 4100
3197 -
    // Resolve should succeed: cyclic use is allowed.
3198 -
    let result = try resolveModuleTree(&mut a, rootId);
3199 -
    try expectNoErrors(&result);
4101 +
        // Resolve should succeed: cyclic use is allowed.
4102 +
        let result = try resolveModuleTree(&mut a, rootId);
4103 +
        try expectNoErrors(&result);
4104 +
    }
3200 4105
}
3201 4106
3202 4107
@test unsafe fn testResolveAccessModuleType() throws (testing::TestError) {
3203 -
    let mut a = testResolver();
3204 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4108 +
    let mut testArena295 = testArena();
4109 +
    let testStorage295: 'test295 = &mut testArena295 in {
4110 +
        let mut a = testResolver(testStorage295);
4111 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3205 4112
3206 -
    // Register root with types module containing a record.
3207 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3208 -
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena);
3209 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena);
4113 +
        // Register root with types module containing a record.
4114 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
4115 +
        let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena);
4116 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena);
3210 4117
3211 -
    // Resolve should succeed: types can be accessed.
3212 -
    let result = try resolveModuleTree(&mut a, rootId);
3213 -
    try expectNoErrors(&result);
4118 +
        // Resolve should succeed: types can be accessed.
4119 +
        let result = try resolveModuleTree(&mut a, rootId);
4120 +
        try expectNoErrors(&result);
4121 +
    }
3214 4122
}
3215 4123
3216 4124
@test unsafe fn testResolveAccessModuleConstant() throws (testing::TestError) {
3217 -
    let mut a = testResolver();
3218 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4125 +
    let mut testArena296 = testArena();
4126 +
    let testStorage296: 'test296 = &mut testArena296 in {
4127 +
        let mut a = testResolver(testStorage296);
4128 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3219 4129
3220 -
    // Register root with constants module.
3221 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3222 -
    let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena);
3223 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena);
4130 +
        // Register root with constants module.
4131 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
4132 +
        let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena);
4133 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena);
3224 4134
3225 -
    // Resolve should succeed: constants can be accessed.
3226 -
    let result = try resolveModuleTree(&mut a, rootId);
3227 -
    try expectNoErrors(&result);
4135 +
        // Resolve should succeed: constants can be accessed.
4136 +
        let result = try resolveModuleTree(&mut a, rootId);
4137 +
        try expectNoErrors(&result);
4138 +
    }
3228 4139
}
3229 4140
3230 4141
@test unsafe fn testResolveRootSymbolMustBeImported() throws (testing::TestError) {
3231 -
    let mut a = testResolver();
3232 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4142 +
    let mut testArena297 = testArena();
4143 +
    let testStorage297: 'test297 = &mut testArena297 in {
4144 +
        let mut a = testResolver(testStorage297);
4145 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3233 4146
3234 -
    // Register deeply nested modules: `root::app::services::auth`.
3235 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena);
3236 -
    let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena);
4147 +
        // Register deeply nested modules: `root::app::services::auth`.
4148 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena);
4149 +
        let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena);
3237 4150
3238 -
    // Resolve should fail: the `root` module must be imported.
3239 -
    let result = try resolveModuleTree(&mut a, rootId);
3240 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root"));
4151 +
        // Resolve should fail: the `root` module must be imported.
4152 +
        let result = try resolveModuleTree(&mut a, rootId);
4153 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root"));
4154 +
    }
3241 4155
}
3242 4156
3243 4157
@test unsafe fn testResolveUseImportsNestedSymbol() throws (testing::TestError) {
3244 -
    let mut a = testResolver();
3245 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4158 +
    let mut testArena298 = testArena();
4159 +
    let testStorage298: 'test298 = &mut testArena298 in {
4160 +
        let mut a = testResolver(testStorage298);
4161 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3246 4162
3247 -
    // Register deeply nested modules: `root::app::services::auth`.
3248 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena);
3249 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena);
3250 -
    let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena);
3251 -
    let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena);
3252 -
    let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena);
3253 -
    let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena);
4163 +
        // Register deeply nested modules: `root::app::services::auth`.
4164 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena);
4165 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena);
4166 +
        let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena);
4167 +
        let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena);
4168 +
        let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena);
4169 +
        let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena);
3254 4170
3255 -
    // Resolve should succeed: use imports the module symbol.
3256 -
    let result = try resolveModuleTree(&mut a, rootId);
3257 -
    try expectNoErrors(&result);
4171 +
        // Resolve should succeed: use imports the module symbol.
4172 +
        let result = try resolveModuleTree(&mut a, rootId);
4173 +
        try expectNoErrors(&result);
4174 +
    }
3258 4175
}
3259 4176
3260 4177
@test unsafe fn testResolveUseNonExistentModule() throws (testing::TestError) {
3261 -
    let mut a = testResolver();
3262 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4178 +
    let mut testArena299 = testArena();
4179 +
    let testStorage299: 'test299 = &mut testArena299 in {
4180 +
        let mut a = testResolver(testStorage299);
4181 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3263 4182
3264 -
    // Register root with app trying to use a non-existent module.
3265 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena);
3266 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena);
4183 +
        // Register root with app trying to use a non-existent module.
4184 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena);
4185 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena);
3267 4186
3268 -
    // Resolve should fail: module doesn't exist.
3269 -
    let result = try resolveModuleTree(&mut a, rootId);
3270 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown"));
4187 +
        // Resolve should fail: module doesn't exist.
4188 +
        let result = try resolveModuleTree(&mut a, rootId);
4189 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown"));
4190 +
    }
3271 4191
}
3272 4192
3273 4193
@test unsafe fn testResolveUsePrivateFn() throws (testing::TestError) {
3274 -
    let mut a = testResolver();
3275 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4194 +
    let mut testArena300 = testArena();
4195 +
    let testStorage300: 'test300 = &mut testArena300 in {
4196 +
        let mut a = testResolver(testStorage300);
4197 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3276 4198
3277 -
    // Register root with util module containing a private function.
3278 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena);
3279 -
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena);
3280 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena);
4199 +
        // Register root with util module containing a private function.
4200 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena);
4201 +
        let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena);
4202 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena);
3281 4203
3282 -
    // Resolve should fail: function is not public.
3283 -
    let result = try resolveModuleTree(&mut a, rootId);
3284 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4204 +
        // Resolve should fail: function is not public.
4205 +
        let result = try resolveModuleTree(&mut a, rootId);
4206 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4207 +
    }
3285 4208
}
3286 4209
3287 4210
@test unsafe fn testResolveUsePrivateMod() throws (testing::TestError) {
3288 -
    let mut a = testResolver();
3289 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4211 +
    let mut testArena301 = testArena();
4212 +
    let testStorage301: 'test301 = &mut testArena301 in {
4213 +
        let mut a = testResolver(testStorage301);
4214 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3290 4215
3291 -
    // Register root with public and private child modules.
3292 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena);
3293 -
    let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena);
3294 -
    let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena);
4216 +
        // Register root with public and private child modules.
4217 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena);
4218 +
        let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena);
4219 +
        let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena);
3295 4220
3296 -
    // Resolve should fail: module is not public.
3297 -
    let result = try resolveModuleTree(&mut a, rootId);
3298 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4221 +
        // Resolve should fail: module is not public.
4222 +
        let result = try resolveModuleTree(&mut a, rootId);
4223 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4224 +
    }
3299 4225
}
3300 4226
3301 4227
@test unsafe fn testResolveUsePublicMod() throws (testing::TestError) {
3302 -
    let mut a = testResolver();
3303 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4228 +
    let mut testArena302 = testArena();
4229 +
    let testStorage302: 'test302 = &mut testArena302 in {
4230 +
        let mut a = testResolver(testStorage302);
4231 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3304 4232
3305 -
    // Register root with public and private child modules.
3306 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena);
3307 -
    let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena);
3308 -
    let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena);
4233 +
        // Register root with public and private child modules.
4234 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena);
4235 +
        let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena);
4236 +
        let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena);
3309 4237
3310 -
    // Resolve should succeed: module is public.
3311 -
    let result = try resolveModuleTree(&mut a, rootId);
3312 -
    try expectNoErrors(&result);
4238 +
        // Resolve should succeed: module is public.
4239 +
        let result = try resolveModuleTree(&mut a, rootId);
4240 +
        try expectNoErrors(&result);
4241 +
    }
3313 4242
}
3314 4243
3315 4244
@test unsafe fn testResolveUseNonPublicType() throws (testing::TestError) {
3316 -
    let mut a = testResolver();
3317 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4245 +
    let mut testArena303 = testArena();
4246 +
    let testStorage303: 'test303 = &mut testArena303 in {
4247 +
        let mut a = testResolver(testStorage303);
4248 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3318 4249
3319 -
    // Register root with types module containing a private record.
3320 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3321 -
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena);
3322 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena);
4250 +
        // Register root with types module containing a private record.
4251 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
4252 +
        let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena);
4253 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena);
3323 4254
3324 -
    // Resolve should fail: record is not public.
3325 -
    let result = try resolveModuleTree(&mut a, rootId);
3326 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv"));
4255 +
        // Resolve should fail: record is not public.
4256 +
        let result = try resolveModuleTree(&mut a, rootId);
4257 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv"));
4258 +
    }
3327 4259
}
3328 4260
3329 4261
@test unsafe fn testResolveImportPublicType() throws (testing::TestError) {
3330 -
    let mut a = testResolver();
3331 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4262 +
    let mut testArena304 = testArena();
4263 +
    let testStorage304: 'test304 = &mut testArena304 in {
4264 +
        let mut a = testResolver(testStorage304);
4265 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3332 4266
3333 -
    // Register root with types module containing a public record.
3334 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3335 -
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena);
3336 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena);
4267 +
        // Register root with types module containing a public record.
4268 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
4269 +
        let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena);
4270 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena);
3337 4271
3338 -
    // Resolve should succeed: record is public.
3339 -
    let result = try resolveModuleTree(&mut a, rootId);
3340 -
    try expectNoErrors(&result);
4272 +
        // Resolve should succeed: record is public.
4273 +
        let result = try resolveModuleTree(&mut a, rootId);
4274 +
        try expectNoErrors(&result);
4275 +
    }
3341 4276
}
3342 4277
3343 4278
@test unsafe fn testResolveUseNonPublicStatic() throws (testing::TestError) {
3344 -
    let mut a = testResolver();
3345 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4279 +
    let mut testArena305 = testArena();
4280 +
    let testStorage305: 'test305 = &mut testArena305 in {
4281 +
        let mut a = testResolver(testStorage305);
4282 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3346 4283
3347 -
    // Register root with statics module containing a private static.
3348 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3349 -
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena);
3350 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena);
4284 +
        // Register root with statics module containing a private static.
4285 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
4286 +
        let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena);
4287 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena);
3351 4288
3352 -
    // Resolve should fail: static is not public.
3353 -
    let result = try resolveModuleTree(&mut a, rootId);
3354 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE"));
4289 +
        // Resolve should fail: static is not public.
4290 +
        let result = try resolveModuleTree(&mut a, rootId);
4291 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE"));
4292 +
    }
3355 4293
}
3356 4294
3357 4295
@test unsafe fn testResolveImportPublicStatic() throws (testing::TestError) {
3358 -
    let mut a = testResolver();
3359 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4296 +
    let mut testArena306 = testArena();
4297 +
    let testStorage306: 'test306 = &mut testArena306 in {
4298 +
        let mut a = testResolver(testStorage306);
4299 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3360 4300
3361 -
    // Register root with statics module containing a public static.
3362 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3363 -
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena);
3364 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena);
4301 +
        // Register root with statics module containing a public static.
4302 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
4303 +
        let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena);
4304 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena);
3365 4305
3366 -
    // Resolve should succeed: static is public.
3367 -
    let result = try resolveModuleTree(&mut a, rootId);
3368 -
    try expectNoErrors(&result);
4306 +
        // Resolve should succeed: static is public.
4307 +
        let result = try resolveModuleTree(&mut a, rootId);
4308 +
        try expectNoErrors(&result);
4309 +
    }
3369 4310
}
3370 4311
3371 4312
@test unsafe fn testResolveAccessSuper() throws (testing::TestError) {
3372 4313
    {
3373 -
        let mut a = testResolver();
3374 -
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3375 -
3376 -
        // Test function access.
3377 -
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena);
3378 -
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena);
3379 -
        let result = try resolveModuleTree(&mut a, rootId);
3380 -
        try expectNoErrors(&result);
4314 +
        let mut testArena307 = testArena();
4315 +
        let testStorage307: 'test307 = &mut testArena307 in {
4316 +
            let mut a = testResolver(testStorage307);
4317 +
            let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4318 +
4319 +
            // Test function access.
4320 +
            let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena);
4321 +
            let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena);
4322 +
            let result = try resolveModuleTree(&mut a, rootId);
4323 +
            try expectNoErrors(&result);
4324 +
        }
3381 4325
    } {
3382 -
        let mut a = testResolver();
3383 -
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4326 +
        let mut testArena308 = testArena();
4327 +
        let testStorage308: 'test308 = &mut testArena308 in {
4328 +
            let mut a = testResolver(testStorage308);
4329 +
            let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3384 4330
3385 -
        // Test type access.
3386 -
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena);
3387 -
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena);
3388 -
        let result = try resolveModuleTree(&mut a, rootId);
3389 -
        try expectNoErrors(&result);
4331 +
            // Test type access.
4332 +
            let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena);
4333 +
            let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena);
4334 +
            let result = try resolveModuleTree(&mut a, rootId);
4335 +
            try expectNoErrors(&result);
4336 +
        }
3390 4337
    }
3391 4338
}
3392 4339
3393 4340
/// Test nested super access to union variants (e.g. `super::E::A`).
3394 4341
@test unsafe fn testResolveSuperUnionVariant() throws (testing::TestError) {
3395 -
    let mut a = testResolver();
3396 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4342 +
    let mut testArena309 = testArena();
4343 +
    let testStorage309: 'test309 = &mut testArena309 in {
4344 +
        let mut a = testResolver(testStorage309);
4345 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3397 4346
3398 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena);
3399 -
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c",
3400 -
        "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }",
3401 -
        &mut arena);
3402 -
    let result = try resolveModuleTree(&mut a, rootId);
3403 -
    try expectNoErrors(&result);
4347 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena);
4348 +
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c",
4349 +
            "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }",
4350 +
            &mut arena);
4351 +
        let result = try resolveModuleTree(&mut a, rootId);
4352 +
        try expectNoErrors(&result);
4353 +
    }
3404 4354
}
3405 4355
3406 4356
@test unsafe fn testResolveUseSuper() throws (testing::TestError) {
3407 -
    let mut a = testResolver();
3408 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4357 +
    let mut testArena310 = testArena();
4358 +
    let testStorage310: 'test310 = &mut testArena310 in {
4359 +
        let mut a = testResolver(testStorage310);
4360 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3409 4361
3410 -
    // Register root with a function, and a child module that uses super to access it.
3411 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena);
3412 -
    let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena);
3413 -
    let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena);
4362 +
        // Register root with a function, and a child module that uses super to access it.
4363 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena);
4364 +
        let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena);
4365 +
        let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena);
3414 4366
3415 -
    // Resolve should succeed - super allows accessing parent module.
3416 -
    let result = try resolveModuleTree(&mut a, rootId);
3417 -
    try expectNoErrors(&result);
4367 +
        // Resolve should succeed - super allows accessing parent module.
4368 +
        let result = try resolveModuleTree(&mut a, rootId);
4369 +
        try expectNoErrors(&result);
4370 +
    }
3418 4371
}
3419 4372
3420 4373
@test unsafe fn testResolveModNotFound() throws (testing::TestError) {
3421 -
    let mut a = testResolver();
3422 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4374 +
    let mut testArena311 = testArena();
4375 +
    let testStorage311: 'test311 = &mut testArena311 in {
4376 +
        let mut a = testResolver(testStorage311);
4377 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3423 4378
3424 -
    // Register root that declares a module that doesn't exist.
3425 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena);
4379 +
        // Register root that declares a module that doesn't exist.
4380 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena);
3426 4381
3427 -
    // Resolve should fail: module doesn't exist.
3428 -
    let result = try resolveModuleTree(&mut a, rootId);
3429 -
    let err = try expectError(&result);
3430 -
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3431 -
        else throw testing::TestError::Failed;
3432 -
    try testing::expect(mem::eq(name, "unknown"));
4382 +
        // Resolve should fail: module doesn't exist.
4383 +
        let result = try resolveModuleTree(&mut a, rootId);
4384 +
        let err = try expectError(&result);
4385 +
        let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
4386 +
            else throw testing::TestError::Failed;
4387 +
        try testing::expect(mem::eq(name, "unknown"));
4388 +
    }
3433 4389
}
3434 4390
3435 4391
@test unsafe fn testResolveDuplicateSubModule() throws (testing::TestError) {
3436 -
    let mut a = testResolver();
3437 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4392 +
    let mut testArena312 = testArena();
4393 +
    let testStorage312: 'test312 = &mut testArena312 in {
4394 +
        let mut a = testResolver(testStorage312);
4395 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3438 4396
3439 -
    // Register root that declares a module twice.
3440 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena);
3441 -
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
4397 +
        // Register root that declares a module twice.
4398 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena);
4399 +
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
3442 4400
3443 -
    // Resolve should fail: can't declare the same module twice.
3444 -
    let result = try resolveModuleTree(&mut a, rootId);
3445 -
    let err = try expectError(&result);
3446 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
4401 +
        // Resolve should fail: can't declare the same module twice.
4402 +
        let result = try resolveModuleTree(&mut a, rootId);
4403 +
        let err = try expectError(&result);
4404 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
4405 +
    }
3447 4406
}
3448 4407
3449 4408
@test unsafe fn testResolveUseSubModule() throws (testing::TestError) {
3450 -
    let mut a = testResolver();
3451 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4409 +
    let mut testArena313 = testArena();
4410 +
    let testStorage313: 'test313 = &mut testArena313 in {
4411 +
        let mut a = testResolver(testStorage313);
4412 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3452 4413
3453 -
    // Register root that declares and imports the same module.
3454 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena);
3455 -
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
4414 +
        // Register root that declares and imports the same module.
4415 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena);
4416 +
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
3456 4417
3457 -
    // Resolve should fail: Both `mod` and `use` are trying to create the same binding.
3458 -
    let result = try resolveModuleTree(&mut a, rootId);
3459 -
    let err = try expectError(&result);
3460 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
4418 +
        // Resolve should fail: Both `mod` and `use` are trying to create the same binding.
4419 +
        let result = try resolveModuleTree(&mut a, rootId);
4420 +
        let err = try expectError(&result);
4421 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
4422 +
    }
3461 4423
}
3462 4424
3463 4425
@test unsafe fn testResolveDuplicateUse() throws (testing::TestError) {
3464 -
    let mut a = testResolver();
3465 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4426 +
    let mut testArena314 = testArena();
4427 +
    let testStorage314: 'test314 = &mut testArena314 in {
4428 +
        let mut a = testResolver(testStorage314);
4429 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3466 4430
3467 -
    // Register a module that imports the same module twice.
3468 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena);
3469 -
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena);
4431 +
        // Register a module that imports the same module twice.
4432 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena);
4433 +
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena);
3470 4434
3471 -
    // Resolve should fail.
3472 -
    let result = try resolveModuleTree(&mut a, rootId);
3473 -
    let err = try expectError(&result);
3474 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root"));
4435 +
        // Resolve should fail.
4436 +
        let result = try resolveModuleTree(&mut a, rootId);
4437 +
        let err = try expectError(&result);
4438 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root"));
4439 +
    }
3475 4440
}
3476 4441
3477 4442
/// Test that opaque pointers are allowed in record fields.
3478 4443
@test unsafe fn testOpaquePointerInRecordField() throws (testing::TestError) {
3479 -
    let mut a = testResolver();
3480 -
    let result = try resolveProgramStr(&mut a, "record T { x: *opaque }");
3481 -
    try expectNoErrors(&result);
4444 +
    let mut testArena315 = testArena();
4445 +
    let testStorage315: 'test315 = &mut testArena315 in {
4446 +
        let mut a = testResolver(testStorage315);
4447 +
        let result = try resolveProgramStr(&mut a, "record T { x: *opaque }");
4448 +
        try expectNoErrors(&result);
4449 +
    }
3482 4450
}
3483 4451
3484 4452
/// You cannot use `@sizeOf` or `@alignOf` on opaque type.
3485 4453
@test unsafe fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) {
3486 -
    let mut a = testResolver();
4454 +
    let mut testArena316 = testArena();
4455 +
    let testStorage316: 'test316 = &mut testArena316 in {
4456 +
        let mut a = testResolver(testStorage316);
3487 4457
3488 -
    let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)");
3489 -
    let err1 = try expectError(&result1);
3490 -
    try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed);
4458 +
        let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)");
4459 +
        let err1 = try expectError(&result1);
4460 +
        try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed);
3491 4461
3492 -
    let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)");
3493 -
    let err2 = try expectError(&result2);
3494 -
    try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed);
4462 +
        let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)");
4463 +
        let err2 = try expectError(&result2);
4464 +
        try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed);
4465 +
    }
3495 4466
}
3496 4467
3497 4468
/// Test that immutable slice/pointer parameters cannot be borrowed mutably.
3498 4469
@test unsafe fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) {
3499 -
    let mut a = testResolver();
3500 -
    let program = "fn f(p: *i32) { let x = &mut *p; }";
3501 -
    let result = try resolveProgramStr(&mut a, program);
3502 -
    let err = try expectError(&result);
3503 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4470 +
    let mut testArena317 = testArena();
4471 +
    let testStorage317: 'test317 = &mut testArena317 in {
4472 +
        let mut a = testResolver(testStorage317);
4473 +
        let program = "fn f(p: *i32) { let x = &mut *p; }";
4474 +
        let result = try resolveProgramStr(&mut a, program);
4475 +
        let err = try expectError(&result);
4476 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4477 +
    }
3504 4478
}
3505 4479
3506 4480
/// Test that immutable slice parameters cannot be borrowed mutably.
3507 4481
@test unsafe fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) {
3508 -
    let mut a = testResolver();
3509 -
    let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }";
3510 -
    let result = try resolveProgramStr(&mut a, program);
3511 -
    let err = try expectError(&result);
3512 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4482 +
    let mut testArena318 = testArena();
4483 +
    let testStorage318: 'test318 = &mut testArena318 in {
4484 +
        let mut a = testResolver(testStorage318);
4485 +
        let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }";
4486 +
        let result = try resolveProgramStr(&mut a, program);
4487 +
        let err = try expectError(&result);
4488 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4489 +
    }
3513 4490
}
3514 4491
3515 4492
/// Test that mutable pointer parameters can be borrowed mutably.
3516 4493
@test unsafe fn testMutableBorrowFromMutablePointer() throws (testing::TestError) {
3517 -
    let mut a = testResolver();
3518 -
    let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }";
3519 -
    let result = try resolveProgramStr(&mut a, program);
3520 -
    try expectNoErrors(&result);
4494 +
    let mut testArena319 = testArena();
4495 +
    let testStorage319: 'test319 = &mut testArena319 in {
4496 +
        let mut a = testResolver(testStorage319);
4497 +
        let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }";
4498 +
        let result = try resolveProgramStr(&mut a, program);
4499 +
        try expectNoErrors(&result);
4500 +
    }
3521 4501
}
3522 4502
3523 4503
/// Test that mutable slice parameters can be borrowed mutably.
3524 4504
@test unsafe fn testMutableBorrowFromMutableSlice() throws (testing::TestError) {
3525 -
    let mut a = testResolver();
3526 -
    let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }";
3527 -
    let result = try resolveProgramStr(&mut a, program);
3528 -
    try expectNoErrors(&result);
4505 +
    let mut testArena320 = testArena();
4506 +
    let testStorage320: 'test320 = &mut testArena320 in {
4507 +
        let mut a = testResolver(testStorage320);
4508 +
        let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }";
4509 +
        let result = try resolveProgramStr(&mut a, program);
4510 +
        try expectNoErrors(&result);
4511 +
    }
3529 4512
}
3530 4513
3531 4514
/// Test borrowing mutably from a field access on a call returning `*mut`.
3532 4515
@test unsafe fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) {
3533 -
    let mut a = testResolver();
3534 -
    let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }";
3535 -
    let result = try resolveProgramStr(&mut a, program);
3536 -
    try expectNoErrors(&result);
4516 +
    let mut testArena321 = testArena();
4517 +
    let testStorage321: 'test321 = &mut testArena321 in {
4518 +
        let mut a = testResolver(testStorage321);
4519 +
        let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }";
4520 +
        let result = try resolveProgramStr(&mut a, program);
4521 +
        try expectNoErrors(&result);
4522 +
    }
3537 4523
}
3538 4524
3539 4525
/// Test that calls returning immutable pointers cannot be mutably borrowed.
3540 4526
@test unsafe fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) {
3541 -
    let mut a = testResolver();
3542 -
    let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }";
3543 -
    let result = try resolveProgramStr(&mut a, program);
3544 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4527 +
    let mut testArena322 = testArena();
4528 +
    let testStorage322: 'test322 = &mut testArena322 in {
4529 +
        let mut a = testResolver(testStorage322);
4530 +
        let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }";
4531 +
        let result = try resolveProgramStr(&mut a, program);
4532 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4533 +
    }
3545 4534
}
3546 4535
3547 4536
/// Test borrowing mutably from a public static through scope access.
3548 4537
@test unsafe fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) {
3549 -
    let mut a = testResolver();
3550 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4538 +
    let mut testArena323 = testArena();
4539 +
    let testStorage323: 'test323 = &mut testArena323 in {
4540 +
        let mut a = testResolver(testStorage323);
4541 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3551 4542
3552 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3553 -
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena);
3554 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; set *p = 7; }", &mut arena);
4543 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
4544 +
        let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena);
4545 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; set *p = 7; }", &mut arena);
3555 4546
3556 -
    let result = try resolveModuleTree(&mut a, rootId);
3557 -
    try expectNoErrors(&result);
4547 +
        let result = try resolveModuleTree(&mut a, rootId);
4548 +
        try expectNoErrors(&result);
4549 +
    }
3558 4550
}
3559 4551
3560 4552
/// Test that constants through scope access cannot be mutably borrowed.
3561 4553
@test unsafe fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) {
3562 -
    let mut a = testResolver();
3563 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4554 +
    let mut testArena324 = testArena();
4555 +
    let testStorage324: 'test324 = &mut testArena324 in {
4556 +
        let mut a = testResolver(testStorage324);
4557 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3564 4558
3565 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3566 -
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena);
3567 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; set *p = 9; }", &mut arena);
4559 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
4560 +
        let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena);
4561 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; set *p = 9; }", &mut arena);
3568 4562
3569 -
    let result = try resolveModuleTree(&mut a, rootId);
3570 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4563 +
        let result = try resolveModuleTree(&mut a, rootId);
4564 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4565 +
    }
3571 4566
}
3572 4567
3573 4568
/// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer.
3574 4569
@test unsafe fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) {
3575 -
    let mut a = testResolver();
3576 -
    let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }";
3577 -
    let result = try resolveProgramStr(&mut a, program);
3578 -
    let err = try expectError(&result);
3579 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4570 +
    let mut testArena325 = testArena();
4571 +
    let testStorage325: 'test325 = &mut testArena325 in {
4572 +
        let mut a = testResolver(testStorage325);
4573 +
        let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }";
4574 +
        let result = try resolveProgramStr(&mut a, program);
4575 +
        let err = try expectError(&result);
4576 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4577 +
    }
3580 4578
}
3581 4579
3582 4580
/// Test that mutable pointer to immutable slice cannot be assigned through index.
3583 4581
/// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but
3584 4582
/// the inner slice is immutable, so we shouldn't be able to mutate the elements.
3585 4583
@test unsafe fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) {
3586 -
    let mut a = testResolver();
3587 -
    let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }";
3588 -
    let result = try resolveProgramStr(&mut a, program);
4584 +
    let mut testArena326 = testArena();
4585 +
    let testStorage326: 'test326 = &mut testArena326 in {
4586 +
        let mut a = testResolver(testStorage326);
4587 +
        let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }";
4588 +
        let result = try resolveProgramStr(&mut a, program);
3589 4589
3590 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4590 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4591 +
    }
3591 4592
}
3592 4593
3593 4594
/// Test that mutable slice parameters can be assigned through index.
3594 4595
@test unsafe fn testAssignThroughMutableSliceParam() throws (testing::TestError) {
3595 4596
    {
3596 4597
        // Mutable slice param: direct assignment should work
3597 -
        let mut a = testResolver();
3598 -
        let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }";
3599 -
        let result = try resolveProgramStr(&mut a, program);
3600 -
        try expectNoErrors(&result);
4598 +
        let mut testArena327 = testArena();
4599 +
        let testStorage327: 'test327 = &mut testArena327 in {
4600 +
            let mut a = testResolver(testStorage327);
4601 +
            let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }";
4602 +
            let result = try resolveProgramStr(&mut a, program);
4603 +
            try expectNoErrors(&result);
4604 +
        }
3601 4605
    } {
3602 4606
        // Immutable slice param: direct assignment should fail
3603 -
        let mut a = testResolver();
3604 -
        let program = "fn f(slice: *[i32]) { set slice[0] = 1; }";
3605 -
        let result = try resolveProgramStr(&mut a, program);
3606 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4607 +
        let mut testArena328 = testArena();
4608 +
        let testStorage328: 'test328 = &mut testArena328 in {
4609 +
            let mut a = testResolver(testStorage328);
4610 +
            let program = "fn f(slice: *[i32]) { set slice[0] = 1; }";
4611 +
            let result = try resolveProgramStr(&mut a, program);
4612 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4613 +
        }
3607 4614
    }
3608 4615
}
3609 4616
3610 4617
/// Test range end type coercion with assignable types.
3611 4618
@test unsafe fn testRangeEndTypeCoercion() throws (testing::TestError) {
3612 4619
    {
3613 -
        let mut a = testResolver();
3614 -
        let program = "fn f(end: u32) { for i in 0..end {} }";
3615 -
        let result = try resolveProgramStr(&mut a, program);
3616 -
        try expectNoErrors(&result);
4620 +
        let mut testArena329 = testArena();
4621 +
        let testStorage329: 'test329 = &mut testArena329 in {
4622 +
            let mut a = testResolver(testStorage329);
4623 +
            let program = "fn f(end: u32) { for i in 0..end {} }";
4624 +
            let result = try resolveProgramStr(&mut a, program);
4625 +
            try expectNoErrors(&result);
4626 +
        }
3617 4627
    } {
3618 -
        let mut a = testResolver();
3619 -
        let program = "fn f(start: u32) { for i in start..9 {} }";
3620 -
        let result = try resolveProgramStr(&mut a, program);
3621 -
        try expectNoErrors(&result);
4628 +
        let mut testArena330 = testArena();
4629 +
        let testStorage330: 'test330 = &mut testArena330 in {
4630 +
            let mut a = testResolver(testStorage330);
4631 +
            let program = "fn f(start: u32) { for i in start..9 {} }";
4632 +
            let result = try resolveProgramStr(&mut a, program);
4633 +
            try expectNoErrors(&result);
4634 +
        }
3622 4635
    }
3623 4636
}
3624 4637
3625 4638
/// Mixed-width range bounds require an explicit cast.
3626 4639
@test unsafe fn testRangeEndTypeSubType() throws (testing::TestError) {
3627 4640
    {
3628 -
        let mut a = testResolver();
3629 -
        let program = "fn f(start: i8, end: u32) { for i in start..end {} }";
3630 -
        let result = try resolveProgramStr(&mut a, program);
3631 -
        let err = try expectError(&result);
3632 -
        try expectTypeMismatch(err, super::Type::I8, super::Type::U32);
4641 +
        let mut testArena331 = testArena();
4642 +
        let testStorage331: 'test331 = &mut testArena331 in {
4643 +
            let mut a = testResolver(testStorage331);
4644 +
            let program = "fn f(start: i8, end: u32) { for i in start..end {} }";
4645 +
            let result = try resolveProgramStr(&mut a, program);
4646 +
            let err = try expectError(&result);
4647 +
            try expectTypeMismatch(err, super::Type::I8, super::Type::U32);
4648 +
        }
3633 4649
    } {
3634 -
        let mut a = testResolver();
3635 -
        let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }";
3636 -
        let result = try resolveProgramStr(&mut a, program);
3637 -
        try expectNoErrors(&result);
4650 +
        let mut testArena332 = testArena();
4651 +
        let testStorage332: 'test332 = &mut testArena332 in {
4652 +
            let mut a = testResolver(testStorage332);
4653 +
            let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }";
4654 +
            let result = try resolveProgramStr(&mut a, program);
4655 +
            try expectNoErrors(&result);
4656 +
        }
3638 4657
    }
3639 4658
}
3640 4659
3641 4660
/// Test that try-catch expressions in statement context accept mismatched types.
3642 4661
@test unsafe fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) {
3643 -
    let mut a = testResolver();
3644 -
    let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3645 -
    let result = try resolveProgramStr(&mut a, program);
3646 -
    try expectNoErrors(&result);
4662 +
    let mut testArena333 = testArena();
4663 +
    let testStorage333: 'test333 = &mut testArena333 in {
4664 +
        let mut a = testResolver(testStorage333);
4665 +
        let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
4666 +
        let result = try resolveProgramStr(&mut a, program);
4667 +
        try expectNoErrors(&result);
4668 +
    }
3647 4669
}
3648 4670
3649 4671
/// Test that try-catch blocks in value context require divergence or void.
3650 4672
@test unsafe fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) {
3651 -
    let mut a = testResolver();
3652 -
    let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3653 -
    let result = try resolveProgramStr(&mut a, program);
3654 -
    let err = try expectError(&result);
3655 -
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Void);
4673 +
    let mut testArena334 = testArena();
4674 +
    let testStorage334: 'test334 = &mut testArena334 in {
4675 +
        let mut a = testResolver(testStorage334);
4676 +
        let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
4677 +
        let result = try resolveProgramStr(&mut a, program);
4678 +
        let err = try expectError(&result);
4679 +
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Void);
4680 +
    }
3656 4681
}
3657 4682
3658 4683
/// Test that try-catch blocks in value context work when they diverge.
3659 4684
@test unsafe fn testTryCatchInValueContextDiverges() throws (testing::TestError) {
3660 -
    let mut a = testResolver();
3661 -
    let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }";
3662 -
    let result = try resolveProgramStr(&mut a, program);
3663 -
    try expectNoErrors(&result);
4685 +
    let mut testArena335 = testArena();
4686 +
    let testStorage335: 'test335 = &mut testArena335 in {
4687 +
        let mut a = testResolver(testStorage335);
4688 +
        let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }";
4689 +
        let result = try resolveProgramStr(&mut a, program);
4690 +
        try expectNoErrors(&result);
4691 +
    }
3664 4692
}
3665 4693
3666 4694
/// Test that `try?` lifts result type to optional.
3667 4695
@test unsafe fn testTryOptionalLiftsToOptional() throws (testing::TestError) {
3668 -
    let mut a = testResolver();
3669 -
    let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }";
3670 -
    let result = try resolveProgramStr(&mut a, program);
3671 -
    try expectNoErrors(&result);
4696 +
    let mut testArena336 = testArena();
4697 +
    let testStorage336: 'test336 = &mut testArena336 in {
4698 +
        let mut a = testResolver(testStorage336);
4699 +
        let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }";
4700 +
        let result = try resolveProgramStr(&mut a, program);
4701 +
        try expectNoErrors(&result);
4702 +
    }
3672 4703
}
3673 4704
3674 4705
/// Test that record fields can be assigned if the record binding is mutable.
3675 4706
@test unsafe fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) {
3676 -
    let mut a = testResolver();
3677 -
    let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }";
3678 -
    let result = try resolveProgramStr(&mut a, program);
3679 -
    try expectNoErrors(&result);
4707 +
    let mut testArena337 = testArena();
4708 +
    let testStorage337: 'test337 = &mut testArena337 in {
4709 +
        let mut a = testResolver(testStorage337);
4710 +
        let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }";
4711 +
        let result = try resolveProgramStr(&mut a, program);
4712 +
        try expectNoErrors(&result);
4713 +
    }
3680 4714
}
3681 4715
3682 4716
/// Test that record fields cannot be assigned if the record binding is immutable.
3683 4717
@test unsafe fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) {
3684 -
    let mut a = testResolver();
3685 -
    let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }";
3686 -
    let result = try resolveProgramStr(&mut a, program);
3687 -
    let err = try expectError(&result);
3688 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4718 +
    let mut testArena338 = testArena();
4719 +
    let testStorage338: 'test338 = &mut testArena338 in {
4720 +
        let mut a = testResolver(testStorage338);
4721 +
        let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }";
4722 +
        let result = try resolveProgramStr(&mut a, program);
4723 +
        let err = try expectError(&result);
4724 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4725 +
    }
3689 4726
}
3690 4727
3691 4728
/// Test that record fields can be assigned through a mutable pointer.
3692 4729
@test unsafe fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) {
3693 -
    let mut a = testResolver();
3694 -
    let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }";
3695 -
    let result = try resolveProgramStr(&mut a, program);
3696 -
    try expectNoErrors(&result);
4730 +
    let mut testArena339 = testArena();
4731 +
    let testStorage339: 'test339 = &mut testArena339 in {
4732 +
        let mut a = testResolver(testStorage339);
4733 +
        let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }";
4734 +
        let result = try resolveProgramStr(&mut a, program);
4735 +
        try expectNoErrors(&result);
4736 +
    }
3697 4737
}
3698 4738
3699 4739
/// Test that record fields cannot be assigned through an immutable pointer.
3700 4740
@test unsafe fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) {
3701 -
    let mut a = testResolver();
3702 -
    let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }";
3703 -
    let result = try resolveProgramStr(&mut a, program);
3704 -
    let err = try expectError(&result);
3705 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4741 +
    let mut testArena340 = testArena();
4742 +
    let testStorage340: 'test340 = &mut testArena340 in {
4743 +
        let mut a = testResolver(testStorage340);
4744 +
        let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }";
4745 +
        let result = try resolveProgramStr(&mut a, program);
4746 +
        let err = try expectError(&result);
4747 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4748 +
    }
3706 4749
}
3707 4750
3708 4751
// Opaque pointer tests.
3709 4752
3710 4753
/// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast.
3711 4754
@test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) {
3712 -
    let mut a = testResolver();
3713 -
    let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }");
3714 -
    try expectNoErrors(&result);
4755 +
    let mut testArena341 = testArena();
4756 +
    let testStorage341: 'test341 = &mut testArena341 in {
4757 +
        let mut a = testResolver(testStorage341);
4758 +
        let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }");
4759 +
        try expectNoErrors(&result);
4760 +
    }
3715 4761
}
3716 4762
3717 4763
/// You cannot assign an opaque pointer to a non-opaque pointer without a cast.
3718 4764
@test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) {
3719 -
    let mut a = testResolver();
3720 -
    let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }");
3721 -
    let err = try expectError(&result);
3722 -
    let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
3723 -
        else throw testing::TestError::Failed;
3724 -
    let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected
3725 -
        else throw testing::TestError::Failed;
3726 -
    let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual
3727 -
        else throw testing::TestError::Failed;
4765 +
    let mut testArena342 = testArena();
4766 +
    let testStorage342: 'test342 = &mut testArena342 in {
4767 +
        let mut a = testResolver(testStorage342);
4768 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }");
4769 +
        let err = try expectError(&result);
4770 +
        let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
4771 +
            else throw testing::TestError::Failed;
4772 +
        let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected
4773 +
            else throw testing::TestError::Failed;
4774 +
        let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual
4775 +
            else throw testing::TestError::Failed;
3728 4776
3729 -
    try testing::expect(*expectedTarget == super::Type::I32);
3730 -
    try testing::expect(*actualTarget == super::Type::Opaque);
4777 +
        try testing::expect(*expectedTarget == super::Type::I32);
4778 +
        try testing::expect(*actualTarget == super::Type::Opaque);
4779 +
    }
3731 4780
}
3732 4781
3733 4782
/// You cannot have a value of type `opaque` (function parameter).
3734 4783
@test unsafe fn testOpaqueValue() throws (testing::TestError) {
3735 4784
    {
3736 -
        let mut a = testResolver();
3737 -
        let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}");
3738 -
        let err = try expectError(&result);
3739 -
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4785 +
        let mut testArena343 = testArena();
4786 +
        let testStorage343: 'test343 = &mut testArena343 in {
4787 +
            let mut a = testResolver(testStorage343);
4788 +
            let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}");
4789 +
            let err = try expectError(&result);
4790 +
            try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4791 +
        }
3740 4792
    } {
3741 -
        let mut a = testResolver();
3742 -
        let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }");
3743 -
        let err = try expectError(&result);
3744 -
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4793 +
        let mut testArena344 = testArena();
4794 +
        let testStorage344: 'test344 = &mut testArena344 in {
4795 +
            let mut a = testResolver(testStorage344);
4796 +
            let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }");
4797 +
            let err = try expectError(&result);
4798 +
            try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4799 +
        }
3745 4800
    } {
3746 -
        let mut a = testResolver();
3747 -
        let result = try resolveProgramStr(&mut a, "record R { x: opaque }");
3748 -
        let err = try expectError(&result);
3749 -
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4801 +
        let mut testArena345 = testArena();
4802 +
        let testStorage345: 'test345 = &mut testArena345 in {
4803 +
            let mut a = testResolver(testStorage345);
4804 +
            let result = try resolveProgramStr(&mut a, "record R { x: opaque }");
4805 +
            let err = try expectError(&result);
4806 +
            try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
4807 +
        }
3750 4808
    }
3751 4809
}
3752 4810
3753 4811
/// You cannot dereference an opaque pointer, you have to cast it first.
3754 4812
@test unsafe fn testOpaquePointerNoDereference() throws (testing::TestError) {
3755 -
    let mut a = testResolver();
3756 -
    let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }");
3757 -
    let err = try expectError(&result);
3758 -
    try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref);
4813 +
    let mut testArena346 = testArena();
4814 +
    let testStorage346: 'test346 = &mut testArena346 in {
4815 +
        let mut a = testResolver(testStorage346);
4816 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }");
4817 +
        let err = try expectError(&result);
4818 +
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref);
4819 +
    }
3759 4820
}
3760 4821
3761 4822
/// Test that you can dereference after casting.
3762 4823
@test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) {
3763 -
    let mut a = testResolver();
3764 -
    let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }");
3765 -
    try expectNoErrors(&result);
4824 +
    let mut testArena347 = testArena();
4825 +
    let testStorage347: 'test347 = &mut testArena347 in {
4826 +
        let mut a = testResolver(testStorage347);
4827 +
        let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }");
4828 +
        try expectNoErrors(&result);
4829 +
    }
3766 4830
}
3767 4831
3768 4832
/// You cannot do pointer arithmetic with an opaque pointer.
3769 4833
@test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) {
3770 4834
    {
3771 -
        let mut a = testResolver();
3772 -
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }");
3773 -
        let err = try expectError(&result);
3774 -
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4835 +
        let mut testArena348 = testArena();
4836 +
        let testStorage348: 'test348 = &mut testArena348 in {
4837 +
            let mut a = testResolver(testStorage348);
4838 +
            let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }");
4839 +
            let err = try expectError(&result);
4840 +
            try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4841 +
        }
3775 4842
    } {
3776 -
        let mut a = testResolver();
3777 -
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }");
3778 -
        let err = try expectError(&result);
3779 -
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4843 +
        let mut testArena349 = testArena();
4844 +
        let testStorage349: 'test349 = &mut testArena349 in {
4845 +
            let mut a = testResolver(testStorage349);
4846 +
            let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }");
4847 +
            let err = try expectError(&result);
4848 +
            try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4849 +
        }
3780 4850
    } {
3781 -
        let mut a = testResolver();
3782 -
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }");
3783 -
        let err = try expectError(&result);
3784 -
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4851 +
        let mut testArena350 = testArena();
4852 +
        let testStorage350: 'test350 = &mut testArena350 in {
4853 +
            let mut a = testResolver(testStorage350);
4854 +
            let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }");
4855 +
            let err = try expectError(&result);
4856 +
            try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4857 +
        }
3785 4858
    } {
3786 -
        let mut a = testResolver();
3787 -
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }");
3788 -
        let err = try expectError(&result);
3789 -
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4859 +
        let mut testArena351 = testArena();
4860 +
        let testStorage351: 'test351 = &mut testArena351 in {
4861 +
            let mut a = testResolver(testStorage351);
4862 +
            let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }");
4863 +
            let err = try expectError(&result);
4864 +
            try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
4865 +
        }
3790 4866
    }
3791 4867
}
3792 4868
3793 4869
// Wildcard import/reexport tests.
3794 4870
3795 4871
/// Test transitive re-export.
3796 4872
@test unsafe fn testWildcardReexportTransitive() throws (testing::TestError) {
3797 -
    let mut a = testResolver();
3798 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4873 +
    let mut testArena352 = testArena();
4874 +
    let testStorage352: 'test352 = &mut testArena352 in {
4875 +
        let mut a = testResolver(testStorage352);
4876 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3799 4877
3800 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena);
3801 -
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena);
3802 -
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena);
3803 -
    let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena);
3804 -
    let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena);
4878 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena);
4879 +
        let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena);
4880 +
        let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena);
4881 +
        let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena);
4882 +
        let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena);
3805 4883
3806 -
    let result = try resolveModuleTree(&mut a, rootId);
3807 -
    try expectNoErrors(&result);
4884 +
        let result = try resolveModuleTree(&mut a, rootId);
4885 +
        try expectNoErrors(&result);
4886 +
    }
3808 4887
}
3809 4888
3810 4889
/// Test that wildcard import can access public symbols.
3811 4890
@test unsafe fn testWildcardImportPublicOnly() throws (testing::TestError) {
3812 -
    let mut a = testResolver();
3813 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4891 +
    let mut testArena353 = testArena();
4892 +
    let testStorage353: 'test353 = &mut testArena353 in {
4893 +
        let mut a = testResolver(testStorage353);
4894 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3814 4895
3815 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3816 -
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
3817 -
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn id(value: Value) -> Value { return value; } fn main() -> i32 { return public() + id(Value { number: 2 }).number; }", &mut arena);
4896 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
4897 +
        let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
4898 +
        let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn id(value: Value) -> Value { return value; } fn main() -> i32 { return public() + id(Value { number: 2 }).number; }", &mut arena);
3818 4899
3819 -
    let result = try resolveModuleTree(&mut a, rootId);
3820 -
    try expectNoErrors(&result);
4900 +
        let result = try resolveModuleTree(&mut a, rootId);
4901 +
        try expectNoErrors(&result);
4902 +
    }
3821 4903
}
3822 4904
3823 4905
/// Test that wildcard import cannot access private symbols.
3824 4906
@test unsafe fn testWildcardImportSkipsPrivate() throws (testing::TestError) {
3825 -
    let mut a = testResolver();
3826 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4907 +
    let mut testArena354 = testArena();
4908 +
    let testStorage354: 'test354 = &mut testArena354 in {
4909 +
        let mut a = testResolver(testStorage354);
4910 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3827 4911
3828 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3829 -
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
3830 -
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena);
4912 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
4913 +
        let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
4914 +
        let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena);
3831 4915
3832 -
    let result = try resolveModuleTree(&mut a, rootId);
3833 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4916 +
        let result = try resolveModuleTree(&mut a, rootId);
4917 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
4918 +
    }
3834 4919
}
3835 4920
3836 4921
/// Test that a constant array can use another constant as its length.
3837 4922
@test unsafe fn testConstArrayWithConstLength() throws (testing::TestError) {
3838 -
    let mut a = testResolver();
3839 -
    let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];";
3840 -
    let result = try resolveProgramStr(&mut a, program);
3841 -
    try expectNoErrors(&result);
3842 -
3843 -
    // Verify the array constant has the correct type with length 3.
3844 -
    let arrStmt = try getBlockStmt(result.root, 1);
3845 -
    let sym = super::symbolFor(&a, arrStmt)
3846 -
        else throw testing::TestError::Failed;
3847 -
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
3848 -
        else throw testing::TestError::Failed;
3849 -
    try testing::expect(arrType.length == 3);
4923 +
    let mut testArena355 = testArena();
4924 +
    let testStorage355: 'test355 = &mut testArena355 in {
4925 +
        let mut a = testResolver(testStorage355);
4926 +
        let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];";
4927 +
        let result = try resolveProgramStr(&mut a, program);
4928 +
        try expectNoErrors(&result);
4929 +
4930 +
        // Verify the array constant has the correct type with length 3.
4931 +
        let arrStmt = try getBlockStmt(result.root, 1);
4932 +
        let sym = super::symbolFor(&a, arrStmt)
4933 +
            else throw testing::TestError::Failed;
4934 +
        let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
4935 +
            else throw testing::TestError::Failed;
4936 +
        try testing::expect(arrType.length == 3);
4937 +
    }
3850 4938
}
3851 4939
3852 4940
/// Test that a record field can use a constant as its array length.
3853 4941
@test unsafe fn testRecordFieldWithConstArrayLength() throws (testing::TestError) {
3854 -
    let mut a = testResolver();
3855 -
    let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }";
3856 -
    let result = try resolveProgramStr(&mut a, program);
3857 -
    try expectNoErrors(&result);
4942 +
    let mut testArena356 = testArena();
4943 +
    let testStorage356: 'test356 = &mut testArena356 in {
4944 +
        let mut a = testResolver(testStorage356);
4945 +
        let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }";
4946 +
        let result = try resolveProgramStr(&mut a, program);
4947 +
        try expectNoErrors(&result);
4948 +
    }
3858 4949
}
3859 4950
3860 4951
/// Test that a constant can have a record literal value (lazy record body resolution).
3861 4952
@test unsafe fn testConstWithRecordLiteral() throws (testing::TestError) {
3862 -
    let mut a = testResolver();
3863 -
    let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };";
3864 -
    let result = try resolveProgramStr(&mut a, program);
3865 -
    try expectNoErrors(&result);
4953 +
    let mut testArena357 = testArena();
4954 +
    let testStorage357: 'test357 = &mut testArena357 in {
4955 +
        let mut a = testResolver(testStorage357);
4956 +
        let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };";
4957 +
        let result = try resolveProgramStr(&mut a, program);
4958 +
        try expectNoErrors(&result);
4959 +
    }
3866 4960
}
3867 4961
3868 4962
/// Test that a constant can have a union variant value (lazy union body resolution).
3869 4963
@test unsafe fn testConstWithUnionVariant() throws (testing::TestError) {
3870 -
    let mut a = testResolver();
3871 -
    let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;";
3872 -
    let result = try resolveProgramStr(&mut a, program);
3873 -
    try expectNoErrors(&result);
4964 +
    let mut testArena358 = testArena();
4965 +
    let testStorage358: 'test358 = &mut testArena358 in {
4966 +
        let mut a = testResolver(testStorage358);
4967 +
        let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;";
4968 +
        let result = try resolveProgramStr(&mut a, program);
4969 +
        try expectNoErrors(&result);
4970 +
    }
3874 4971
}
3875 4972
3876 4973
/// Test that record field types can reference imported types.
3877 4974
///
3878 4975
/// This tests that `use` statements are processed before record body resolution,
3879 4976
/// allowing record fields to use types from imported modules.
3880 4977
@test unsafe fn testRecordFieldUsesImportedType() throws (testing::TestError) {
3881 -
    let mut a = testResolver();
3882 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4978 +
    let mut testArena359 = testArena();
4979 +
    let testStorage359: 'test359 = &mut testArena359 in {
4980 +
        let mut a = testResolver(testStorage359);
4981 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3883 4982
3884 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena);
3885 -
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena);
3886 -
    let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena);
4983 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena);
4984 +
        let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena);
4985 +
        let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena);
3887 4986
3888 -
    let result = try resolveModuleTree(&mut a, rootId);
3889 -
    try expectNoErrors(&result);
4987 +
        let result = try resolveModuleTree(&mut a, rootId);
4988 +
        try expectNoErrors(&result);
4989 +
    }
3890 4990
}
3891 4991
3892 4992
/// Test that imported constants can be used in array size expressions.
3893 4993
///
3894 4994
/// This tests that constant values are propagated through scope access expressions,
3895 4995
/// enabling compile-time evaluation of array sizes using imported constants.
3896 4996
@test unsafe fn testImportedConstantInArraySize() throws (testing::TestError) {
3897 -
    let mut a = testResolver();
3898 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4997 +
    let mut testArena360 = testArena();
4998 +
    let testStorage360: 'test360 = &mut testArena360 in {
4999 +
        let mut a = testResolver(testStorage360);
5000 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3899 5001
3900 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3901 -
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena);
3902 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena);
5002 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5003 +
        let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena);
5004 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena);
3903 5005
3904 -
    let result = try resolveModuleTree(&mut a, rootId);
3905 -
    try expectNoErrors(&result);
5006 +
        let result = try resolveModuleTree(&mut a, rootId);
5007 +
        try expectNoErrors(&result);
5008 +
    }
3906 5009
}
3907 5010
3908 5011
/// Test that `if let case` binds payload variables in the then branch.
3909 5012
///
3910 5013
/// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should
3911 5014
/// be bound to the payload value within the then branch scope.
3912 5015
@test unsafe fn testResolveIfCaseBindsPayload() throws (testing::TestError) {
3913 -
    let mut a = testResolver();
3914 -
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }";
3915 -
    let result = try resolveProgramStr(&mut a, program);
3916 -
    try expectNoErrors(&result);
5016 +
    let mut testArena361 = testArena();
5017 +
    let testStorage361: 'test361 = &mut testArena361 in {
5018 +
        let mut a = testResolver(testStorage361);
5019 +
        let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }";
5020 +
        let result = try resolveProgramStr(&mut a, program);
5021 +
        try expectNoErrors(&result);
5022 +
    }
3917 5023
}
3918 5024
3919 5025
/// Test that `if let case` payload binding is scoped to the then branch.
3920 5026
///
3921 5027
/// The payload variable should not be accessible outside the then branch.
3922 5028
@test unsafe fn testResolveIfCasePayloadScopeError() throws (testing::TestError) {
3923 -
    let mut a = testResolver();
3924 -
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }";
3925 -
    let result = try resolveProgramStr(&mut a, program);
3926 -
    let err = try expectError(&result);
3927 -
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3928 -
        else throw testing::TestError::Failed;
3929 -
    try testing::expect(mem::eq(name, "x"));
5029 +
    let mut testArena362 = testArena();
5030 +
    let testStorage362: 'test362 = &mut testArena362 in {
5031 +
        let mut a = testResolver(testStorage362);
5032 +
        let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }";
5033 +
        let result = try resolveProgramStr(&mut a, program);
5034 +
        let err = try expectError(&result);
5035 +
        let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
5036 +
            else throw testing::TestError::Failed;
5037 +
        try testing::expect(mem::eq(name, "x"));
5038 +
    }
3930 5039
}
3931 5040
3932 5041
/// Test that `let case` binds payload variables in the current scope.
3933 5042
///
3934 5043
/// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x`
3935 5044
/// should be bound in the scope after the statement.
3936 5045
@test unsafe fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) {
3937 -
    let mut a = testResolver();
3938 -
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }";
3939 -
    let result = try resolveProgramStr(&mut a, program);
3940 -
    try expectNoErrors(&result);
5046 +
    let mut testArena363 = testArena();
5047 +
    let testStorage363: 'test363 = &mut testArena363 in {
5048 +
        let mut a = testResolver(testStorage363);
5049 +
        let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }";
5050 +
        let result = try resolveProgramStr(&mut a, program);
5051 +
        try expectNoErrors(&result);
5052 +
    }
3941 5053
}
3942 5054
3943 5055
/// Test that function pointers with identical signatures are assignable.
3944 5056
///
3945 5057
/// Two function types with the same parameters, return type, and throw list
3946 5058
/// should be considered structurally equal, even if they are separate allocations.
3947 5059
@test unsafe fn testFnPointerAssignability() throws (testing::TestError) {
3948 -
    let mut a = testResolver();
3949 -
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);";
3950 -
    let result = try resolveProgramStr(&mut a, program);
3951 -
    try expectNoErrors(&result);
5060 +
    let mut testArena364 = testArena();
5061 +
    let testStorage364: 'test364 = &mut testArena364 in {
5062 +
        let mut a = testResolver(testStorage364);
5063 +
        let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);";
5064 +
        let result = try resolveProgramStr(&mut a, program);
5065 +
        try expectNoErrors(&result);
5066 +
    }
3952 5067
}
3953 5068
3954 5069
/// Test that function pointers with different parameter types are not assignable.
3955 5070
@test unsafe fn testFnPointerParamMismatch() throws (testing::TestError) {
3956 -
    let mut a = testResolver();
3957 -
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);";
3958 -
    let result = try resolveProgramStr(&mut a, program);
3959 -
    let err = try expectError(&result);
3960 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3961 -
        else throw testing::TestError::Failed;
5071 +
    let mut testArena365 = testArena();
5072 +
    let testStorage365: 'test365 = &mut testArena365 in {
5073 +
        let mut a = testResolver(testStorage365);
5074 +
        let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);";
5075 +
        let result = try resolveProgramStr(&mut a, program);
5076 +
        let err = try expectError(&result);
5077 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5078 +
            else throw testing::TestError::Failed;
5079 +
    }
3962 5080
}
3963 5081
3964 5082
/// Test that function pointers with different return types are not assignable.
3965 5083
@test unsafe fn testFnPointerReturnMismatch() throws (testing::TestError) {
3966 -
    let mut a = testResolver();
3967 -
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);";
3968 -
    let result = try resolveProgramStr(&mut a, program);
3969 -
    let err = try expectError(&result);
3970 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3971 -
        else throw testing::TestError::Failed;
5084 +
    let mut testArena366 = testArena();
5085 +
    let testStorage366: 'test366 = &mut testArena366 in {
5086 +
        let mut a = testResolver(testStorage366);
5087 +
        let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);";
5088 +
        let result = try resolveProgramStr(&mut a, program);
5089 +
        let err = try expectError(&result);
5090 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5091 +
            else throw testing::TestError::Failed;
5092 +
    }
3972 5093
}
3973 5094
3974 5095
/// Test that named records use nominal typing, not structural.
3975 5096
///
3976 5097
/// Two different named record types with identical fields should NOT be
3977 5098
/// assignable to each other, because they are distinct nominal types.
3978 5099
@test unsafe fn testNamedRecordNominalTyping() throws (testing::TestError) {
3979 -
    let mut a = testResolver();
3980 -
    let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);";
3981 -
    let result = try resolveProgramStr(&mut a, program);
3982 -
    let err = try expectError(&result);
3983 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3984 -
        else throw testing::TestError::Failed;
5100 +
    let mut testArena367 = testArena();
5101 +
    let testStorage367: 'test367 = &mut testArena367 in {
5102 +
        let mut a = testResolver(testStorage367);
5103 +
        let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);";
5104 +
        let result = try resolveProgramStr(&mut a, program);
5105 +
        let err = try expectError(&result);
5106 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5107 +
            else throw testing::TestError::Failed;
5108 +
    }
3985 5109
}
3986 5110
3987 5111
/// Test that union variants with labeled record payloads can be constructed.
3988 5112
@test unsafe fn testUnionVariantAnonRecordPayload() throws (testing::TestError) {
3989 -
    let mut a = testResolver();
3990 -
    let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };";
3991 -
    let result = try resolveProgramStr(&mut a, program);
3992 -
    try expectNoErrors(&result);
5113 +
    let mut testArena368 = testArena();
5114 +
    let testStorage368: 'test368 = &mut testArena368 in {
5115 +
        let mut a = testResolver(testStorage368);
5116 +
        let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };";
5117 +
        let result = try resolveProgramStr(&mut a, program);
5118 +
        try expectNoErrors(&result);
5119 +
    }
3993 5120
}
3994 5121
3995 5122
/// Test that unlabeled record literals with positional fields work correctly.
3996 5123
///
3997 5124
/// When a record is declared with positional fields (e.g., `record R(i32, bool)`),
3998 5125
/// the literal must use constructor call syntax with positional arguments.
3999 5126
@test unsafe fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) {
4000 -
    let mut a = testResolver();
4001 -
    let program = "record R(i32, bool); let r: R = R(1, true);";
4002 -
    let result = try resolveProgramStr(&mut a, program);
4003 -
    try expectNoErrors(&result);
5127 +
    let mut testArena369 = testArena();
5128 +
    let testStorage369: 'test369 = &mut testArena369 in {
5129 +
        let mut a = testResolver(testStorage369);
5130 +
        let program = "record R(i32, bool); let r: R = R(1, true);";
5131 +
        let result = try resolveProgramStr(&mut a, program);
5132 +
        try expectNoErrors(&result);
5133 +
    }
4004 5134
}
4005 5135
4006 5136
/// Test that using brace syntax for an unlabeled record causes an error.
4007 5137
@test unsafe fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) {
4008 -
    let mut a = testResolver();
4009 -
    let program = "record R(i32); let r = R { x: 1 };";
4010 -
    let result = try resolveProgramStr(&mut a, program);
4011 -
    try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch);
5138 +
    let mut testArena370 = testArena();
5139 +
    let testStorage370: 'test370 = &mut testArena370 in {
5140 +
        let mut a = testResolver(testStorage370);
5141 +
        let program = "record R(i32); let r = R { x: 1 };";
5142 +
        let result = try resolveProgramStr(&mut a, program);
5143 +
        try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch);
5144 +
    }
4012 5145
}
4013 5146
4014 5147
/// Test that providing too many fields for an unlabeled record causes count mismatch.
4015 5148
@test unsafe fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) {
4016 -
    let mut a = testResolver();
4017 -
    let program = "record R(i32, bool); let r = R(1, true, 3);";
4018 -
    let result = try resolveProgramStr(&mut a, program);
4019 -
    let err = try expectError(&result);
4020 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4021 -
        else throw testing::TestError::Failed;
5149 +
    let mut testArena371 = testArena();
5150 +
    let testStorage371: 'test371 = &mut testArena371 in {
5151 +
        let mut a = testResolver(testStorage371);
5152 +
        let program = "record R(i32, bool); let r = R(1, true, 3);";
5153 +
        let result = try resolveProgramStr(&mut a, program);
5154 +
        let err = try expectError(&result);
5155 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
5156 +
            else throw testing::TestError::Failed;
5157 +
    }
4022 5158
}
4023 5159
4024 5160
/// Test that match pattern with wrong number of bindings causes count mismatch.
4025 5161
@test unsafe fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) {
4026 -
    let mut a = testResolver();
4027 -
    let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }";
4028 -
    let result = try resolveProgramStr(&mut a, program);
4029 -
    let err = try expectError(&result);
4030 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4031 -
        else throw testing::TestError::Failed;
5162 +
    let mut testArena372 = testArena();
5163 +
    let testStorage372: 'test372 = &mut testArena372 in {
5164 +
        let mut a = testResolver(testStorage372);
5165 +
        let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }";
5166 +
        let result = try resolveProgramStr(&mut a, program);
5167 +
        let err = try expectError(&result);
5168 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
5169 +
            else throw testing::TestError::Failed;
5170 +
    }
4032 5171
}
4033 5172
4034 5173
/// Test that shorthand field syntax works in record literals.
4035 5174
/// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`.
4036 5175
@test unsafe fn testResolveRecordLiteralShorthand() throws (testing::TestError) {
4037 -
    let mut a = testResolver();
4038 -
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }";
4039 -
    let result = try resolveProgramStr(&mut a, program);
4040 -
    try expectNoErrors(&result);
5176 +
    let mut testArena373 = testArena();
5177 +
    let testStorage373: 'test373 = &mut testArena373 in {
5178 +
        let mut a = testResolver(testStorage373);
5179 +
        let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }";
5180 +
        let result = try resolveProgramStr(&mut a, program);
5181 +
        try expectNoErrors(&result);
5182 +
    }
4041 5183
}
4042 5184
4043 5185
/// Test shorthand field syntax with mixed explicit and shorthand fields.
4044 5186
@test unsafe fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) {
4045 -
    let mut a = testResolver();
4046 -
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }";
4047 -
    let result = try resolveProgramStr(&mut a, program);
4048 -
    try expectNoErrors(&result);
5187 +
    let mut testArena374 = testArena();
5188 +
    let testStorage374: 'test374 = &mut testArena374 in {
5189 +
        let mut a = testResolver(testStorage374);
5190 +
        let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }";
5191 +
        let result = try resolveProgramStr(&mut a, program);
5192 +
        try expectNoErrors(&result);
5193 +
    }
4049 5194
}
4050 5195
4051 5196
/// Test record-style union variant patterns with shorthand syntax.
4052 5197
@test unsafe fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) {
4053 -
    let mut a = testResolver();
4054 -
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }";
4055 -
    let result = try resolveProgramStr(&mut a, program);
4056 -
    try expectNoErrors(&result);
5198 +
    let mut testArena375 = testArena();
5199 +
    let testStorage375: 'test375 = &mut testArena375 in {
5200 +
        let mut a = testResolver(testStorage375);
5201 +
        let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }";
5202 +
        let result = try resolveProgramStr(&mut a, program);
5203 +
        try expectNoErrors(&result);
5204 +
    }
4057 5205
}
4058 5206
4059 5207
/// Test record pattern with mixed shorthand and explicit labels.
4060 5208
@test unsafe fn testResolveMatchRecordPatternMixed() throws (testing::TestError) {
4061 -
    let mut a = testResolver();
4062 -
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }";
4063 -
    let result = try resolveProgramStr(&mut a, program);
4064 -
    try expectNoErrors(&result);
5209 +
    let mut testArena376 = testArena();
5210 +
    let testStorage376: 'test376 = &mut testArena376 in {
5211 +
        let mut a = testResolver(testStorage376);
5212 +
        let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }";
5213 +
        let result = try resolveProgramStr(&mut a, program);
5214 +
        try expectNoErrors(&result);
5215 +
    }
4065 5216
}
4066 5217
4067 5218
/// Test record pattern with fields in reverse order.
4068 5219
@test unsafe fn testResolveMatchRecordPatternReversed() throws (testing::TestError) {
4069 -
    let mut a = testResolver();
4070 -
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }";
4071 -
    let result = try resolveProgramStr(&mut a, program);
4072 -
    try expectNoErrors(&result);
5220 +
    let mut testArena377 = testArena();
5221 +
    let testStorage377: 'test377 = &mut testArena377 in {
5222 +
        let mut a = testResolver(testStorage377);
5223 +
        let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }";
5224 +
        let result = try resolveProgramStr(&mut a, program);
5225 +
        try expectNoErrors(&result);
5226 +
    }
4073 5227
}
4074 5228
4075 5229
/// Test record pattern with shorthand syntax in reverse order.
4076 5230
/// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order.
4077 5231
@test unsafe fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) {
4078 -
    let mut a = testResolver();
4079 -
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }";
4080 -
    let result = try resolveProgramStr(&mut a, program);
4081 -
    try expectNoErrors(&result);
5232 +
    let mut testArena378 = testArena();
5233 +
    let testStorage378: 'test378 = &mut testArena378 in {
5234 +
        let mut a = testResolver(testStorage378);
5235 +
        let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }";
5236 +
        let result = try resolveProgramStr(&mut a, program);
5237 +
        try expectNoErrors(&result);
5238 +
    }
4082 5239
}
4083 5240
4084 5241
/// Test record pattern with `..` ignoring fields.
4085 5242
@test unsafe fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) {
4086 5243
    {
4087 -
        let mut a = testResolver();
4088 -
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }";
4089 -
        let result = try resolveProgramStr(&mut a, program);
4090 -
        try expectNoErrors(&result);
5244 +
        let mut testArena379 = testArena();
5245 +
        let testStorage379: 'test379 = &mut testArena379 in {
5246 +
            let mut a = testResolver(testStorage379);
5247 +
            let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }";
5248 +
            let result = try resolveProgramStr(&mut a, program);
5249 +
            try expectNoErrors(&result);
5250 +
        }
4091 5251
    } {
4092 -
        let mut a = testResolver();
4093 -
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x: val, .. } => return val } }";
4094 -
        let result = try resolveProgramStr(&mut a, program);
4095 -
        try expectNoErrors(&result);
5252 +
        let mut testArena380 = testArena();
5253 +
        let testStorage380: 'test380 = &mut testArena380 in {
5254 +
            let mut a = testResolver(testStorage380);
5255 +
            let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x: val, .. } => return val } }";
5256 +
            let result = try resolveProgramStr(&mut a, program);
5257 +
            try expectNoErrors(&result);
5258 +
        }
4096 5259
    } {
4097 -
        let mut a = testResolver();
4098 -
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }";
4099 -
        let result = try resolveProgramStr(&mut a, program);
4100 -
        try expectNoErrors(&result);
5260 +
        let mut testArena381 = testArena();
5261 +
        let testStorage381: 'test381 = &mut testArena381 in {
5262 +
            let mut a = testResolver(testStorage381);
5263 +
            let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }";
5264 +
            let result = try resolveProgramStr(&mut a, program);
5265 +
            try expectNoErrors(&result);
5266 +
        }
4101 5267
    } {
4102 -
        let mut a = testResolver();
4103 -
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, x, .. } => return x + z } }";
4104 -
        let result = try resolveProgramStr(&mut a, program);
4105 -
        try expectNoErrors(&result);
5268 +
        let mut testArena382 = testArena();
5269 +
        let testStorage382: 'test382 = &mut testArena382 in {
5270 +
            let mut a = testResolver(testStorage382);
5271 +
            let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, x, .. } => return x + z } }";
5272 +
            let result = try resolveProgramStr(&mut a, program);
5273 +
            try expectNoErrors(&result);
5274 +
        }
4106 5275
    } {
4107 -
        let mut a = testResolver();
4108 -
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }";
4109 -
        let result = try resolveProgramStr(&mut a, program);
4110 -
        try expectNoErrors(&result);
5276 +
        let mut testArena383 = testArena();
5277 +
        let testStorage383: 'test383 = &mut testArena383 in {
5278 +
            let mut a = testResolver(testStorage383);
5279 +
            let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }";
5280 +
            let result = try resolveProgramStr(&mut a, program);
5281 +
            try expectNoErrors(&result);
5282 +
        }
4111 5283
    }
4112 5284
}
4113 5285
4114 5286
/// Test standalone record pattern matching with unlabeled patterns.
4115 5287
@test unsafe fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) {
4116 -
    let mut a = testResolver();
4117 -
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }";
4118 -
    let result = try resolveProgramStr(&mut a, program);
4119 -
    try expectNoErrors(&result);
5288 +
    let mut testArena384 = testArena();
5289 +
    let testStorage384: 'test384 = &mut testArena384 in {
5290 +
        let mut a = testResolver(testStorage384);
5291 +
        let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }";
5292 +
        let result = try resolveProgramStr(&mut a, program);
5293 +
        try expectNoErrors(&result);
5294 +
    }
4120 5295
}
4121 5296
4122 5297
/// Test standalone record pattern matching with labeled patterns.
4123 5298
/// Pattern syntax: `T { x }` matches a named record and binds x to the field.
4124 5299
@test unsafe fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) {
4125 -
    let mut a = testResolver();
4126 -
    let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }";
4127 -
    let result = try resolveProgramStr(&mut a, program);
4128 -
    try expectNoErrors(&result);
5300 +
    let mut testArena385 = testArena();
5301 +
    let testStorage385: 'test385 = &mut testArena385 in {
5302 +
        let mut a = testResolver(testStorage385);
5303 +
        let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }";
5304 +
        let result = try resolveProgramStr(&mut a, program);
5305 +
        try expectNoErrors(&result);
5306 +
    }
4129 5307
}
4130 5308
4131 5309
/// Test standalone record pattern with multiple fields.
4132 5310
/// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields.
4133 5311
@test unsafe fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) {
4134 -
    let mut a = testResolver();
4135 -
    let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }";
4136 -
    let result = try resolveProgramStr(&mut a, program);
4137 -
    try expectNoErrors(&result);
5312 +
    let mut testArena386 = testArena();
5313 +
    let testStorage386: 'test386 = &mut testArena386 in {
5314 +
        let mut a = testResolver(testStorage386);
5315 +
        let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }";
5316 +
        let result = try resolveProgramStr(&mut a, program);
5317 +
        try expectNoErrors(&result);
5318 +
    }
4138 5319
}
4139 5320
4140 5321
/// Test standalone record pattern with wrong field count.
4141 5322
/// Pattern `S(x, y)` should fail for a single-field record.
4142 5323
@test unsafe fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) {
4143 -
    let mut a = testResolver();
4144 -
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }";
4145 -
    let result = try resolveProgramStr(&mut a, program);
4146 -
    let err = try expectError(&result);
4147 -
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4148 -
        else throw testing::TestError::Failed;
5324 +
    let mut testArena387 = testArena();
5325 +
    let testStorage387: 'test387 = &mut testArena387 in {
5326 +
        let mut a = testResolver(testStorage387);
5327 +
        let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }";
5328 +
        let result = try resolveProgramStr(&mut a, program);
5329 +
        let err = try expectError(&result);
5330 +
        let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
5331 +
            else throw testing::TestError::Failed;
5332 +
    }
4149 5333
}
4150 5334
4151 5335
/// Test array pattern matching with element bindings.
4152 5336
/// Pattern syntax: `[x, y]` matches an array and binds elements.
4153 5337
@test unsafe fn testResolveMatchArrayPattern() throws (testing::TestError) {
4154 -
    let mut a = testResolver();
4155 -
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }";
4156 -
    let result = try resolveProgramStr(&mut a, program);
4157 -
    try expectNoErrors(&result);
5338 +
    let mut testArena388 = testArena();
5339 +
    let testStorage388: 'test388 = &mut testArena388 in {
5340 +
        let mut a = testResolver(testStorage388);
5341 +
        let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }";
5342 +
        let result = try resolveProgramStr(&mut a, program);
5343 +
        try expectNoErrors(&result);
5344 +
    }
4158 5345
}
4159 5346
4160 5347
/// Test array pattern with placeholder elements.
4161 5348
/// Pattern syntax: `[_, y]` ignores first element.
4162 5349
@test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) {
4163 -
    let mut a = testResolver();
4164 -
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }";
4165 -
    let result = try resolveProgramStr(&mut a, program);
4166 -
    try expectNoErrors(&result);
5350 +
    let mut testArena389 = testArena();
5351 +
    let testStorage389: 'test389 = &mut testArena389 in {
5352 +
        let mut a = testResolver(testStorage389);
5353 +
        let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }";
5354 +
        let result = try resolveProgramStr(&mut a, program);
5355 +
        try expectNoErrors(&result);
5356 +
    }
4167 5357
}
4168 5358
4169 5359
/// Test identifier pattern that binds the whole value.
4170 5360
/// Pattern syntax: `x` matches any value and binds it.
4171 5361
@test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) {
4172 -
    let mut a = testResolver();
4173 -
    let program = "fn f(val: i32) -> i32 { match val { x => return x } }";
4174 -
    let result = try resolveProgramStr(&mut a, program);
4175 -
    try expectNoErrors(&result);
5362 +
    let mut testArena390 = testArena();
5363 +
    let testStorage390: 'test390 = &mut testArena390 in {
5364 +
        let mut a = testResolver(testStorage390);
5365 +
        let program = "fn f(val: i32) -> i32 { match val { x => return x } }";
5366 +
        let result = try resolveProgramStr(&mut a, program);
5367 +
        try expectNoErrors(&result);
5368 +
    }
4176 5369
}
4177 5370
4178 5371
/// Test numeric literal pattern matching.
4179 5372
@test unsafe fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) {
4180 -
    let mut a = testResolver();
4181 -
    let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }";
4182 -
    let result = try resolveProgramStr(&mut a, program);
4183 -
    try expectNoErrors(&result);
5373 +
    let mut testArena391 = testArena();
5374 +
    let testStorage391: 'test391 = &mut testArena391 in {
5375 +
        let mut a = testResolver(testStorage391);
5376 +
        let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }";
5377 +
        let result = try resolveProgramStr(&mut a, program);
5378 +
        try expectNoErrors(&result);
5379 +
    }
4184 5380
}
4185 5381
4186 5382
/// Test string literal pattern matching.
4187 5383
@test unsafe fn testResolveMatchStringLiteralPattern() throws (testing::TestError) {
4188 -
    let mut a = testResolver();
4189 -
    let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }";
4190 -
    let result = try resolveProgramStr(&mut a, program);
4191 -
    try expectNoErrors(&result);
5384 +
    let mut testArena392 = testArena();
5385 +
    let testStorage392: 'test392 = &mut testArena392 in {
5386 +
        let mut a = testResolver(testStorage392);
5387 +
        let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }";
5388 +
        let result = try resolveProgramStr(&mut a, program);
5389 +
        try expectNoErrors(&result);
5390 +
    }
4192 5391
}
4193 5392
4194 5393
/// Test boolean literal pattern matching.
4195 5394
@test unsafe fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) {
4196 -
    let mut a = testResolver();
4197 -
    let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }";
4198 -
    let result = try resolveProgramStr(&mut a, program);
4199 -
    try expectNoErrors(&result);
5395 +
    let mut testArena393 = testArena();
5396 +
    let testStorage393: 'test393 = &mut testArena393 in {
5397 +
        let mut a = testResolver(testStorage393);
5398 +
        let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }";
5399 +
        let result = try resolveProgramStr(&mut a, program);
5400 +
        try expectNoErrors(&result);
5401 +
    }
4200 5402
}
4201 5403
4202 5404
/// Test @sliceOf with correct arguments succeeds.
4203 5405
@test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) {
4204 5406
    // Immutable pointer.
4205 5407
    {
4206 -
        let mut a = testResolver();
4207 -
        let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }";
4208 -
        let result = try resolveProgramStr(&mut a, program);
4209 -
        try expectNoErrors(&result);
5408 +
        let mut testArena394 = testArena();
5409 +
        let testStorage394: 'test394 = &mut testArena394 in {
5410 +
            let mut a = testResolver(testStorage394);
5411 +
            let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }";
5412 +
            let result = try resolveProgramStr(&mut a, program);
5413 +
            try expectNoErrors(&result);
5414 +
        }
4210 5415
    }
4211 5416
    // Mutable pointer produces mutable slice.
4212 5417
    {
4213 -
        let mut a = testResolver();
4214 -
        let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }";
4215 -
        let result = try resolveProgramStr(&mut a, program);
4216 -
        try expectNoErrors(&result);
5418 +
        let mut testArena395 = testArena();
5419 +
        let testStorage395: 'test395 = &mut testArena395 in {
5420 +
            let mut a = testResolver(testStorage395);
5421 +
            let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }";
5422 +
            let result = try resolveProgramStr(&mut a, program);
5423 +
            try expectNoErrors(&result);
5424 +
        }
4217 5425
    }
4218 5426
}
4219 5427
4220 5428
/// Test @sliceOf with wrong argument count produces an error.
4221 5429
@test unsafe fn testResolveSliceOfWrongArgCount() throws (testing::TestError) {
4222 5430
    // No arguments.
4223 5431
    {
4224 -
        let mut a = testResolver();
4225 -
        let program = "fn f() -> *[u8] { return @sliceOf(); }";
4226 -
        let result = try resolveProgramStr(&mut a, program);
4227 -
        let err = try expectError(&result);
4228 -
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4229 -
            else throw testing::TestError::Failed;
4230 -
        try testing::expect(mismatch.expected == 2);
4231 -
        try testing::expect(mismatch.actual == 0);
5432 +
        let mut testArena396 = testArena();
5433 +
        let testStorage396: 'test396 = &mut testArena396 in {
5434 +
            let mut a = testResolver(testStorage396);
5435 +
            let program = "fn f() -> *[u8] { return @sliceOf(); }";
5436 +
            let result = try resolveProgramStr(&mut a, program);
5437 +
            let err = try expectError(&result);
5438 +
            let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
5439 +
                else throw testing::TestError::Failed;
5440 +
            try testing::expect(mismatch.expected == 2);
5441 +
            try testing::expect(mismatch.actual == 0);
5442 +
        }
4232 5443
    }
4233 5444
    // Too few arguments.
4234 5445
    {
4235 -
        let mut a = testResolver();
4236 -
        let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }";
4237 -
        let result = try resolveProgramStr(&mut a, program);
4238 -
        let err = try expectError(&result);
4239 -
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4240 -
            else throw testing::TestError::Failed;
4241 -
        try testing::expect(mismatch.expected == 2);
4242 -
        try testing::expect(mismatch.actual == 1);
5446 +
        let mut testArena397 = testArena();
5447 +
        let testStorage397: 'test397 = &mut testArena397 in {
5448 +
            let mut a = testResolver(testStorage397);
5449 +
            let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }";
5450 +
            let result = try resolveProgramStr(&mut a, program);
5451 +
            let err = try expectError(&result);
5452 +
            let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
5453 +
                else throw testing::TestError::Failed;
5454 +
            try testing::expect(mismatch.expected == 2);
5455 +
            try testing::expect(mismatch.actual == 1);
5456 +
        }
4243 5457
    }
4244 5458
    // Too many arguments.
4245 5459
    {
4246 -
        let mut a = testResolver();
4247 -
        let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }";
4248 -
        let result = try resolveProgramStr(&mut a, program);
4249 -
        let err = try expectError(&result);
4250 -
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4251 -
            else throw testing::TestError::Failed;
4252 -
        try testing::expect(mismatch.expected == 2);
4253 -
        try testing::expect(mismatch.actual == 4);
5460 +
        let mut testArena398 = testArena();
5461 +
        let testStorage398: 'test398 = &mut testArena398 in {
5462 +
            let mut a = testResolver(testStorage398);
5463 +
            let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }";
5464 +
            let result = try resolveProgramStr(&mut a, program);
5465 +
            let err = try expectError(&result);
5466 +
            let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
5467 +
                else throw testing::TestError::Failed;
5468 +
            try testing::expect(mismatch.expected == 2);
5469 +
            try testing::expect(mismatch.actual == 4);
5470 +
        }
4254 5471
    }
4255 5472
}
4256 5473
4257 5474
/// Test @sliceOf with wrong argument types produces errors.
4258 5475
@test unsafe fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) {
4259 5476
    // Non-pointer first argument.
4260 5477
    {
4261 -
        let mut a = testResolver();
4262 -
        let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }";
4263 -
        let result = try resolveProgramStr(&mut a, program);
4264 -
        let err = try expectError(&result);
4265 -
        let case super::ErrorKind::ExpectedPointer = err.kind
4266 -
            else throw testing::TestError::Failed;
5478 +
        let mut testArena399 = testArena();
5479 +
        let testStorage399: 'test399 = &mut testArena399 in {
5480 +
            let mut a = testResolver(testStorage399);
5481 +
            let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }";
5482 +
            let result = try resolveProgramStr(&mut a, program);
5483 +
            let err = try expectError(&result);
5484 +
            let case super::ErrorKind::ExpectedPointer = err.kind
5485 +
                else throw testing::TestError::Failed;
5486 +
        }
4267 5487
    }
4268 5488
    // Array instead of pointer.
4269 5489
    {
4270 -
        let mut a = testResolver();
4271 -
        let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }";
4272 -
        let result = try resolveProgramStr(&mut a, program);
4273 -
        let err = try expectError(&result);
4274 -
        let case super::ErrorKind::ExpectedPointer = err.kind
4275 -
            else throw testing::TestError::Failed;
5490 +
        let mut testArena400 = testArena();
5491 +
        let testStorage400: 'test400 = &mut testArena400 in {
5492 +
            let mut a = testResolver(testStorage400);
5493 +
            let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }";
5494 +
            let result = try resolveProgramStr(&mut a, program);
5495 +
            let err = try expectError(&result);
5496 +
            let case super::ErrorKind::ExpectedPointer = err.kind
5497 +
                else throw testing::TestError::Failed;
5498 +
        }
4276 5499
    }
4277 5500
    // Non-numeric second argument.
4278 5501
    {
4279 -
        let mut a = testResolver();
4280 -
        let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }";
4281 -
        let result = try resolveProgramStr(&mut a, program);
4282 -
        let err = try expectError(&result);
4283 -
        let case super::ErrorKind::TypeMismatch(_) = err.kind
4284 -
            else throw testing::TestError::Failed;
5502 +
        let mut testArena401 = testArena();
5503 +
        let testStorage401: 'test401 = &mut testArena401 in {
5504 +
            let mut a = testResolver(testStorage401);
5505 +
            let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }";
5506 +
            let result = try resolveProgramStr(&mut a, program);
5507 +
            let err = try expectError(&result);
5508 +
            let case super::ErrorKind::TypeMismatch(_) = err.kind
5509 +
                else throw testing::TestError::Failed;
5510 +
        }
4285 5511
    }
4286 5512
    // Pointer second argument.
4287 5513
    {
4288 -
        let mut a = testResolver();
4289 -
        let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }";
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;
5514 +
        let mut testArena402 = testArena();
5515 +
        let testStorage402: 'test402 = &mut testArena402 in {
5516 +
            let mut a = testResolver(testStorage402);
5517 +
            let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }";
5518 +
            let result = try resolveProgramStr(&mut a, program);
5519 +
            let err = try expectError(&result);
5520 +
            let case super::ErrorKind::TypeMismatch(_) = err.kind
5521 +
                else throw testing::TestError::Failed;
5522 +
        }
4294 5523
    }
4295 5524
}
4296 5525
4297 5526
/// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds.
4298 5527
@test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) {
4299 5528
    {
4300 -
        let mut a = testResolver();
4301 -
        let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4302 -
        let result = try resolveProgramStr(&mut a, program);
4303 -
        try expectNoErrors(&result);
5529 +
        let mut testArena403 = testArena();
5530 +
        let testStorage403: 'test403 = &mut testArena403 in {
5531 +
            let mut a = testResolver(testStorage403);
5532 +
            let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
5533 +
            let result = try resolveProgramStr(&mut a, program);
5534 +
            try expectNoErrors(&result);
5535 +
        }
4304 5536
    }
4305 5537
    // Mutable pointer produces mutable slice.
4306 5538
    {
4307 -
        let mut a = testResolver();
4308 -
        let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }";
4309 -
        let result = try resolveProgramStr(&mut a, program);
4310 -
        try expectNoErrors(&result);
5539 +
        let mut testArena404 = testArena();
5540 +
        let testStorage404: 'test404 = &mut testArena404 in {
5541 +
            let mut a = testResolver(testStorage404);
5542 +
            let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }";
5543 +
            let result = try resolveProgramStr(&mut a, program);
5544 +
            try expectNoErrors(&result);
5545 +
        }
4311 5546
    }
4312 5547
}
4313 5548
4314 5549
/// Test @sliceOf with 3 arguments but wrong cap type.
4315 5550
@test unsafe fn testResolveSliceOfCapWrongType() throws (testing::TestError) {
4316 -
    let mut a = testResolver();
4317 -
    let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4318 -
    let result = try resolveProgramStr(&mut a, program);
4319 -
    let err = try expectError(&result);
4320 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4321 -
        else throw testing::TestError::Failed;
5551 +
    let mut testArena405 = testArena();
5552 +
    let testStorage405: 'test405 = &mut testArena405 in {
5553 +
        let mut a = testResolver(testStorage405);
5554 +
        let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }";
5555 +
        let result = try resolveProgramStr(&mut a, program);
5556 +
        let err = try expectError(&result);
5557 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5558 +
            else throw testing::TestError::Failed;
5559 +
    }
4322 5560
}
4323 5561
4324 5562
/// Test .cap field access on slices resolves to u32.
4325 5563
@test unsafe fn testResolveSliceCapField() throws (testing::TestError) {
4326 -
    let mut a = testResolver();
4327 -
    let program = "fn f(s: *[u8]) -> u32 { return s.cap; }";
4328 -
    let result = try resolveProgramStr(&mut a, program);
4329 -
    try expectNoErrors(&result);
5564 +
    let mut testArena406 = testArena();
5565 +
    let testStorage406: 'test406 = &mut testArena406 in {
5566 +
        let mut a = testResolver(testStorage406);
5567 +
        let program = "fn f(s: *[u8]) -> u32 { return s.cap; }";
5568 +
        let result = try resolveProgramStr(&mut a, program);
5569 +
        try expectNoErrors(&result);
5570 +
    }
4330 5571
}
4331 5572
4332 5573
/// Test `.append()` on immutable slice produces an error.
4333 5574
@test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4334 -
    let mut a = testResolver();
4335 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }";
4336 -
    let result = try resolveProgramStr(&mut a, program);
4337 -
    let err = try expectError(&result);
4338 -
    let case super::ErrorKind::ImmutableBinding = err.kind
4339 -
        else throw testing::TestError::Failed;
5575 +
    let mut testArena407 = testArena();
5576 +
    let testStorage407: 'test407 = &mut testArena407 in {
5577 +
        let mut a = testResolver(testStorage407);
5578 +
        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); }";
5579 +
        let result = try resolveProgramStr(&mut a, program);
5580 +
        let err = try expectError(&result);
5581 +
        let case super::ErrorKind::ImmutableBinding = err.kind
5582 +
            else throw testing::TestError::Failed;
5583 +
    }
4340 5584
}
4341 5585
4342 5586
/// Test `.append()` with wrong argument count produces an error.
4343 5587
@test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4344 5588
    // Too few arguments.
4345 5589
    {
4346 -
        let mut a = testResolver();
4347 -
        let program = "fn f(s: *mut [i32]) { s.append(1); }";
4348 -
        let result = try resolveProgramStr(&mut a, program);
4349 -
        let err = try expectError(&result);
4350 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4351 -
            else throw testing::TestError::Failed;
4352 -
        try testing::expect(m.expected == 2);
4353 -
        try testing::expect(m.actual == 1);
5590 +
        let mut testArena408 = testArena();
5591 +
        let testStorage408: 'test408 = &mut testArena408 in {
5592 +
            let mut a = testResolver(testStorage408);
5593 +
            let program = "fn f(s: *mut [i32]) { s.append(1); }";
5594 +
            let result = try resolveProgramStr(&mut a, program);
5595 +
            let err = try expectError(&result);
5596 +
            let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
5597 +
                else throw testing::TestError::Failed;
5598 +
            try testing::expect(m.expected == 2);
5599 +
            try testing::expect(m.actual == 1);
5600 +
        }
4354 5601
    }
4355 5602
    // Too many arguments.
4356 5603
    {
4357 -
        let mut a = testResolver();
4358 -
        let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }";
4359 -
        let result = try resolveProgramStr(&mut a, program);
4360 -
        let err = try expectError(&result);
4361 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4362 -
            else throw testing::TestError::Failed;
4363 -
        try testing::expect(m.expected == 2);
4364 -
        try testing::expect(m.actual == 3);
5604 +
        let mut testArena409 = testArena();
5605 +
        let testStorage409: 'test409 = &mut testArena409 in {
5606 +
            let mut a = testResolver(testStorage409);
5607 +
            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); }";
5608 +
            let result = try resolveProgramStr(&mut a, program);
5609 +
            let err = try expectError(&result);
5610 +
            let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
5611 +
                else throw testing::TestError::Failed;
5612 +
            try testing::expect(m.expected == 2);
5613 +
            try testing::expect(m.actual == 3);
5614 +
        }
4365 5615
    }
4366 5616
}
4367 5617
4368 5618
/// Test `.append()` with correct arguments succeeds.
4369 5619
@test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4370 -
    let mut a = testResolver();
4371 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }";
4372 -
    let result = try resolveProgramStr(&mut a, program);
4373 -
    try expectNoErrors(&result);
5620 +
    let mut testArena410 = testArena();
5621 +
    let testStorage410: 'test410 = &mut testArena410 in {
5622 +
        let mut a = testResolver(testStorage410);
5623 +
        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); }";
5624 +
        let result = try resolveProgramStr(&mut a, program);
5625 +
        try expectNoErrors(&result);
5626 +
    }
4374 5627
}
4375 5628
4376 5629
/// Test `.append()` with wrong element type produces an error.
4377 5630
@test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4378 -
    let mut a = testResolver();
4379 -
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }";
4380 -
    let result = try resolveProgramStr(&mut a, program);
4381 -
    let err = try expectError(&result);
4382 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4383 -
        else throw testing::TestError::Failed;
5631 +
    let mut testArena411 = testArena();
5632 +
    let testStorage411: 'test411 = &mut testArena411 in {
5633 +
        let mut a = testResolver(testStorage411);
5634 +
        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); }";
5635 +
        let result = try resolveProgramStr(&mut a, program);
5636 +
        let err = try expectError(&result);
5637 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5638 +
            else throw testing::TestError::Failed;
5639 +
    }
4384 5640
}
4385 5641
4386 5642
/// Test `.delete()` on immutable slice produces an error.
4387 5643
@test unsafe fn testResolveSliceDeleteImmutable() throws (testing::TestError) {
4388 -
    let mut a = testResolver();
4389 -
    let program = "fn f(s: *[i32]) { s.delete(0); }";
4390 -
    let result = try resolveProgramStr(&mut a, program);
4391 -
    let err = try expectError(&result);
4392 -
    let case super::ErrorKind::ImmutableBinding = err.kind
4393 -
        else throw testing::TestError::Failed;
5644 +
    let mut testArena412 = testArena();
5645 +
    let testStorage412: 'test412 = &mut testArena412 in {
5646 +
        let mut a = testResolver(testStorage412);
5647 +
        let program = "fn f(s: *[i32]) { s.delete(0); }";
5648 +
        let result = try resolveProgramStr(&mut a, program);
5649 +
        let err = try expectError(&result);
5650 +
        let case super::ErrorKind::ImmutableBinding = err.kind
5651 +
            else throw testing::TestError::Failed;
5652 +
    }
4394 5653
}
4395 5654
4396 5655
/// Test `.delete()` with wrong argument count produces an error.
4397 5656
@test unsafe fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) {
4398 5657
    // No arguments.
4399 5658
    {
4400 -
        let mut a = testResolver();
4401 -
        let program = "fn f(s: *mut [i32]) { s.delete(); }";
4402 -
        let result = try resolveProgramStr(&mut a, program);
4403 -
        let err = try expectError(&result);
4404 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4405 -
            else throw testing::TestError::Failed;
4406 -
        try testing::expect(m.expected == 1);
4407 -
        try testing::expect(m.actual == 0);
5659 +
        let mut testArena413 = testArena();
5660 +
        let testStorage413: 'test413 = &mut testArena413 in {
5661 +
            let mut a = testResolver(testStorage413);
5662 +
            let program = "fn f(s: *mut [i32]) { s.delete(); }";
5663 +
            let result = try resolveProgramStr(&mut a, program);
5664 +
            let err = try expectError(&result);
5665 +
            let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
5666 +
                else throw testing::TestError::Failed;
5667 +
            try testing::expect(m.expected == 1);
5668 +
            try testing::expect(m.actual == 0);
5669 +
        }
4408 5670
    }
4409 5671
    // Too many arguments.
4410 5672
    {
4411 -
        let mut a = testResolver();
4412 -
        let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }";
4413 -
        let result = try resolveProgramStr(&mut a, program);
4414 -
        let err = try expectError(&result);
4415 -
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4416 -
            else throw testing::TestError::Failed;
4417 -
        try testing::expect(m.expected == 1);
4418 -
        try testing::expect(m.actual == 2);
5673 +
        let mut testArena414 = testArena();
5674 +
        let testStorage414: 'test414 = &mut testArena414 in {
5675 +
            let mut a = testResolver(testStorage414);
5676 +
            let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }";
5677 +
            let result = try resolveProgramStr(&mut a, program);
5678 +
            let err = try expectError(&result);
5679 +
            let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
5680 +
                else throw testing::TestError::Failed;
5681 +
            try testing::expect(m.expected == 1);
5682 +
            try testing::expect(m.actual == 2);
5683 +
        }
4419 5684
    }
4420 5685
}
4421 5686
4422 5687
/// Test `.delete()` with correct arguments succeeds.
4423 5688
@test unsafe fn testResolveSliceDeleteCorrect() throws (testing::TestError) {
4424 -
    let mut a = testResolver();
4425 -
    let program = "fn f(s: *mut [i32]) { s.delete(0); }";
4426 -
    let result = try resolveProgramStr(&mut a, program);
4427 -
    try expectNoErrors(&result);
5689 +
    let mut testArena415 = testArena();
5690 +
    let testStorage415: 'test415 = &mut testArena415 in {
5691 +
        let mut a = testResolver(testStorage415);
5692 +
        let program = "fn f(s: *mut [i32]) { s.delete(0); }";
5693 +
        let result = try resolveProgramStr(&mut a, program);
5694 +
        try expectNoErrors(&result);
5695 +
    }
4428 5696
}
4429 5697
4430 5698
/// Test `.delete()` with wrong argument type produces an error.
4431 5699
@test unsafe fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) {
4432 -
    let mut a = testResolver();
4433 -
    let program = "fn f(s: *mut [i32]) { s.delete(true); }";
4434 -
    let result = try resolveProgramStr(&mut a, program);
4435 -
    let err = try expectError(&result);
4436 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4437 -
        else throw testing::TestError::Failed;
5700 +
    let mut testArena416 = testArena();
5701 +
    let testStorage416: 'test416 = &mut testArena416 in {
5702 +
        let mut a = testResolver(testStorage416);
5703 +
        let program = "fn f(s: *mut [i32]) { s.delete(true); }";
5704 +
        let result = try resolveProgramStr(&mut a, program);
5705 +
        let err = try expectError(&result);
5706 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5707 +
            else throw testing::TestError::Failed;
5708 +
    }
4438 5709
}
4439 5710
4440 5711
/// Test `match &opt` produces immutable pointer bindings.
4441 5712
@test unsafe fn testResolveMatchRefUnionBinding() throws (testing::TestError) {
4442 -
    let mut a = testResolver();
4443 -
    let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }";
4444 -
    let result = try resolveProgramStr(&mut a, program);
4445 -
    try expectNoErrors(&result);
4446 -
4447 -
    let fnBlock = try getFnBody(&a, result.root, "f");
4448 -
    let matchNode = fnBlock.statements[1];
4449 -
    let case ast::NodeValue::Match(sw) = matchNode.value
4450 -
        else throw testing::TestError::Failed;
4451 -
    let caseNode = sw.prongs[0];
5713 +
    let mut testArena417 = testArena();
5714 +
    let testStorage417: 'test417 = &mut testArena417 in {
5715 +
        let mut a = testResolver(testStorage417);
5716 +
        let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }";
5717 +
        let result = try resolveProgramStr(&mut a, program);
5718 +
        try expectNoErrors(&result);
4452 5719
4453 -
    let scope = super::scopeFor(&a, caseNode)
4454 -
        else throw testing::TestError::Failed;
4455 -
    let payloadSym = super::findSymbolInScope(scope, "x")
4456 -
        else throw testing::TestError::Failed;
4457 -
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4458 -
        else throw testing::TestError::Failed;
4459 -
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4460 -
        else throw testing::TestError::Failed;
4461 -
    try testing::expect(not mutable);
4462 -
    try testing::expect(*target == super::Type::I32);
5720 +
        let fnBlock = try getFnBody(&a, result.root, "f");
5721 +
        let matchNode = fnBlock.statements[1];
5722 +
        let case ast::NodeValue::Match(sw) = matchNode.value
5723 +
            else throw testing::TestError::Failed;
5724 +
        let caseNode = sw.prongs[0];
5725 +
5726 +
        let scope = super::scopeFor(&a, caseNode)
5727 +
            else throw testing::TestError::Failed;
5728 +
        let payloadSym = super::findSymbolInScope(scope, "x")
5729 +
            else throw testing::TestError::Failed;
5730 +
        let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
5731 +
            else throw testing::TestError::Failed;
5732 +
        let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
5733 +
            else throw testing::TestError::Failed;
5734 +
        try testing::expect(not mutable);
5735 +
        try testing::expect(*target == super::Type::I32);
5736 +
    }
4463 5737
}
4464 5738
4465 5739
/// Test `match &mut opt` produces mutable pointer bindings.
4466 5740
@test unsafe fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) {
4467 -
    let mut a = testResolver();
4468 -
    let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }";
4469 -
    let result = try resolveProgramStr(&mut a, program);
4470 -
    try expectNoErrors(&result);
4471 -
4472 -
    let fnBlock = try getFnBody(&a, result.root, "f");
4473 -
    let matchNode = fnBlock.statements[1];
4474 -
    let case ast::NodeValue::Match(sw) = matchNode.value
4475 -
        else throw testing::TestError::Failed;
4476 -
    let caseNode = sw.prongs[0];
5741 +
    let mut testArena418 = testArena();
5742 +
    let testStorage418: 'test418 = &mut testArena418 in {
5743 +
        let mut a = testResolver(testStorage418);
5744 +
        let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }";
5745 +
        let result = try resolveProgramStr(&mut a, program);
5746 +
        try expectNoErrors(&result);
4477 5747
4478 -
    let scope = super::scopeFor(&a, caseNode)
4479 -
        else throw testing::TestError::Failed;
4480 -
    let payloadSym = super::findSymbolInScope(scope, "x")
4481 -
        else throw testing::TestError::Failed;
4482 -
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4483 -
        else throw testing::TestError::Failed;
4484 -
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4485 -
        else throw testing::TestError::Failed;
4486 -
    try testing::expect(mutable);
4487 -
    try testing::expect(*target == super::Type::I32);
5748 +
        let fnBlock = try getFnBody(&a, result.root, "f");
5749 +
        let matchNode = fnBlock.statements[1];
5750 +
        let case ast::NodeValue::Match(sw) = matchNode.value
5751 +
            else throw testing::TestError::Failed;
5752 +
        let caseNode = sw.prongs[0];
5753 +
5754 +
        let scope = super::scopeFor(&a, caseNode)
5755 +
            else throw testing::TestError::Failed;
5756 +
        let payloadSym = super::findSymbolInScope(scope, "x")
5757 +
            else throw testing::TestError::Failed;
5758 +
        let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
5759 +
            else throw testing::TestError::Failed;
5760 +
        let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
5761 +
            else throw testing::TestError::Failed;
5762 +
        try testing::expect(mutable);
5763 +
        try testing::expect(*target == super::Type::I32);
5764 +
    }
4488 5765
}
4489 5766
4490 5767
/// Non-constant integer widening must use an explicit cast.
4491 5768
@test unsafe fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) {
4492 5769
    {
4493 -
        let mut a = testResolver();
4494 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;");
4495 -
        let err = try expectError(&result);
4496 -
        try expectTypeMismatch(err, super::Type::U32, super::Type::U8);
5770 +
        let mut testArena419 = testArena();
5771 +
        let testStorage419: 'test419 = &mut testArena419 in {
5772 +
            let mut a = testResolver(testStorage419);
5773 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;");
5774 +
            let err = try expectError(&result);
5775 +
            try expectTypeMismatch(err, super::Type::U32, super::Type::U8);
5776 +
        }
4497 5777
    } {
4498 -
        let mut a = testResolver();
4499 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;");
4500 -
        let err = try expectError(&result);
4501 -
        try expectTypeMismatch(err, super::Type::U16, super::Type::U8);
5778 +
        let mut testArena420 = testArena();
5779 +
        let testStorage420: 'test420 = &mut testArena420 in {
5780 +
            let mut a = testResolver(testStorage420);
5781 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;");
5782 +
            let err = try expectError(&result);
5783 +
            try expectTypeMismatch(err, super::Type::U16, super::Type::U8);
5784 +
        }
4502 5785
    } {
4503 -
        let mut a = testResolver();
4504 -
        let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;");
4505 -
        let err = try expectError(&result);
4506 -
        try expectTypeMismatch(err, super::Type::U32, super::Type::U16);
5786 +
        let mut testArena421 = testArena();
5787 +
        let testStorage421: 'test421 = &mut testArena421 in {
5788 +
            let mut a = testResolver(testStorage421);
5789 +
            let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;");
5790 +
            let err = try expectError(&result);
5791 +
            try expectTypeMismatch(err, super::Type::U32, super::Type::U16);
5792 +
        }
4507 5793
    } {
4508 -
        let mut a = testResolver();
4509 -
        let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;");
4510 -
        let err = try expectError(&result);
4511 -
        try expectTypeMismatch(err, super::Type::I32, super::Type::I8);
5794 +
        let mut testArena422 = testArena();
5795 +
        let testStorage422: 'test422 = &mut testArena422 in {
5796 +
            let mut a = testResolver(testStorage422);
5797 +
            let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;");
5798 +
            let err = try expectError(&result);
5799 +
            try expectTypeMismatch(err, super::Type::I32, super::Type::I8);
5800 +
        }
4512 5801
    } {
4513 -
        let mut a = testResolver();
4514 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;");
4515 -
        try expectNoErrors(&result);
5802 +
        let mut testArena423 = testArena();
5803 +
        let testStorage423: 'test423 = &mut testArena423 in {
5804 +
            let mut a = testResolver(testStorage423);
5805 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;");
5806 +
            try expectNoErrors(&result);
5807 +
        }
4516 5808
    } {
4517 -
        let mut a = testResolver();
4518 -
        let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;");
4519 -
        try expectNoErrors(&result);
5809 +
        let mut testArena424 = testArena();
5810 +
        let testStorage424: 'test424 = &mut testArena424 in {
5811 +
            let mut a = testResolver(testStorage424);
5812 +
            let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;");
5813 +
            try expectNoErrors(&result);
5814 +
        }
4520 5815
    }
4521 5816
}
4522 5817
4523 5818
/// Mixed-width integer binary ops require an explicit cast.
4524 5819
@test unsafe fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) {
4525 5820
    {
4526 -
        let mut a = testResolver();
4527 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;");
4528 -
        let err = try expectError(&result);
4529 -
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5821 +
        let mut testArena425 = testArena();
5822 +
        let testStorage425: 'test425 = &mut testArena425 in {
5823 +
            let mut a = testResolver(testStorage425);
5824 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;");
5825 +
            let err = try expectError(&result);
5826 +
            try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5827 +
        }
4530 5828
    } {
4531 -
        let mut a = testResolver();
4532 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;");
4533 -
        let err = try expectError(&result);
4534 -
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5829 +
        let mut testArena426 = testArena();
5830 +
        let testStorage426: 'test426 = &mut testArena426 in {
5831 +
            let mut a = testResolver(testStorage426);
5832 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;");
5833 +
            let err = try expectError(&result);
5834 +
            try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5835 +
        }
4535 5836
    } {
4536 -
        let mut a = testResolver();
4537 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;");
4538 -
        let err = try expectError(&result);
4539 -
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5837 +
        let mut testArena427 = testArena();
5838 +
        let testStorage427: 'test427 = &mut testArena427 in {
5839 +
            let mut a = testResolver(testStorage427);
5840 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;");
5841 +
            let err = try expectError(&result);
5842 +
            try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
5843 +
        }
4540 5844
    } {
4541 -
        let mut a = testResolver();
4542 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;");
4543 -
        try expectNoErrors(&result);
5845 +
        let mut testArena428 = testArena();
5846 +
        let testStorage428: 'test428 = &mut testArena428 in {
5847 +
            let mut a = testResolver(testStorage428);
5848 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;");
5849 +
            try expectNoErrors(&result);
5850 +
        }
4544 5851
    } {
4545 -
        let mut a = testResolver();
4546 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;");
4547 -
        try expectNoErrors(&result);
5852 +
        let mut testArena429 = testArena();
5853 +
        let testStorage429: 'test429 = &mut testArena429 in {
5854 +
            let mut a = testResolver(testStorage429);
5855 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;");
5856 +
            try expectNoErrors(&result);
5857 +
        }
4548 5858
    } {
4549 -
        let mut a = testResolver();
4550 -
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;");
4551 -
        try expectNoErrors(&result);
5859 +
        let mut testArena430 = testArena();
5860 +
        let testStorage430: 'test430 = &mut testArena430 in {
5861 +
            let mut a = testResolver(testStorage430);
5862 +
            let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;");
5863 +
            try expectNoErrors(&result);
5864 +
        }
4552 5865
    }
4553 5866
}
4554 5867
4555 5868
/// A mutable slice pointer should be assignable to an immutable slice pointer.
4556 5869
@test unsafe fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) {
4557 -
    let mut a = testResolver();
4558 -
    let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;");
4559 -
    try expectNoErrors(&result);
5870 +
    let mut testArena431 = testArena();
5871 +
    let testStorage431: 'test431 = &mut testArena431 in {
5872 +
        let mut a = testResolver(testStorage431);
5873 +
        let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;");
5874 +
        try expectNoErrors(&result);
5875 +
    }
4560 5876
}
4561 5877
4562 5878
/// Comprehensive tests for `as` cast expressions.
4563 5879
@test unsafe fn testResolveAsCasts() throws (testing::TestError) {
4564 5880
    { // Pointer to numeric.
4565 -
        let mut a = testResolver();
4566 -
        let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;");
4567 -
        try expectNoErrors(&result);
5881 +
        let mut testArena432 = testArena();
5882 +
        let testStorage432: 'test432 = &mut testArena432 in {
5883 +
            let mut a = testResolver(testStorage432);
5884 +
            let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;");
5885 +
            try expectNoErrors(&result);
5886 +
        }
4568 5887
    } { // Function pointer to numeric.
4569 -
        let mut a = testResolver();
4570 -
        let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }");
4571 -
        try expectNoErrors(&result);
5888 +
        let mut testArena433 = testArena();
5889 +
        let testStorage433: 'test433 = &mut testArena433 in {
5890 +
            let mut a = testResolver(testStorage433);
5891 +
            let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }");
5892 +
            try expectNoErrors(&result);
5893 +
        }
4572 5894
    } { // *u8 to *i32 (u8 to i32 is valid).
4573 -
        let mut a = testResolver();
4574 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }");
4575 -
        try expectNoErrors(&result);
5895 +
        let mut testArena434 = testArena();
5896 +
        let testStorage434: 'test434 = &mut testArena434 in {
5897 +
            let mut a = testResolver(testStorage434);
5898 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }");
5899 +
            try expectNoErrors(&result);
5900 +
        }
4576 5901
    } { // **u8 to **i32 (*u8 to *i32 is valid).
4577 -
        let mut a = testResolver();
4578 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }");
4579 -
        try expectNoErrors(&result);
5902 +
        let mut testArena435 = testArena();
5903 +
        let testStorage435: 'test435 = &mut testArena435 in {
5904 +
            let mut a = testResolver(testStorage435);
5905 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }");
5906 +
            try expectNoErrors(&result);
5907 +
        }
4580 5908
    }
4581 5909
4582 5910
    { // *[i32] to *[opaque].
4583 -
        let mut a = testResolver();
4584 -
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }");
4585 -
        try expectNoErrors(&result);
5911 +
        let mut testArena436 = testArena();
5912 +
        let testStorage436: 'test436 = &mut testArena436 in {
5913 +
            let mut a = testResolver(testStorage436);
5914 +
            let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }");
5915 +
            try expectNoErrors(&result);
5916 +
        }
4586 5917
    } { // *[opaque] to *[i32].
4587 -
        let mut a = testResolver();
4588 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }");
4589 -
        try expectNoErrors(&result);
5918 +
        let mut testArena437 = testArena();
5919 +
        let testStorage437: 'test437 = &mut testArena437 in {
5920 +
            let mut a = testResolver(testStorage437);
5921 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }");
5922 +
            try expectNoErrors(&result);
5923 +
        }
4590 5924
    }
4591 5925
4592 5926
    { // *[i32] to *[u8].
4593 -
        let mut a = testResolver();
4594 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }");
4595 -
        try expectNoErrors(&result);
5927 +
        let mut testArena438 = testArena();
5928 +
        let testStorage438: 'test438 = &mut testArena438 in {
5929 +
            let mut a = testResolver(testStorage438);
5930 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }");
5931 +
            try expectNoErrors(&result);
5932 +
        }
4596 5933
    } { // *[record] to *[u8].
4597 -
        let mut a = testResolver();
4598 -
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }");
4599 -
        try expectNoErrors(&result);
5934 +
        let mut testArena439 = testArena();
5935 +
        let testStorage439: 'test439 = &mut testArena439 in {
5936 +
            let mut a = testResolver(testStorage439);
5937 +
            let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }");
5938 +
            try expectNoErrors(&result);
5939 +
        }
4600 5940
    }
4601 5941
4602 5942
    { // *[u8] to *[i32].
4603 -
        let mut a = testResolver();
4604 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }");
4605 -
        try expectNoErrors(&result);
5943 +
        let mut testArena440 = testArena();
5944 +
        let testStorage440: 'test440 = &mut testArena440 in {
5945 +
            let mut a = testResolver(testStorage440);
5946 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }");
5947 +
            try expectNoErrors(&result);
5948 +
        }
4606 5949
    } { // *[*u8] to *[*i32]
4607 -
        let mut a = testResolver();
4608 -
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }");
4609 -
        try expectNoErrors(&result);
5950 +
        let mut testArena441 = testArena();
5951 +
        let testStorage441: 'test441 = &mut testArena441 in {
5952 +
            let mut a = testResolver(testStorage441);
5953 +
            let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }");
5954 +
            try expectNoErrors(&result);
5955 +
        }
4610 5956
    }
4611 5957
4612 5958
    { // Identity cast: *mut [i32] to *mut [i32].
4613 -
        let mut a = testResolver();
4614 -
        let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }");
4615 -
        try expectNoErrors(&result);
5959 +
        let mut testArena442 = testArena();
5960 +
        let testStorage442: 'test442 = &mut testArena442 in {
5961 +
            let mut a = testResolver(testStorage442);
5962 +
            let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }");
5963 +
            try expectNoErrors(&result);
5964 +
        }
4616 5965
    } { // Identity cast: *i32 to *i32.
4617 -
        let mut a = testResolver();
4618 -
        let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }");
4619 -
        try expectNoErrors(&result);
5966 +
        let mut testArena443 = testArena();
5967 +
        let testStorage443: 'test443 = &mut testArena443 in {
5968 +
            let mut a = testResolver(testStorage443);
5969 +
            let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }");
5970 +
            try expectNoErrors(&result);
5971 +
        }
4620 5972
    } { // Identity cast: i32 to i32.
4621 -
        let mut a = testResolver();
4622 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;");
4623 -
        try expectNoErrors(&result);
5973 +
        let mut testArena444 = testArena();
5974 +
        let testStorage444: 'test444 = &mut testArena444 in {
5975 +
            let mut a = testResolver(testStorage444);
5976 +
            let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;");
5977 +
            try expectNoErrors(&result);
5978 +
        }
4624 5979
    }
4625 5980
}
4626 5981
4627 5982
/// Tests for invalid `as` casts that should be rejected.
4628 5983
@test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) {
4629 5984
    { // Pointer to slice is invalid.
4630 -
        let mut a = testResolver();
4631 -
        let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }");
4632 -
        let err = try expectError(&result);
4633 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4634 -
            else throw testing::TestError::Failed;
5985 +
        let mut testArena445 = testArena();
5986 +
        let testStorage445: 'test445 = &mut testArena445 in {
5987 +
            let mut a = testResolver(testStorage445);
5988 +
            let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }");
5989 +
            let err = try expectError(&result);
5990 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
5991 +
                else throw testing::TestError::Failed;
5992 +
        }
4635 5993
    } { // Slice to pointer is invalid.
4636 -
        let mut a = testResolver();
4637 -
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }");
4638 -
        let err = try expectError(&result);
4639 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4640 -
            else throw testing::TestError::Failed;
5994 +
        let mut testArena446 = testArena();
5995 +
        let testStorage446: 'test446 = &mut testArena446 in {
5996 +
            let mut a = testResolver(testStorage446);
5997 +
            let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }");
5998 +
            let err = try expectError(&result);
5999 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
6000 +
                else throw testing::TestError::Failed;
6001 +
        }
4641 6002
    } { // *T to *i32 is invalid.
4642 -
        let mut a = testResolver();
4643 -
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }");
4644 -
        let err = try expectError(&result);
4645 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4646 -
            else throw testing::TestError::Failed;
6003 +
        let mut testArena447 = testArena();
6004 +
        let testStorage447: 'test447 = &mut testArena447 in {
6005 +
            let mut a = testResolver(testStorage447);
6006 +
            let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }");
6007 +
            let err = try expectError(&result);
6008 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
6009 +
                else throw testing::TestError::Failed;
6010 +
        }
4647 6011
    } { // *[T] to *[i32] is invalid.
4648 -
        let mut a = testResolver();
4649 -
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }");
4650 -
        let err = try expectError(&result);
4651 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4652 -
            else throw testing::TestError::Failed;
6012 +
        let mut testArena448 = testArena();
6013 +
        let testStorage448: 'test448 = &mut testArena448 in {
6014 +
            let mut a = testResolver(testStorage448);
6015 +
            let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }");
6016 +
            let err = try expectError(&result);
6017 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
6018 +
                else throw testing::TestError::Failed;
6019 +
        }
4653 6020
    } { // Slice to numeric is invalid.
4654 -
        let mut a = testResolver();
4655 -
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }");
4656 -
        let err = try expectError(&result);
4657 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4658 -
            else throw testing::TestError::Failed;
6021 +
        let mut testArena449 = testArena();
6022 +
        let testStorage449: 'test449 = &mut testArena449 in {
6023 +
            let mut a = testResolver(testStorage449);
6024 +
            let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }");
6025 +
            let err = try expectError(&result);
6026 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
6027 +
                else throw testing::TestError::Failed;
6028 +
        }
4659 6029
    } { // *record to *u8 is invalid.
4660 -
        let mut a = testResolver();
4661 -
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }");
4662 -
        let err = try expectError(&result);
4663 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4664 -
            else throw testing::TestError::Failed;
6030 +
        let mut testArena450 = testArena();
6031 +
        let testStorage450: 'test450 = &mut testArena450 in {
6032 +
            let mut a = testResolver(testStorage450);
6033 +
            let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }");
6034 +
            let err = try expectError(&result);
6035 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
6036 +
                else throw testing::TestError::Failed;
6037 +
        }
4665 6038
    }
4666 6039
}
4667 6040
4668 6041
/// Test that catch binding is available in catch block scope.
4669 6042
@test unsafe fn testResolveTryCatchBinding() throws (testing::TestError) {
4670 6043
    {
4671 -
        let mut a = testResolver();
4672 -
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }";
4673 -
        let result = try resolveProgramStr(&mut a, program);
4674 -
        try expectNoErrors(&result);
6044 +
        let mut testArena451 = testArena();
6045 +
        let testStorage451: 'test451 = &mut testArena451 in {
6046 +
            let mut a = testResolver(testStorage451);
6047 +
            let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }";
6048 +
            let result = try resolveProgramStr(&mut a, program);
6049 +
            try expectNoErrors(&result);
6050 +
        }
4675 6051
    } {
4676 -
        let mut a = testResolver();
4677 -
        let program = "union Error { A, B } fn fallible() -> u32 throws (Error) { throw Error::A; } fn caller() -> u32 { return try fallible() catch e { if e == Error::A { return 1; } else { return 2; } }; }";
4678 -
        let result = try resolveProgramStr(&mut a, program);
4679 -
        try expectNoErrors(&result);
6052 +
        let mut testArena452 = testArena();
6053 +
        let testStorage452: 'test452 = &mut testArena452 in {
6054 +
            let mut a = testResolver(testStorage452);
6055 +
            let program = "union Error { A, B } fn fallible() -> u32 throws (Error) { throw Error::A; } fn caller() -> u32 { return try fallible() catch e { if e == Error::A { return 1; } else { return 2; } }; }";
6056 +
            let result = try resolveProgramStr(&mut a, program);
6057 +
            try expectNoErrors(&result);
6058 +
        }
4680 6059
    } {
4681 -
        let mut a = testResolver();
4682 -
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { if err == Error::Fail { return 1; } return 0; }; }";
4683 -
        let result = try resolveProgramStr(&mut a, program);
4684 -
        try expectNoErrors(&result);
6060 +
        let mut testArena453 = testArena();
6061 +
        let testStorage453: 'test453 = &mut testArena453 in {
6062 +
            let mut a = testResolver(testStorage453);
6063 +
            let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { if err == Error::Fail { return 1; } return 0; }; }";
6064 +
            let result = try resolveProgramStr(&mut a, program);
6065 +
            try expectNoErrors(&result);
6066 +
        }
4685 6067
    } {
4686 -
        let mut a = testResolver();
4687 -
        let program = "union Error { Fail(u32) } fn fallible() -> u32 throws (Error) { throw Error::Fail(42); } fn caller() -> u32 { return try fallible() catch err { match err { case Error::Fail(x) => return x, } }; }";
4688 -
        let result = try resolveProgramStr(&mut a, program);
4689 -
        try expectNoErrors(&result);
6068 +
        let mut testArena454 = testArena();
6069 +
        let testStorage454: 'test454 = &mut testArena454 in {
6070 +
            let mut a = testResolver(testStorage454);
6071 +
            let program = "union Error { Fail(u32) } fn fallible() -> u32 throws (Error) { throw Error::Fail(42); } fn caller() -> u32 { return try fallible() catch err { match err { case Error::Fail(x) => return x, } }; }";
6072 +
            let result = try resolveProgramStr(&mut a, program);
6073 +
            try expectNoErrors(&result);
6074 +
        }
4690 6075
    }
4691 6076
}
4692 6077
4693 6078
/// Test that duplicate union variant patterns are detected.
4694 6079
@test unsafe fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) {
4695 6080
    {
4696 -
        let mut a = testResolver();
4697 -
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }";
4698 -
        let result = try resolveProgramStr(&mut a, program);
4699 -
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6081 +
        let mut testArena455 = testArena();
6082 +
        let testStorage455: 'test455 = &mut testArena455 in {
6083 +
            let mut a = testResolver(testStorage455);
6084 +
            let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }";
6085 +
            let result = try resolveProgramStr(&mut a, program);
6086 +
            try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6087 +
        }
4700 6088
    } {
4701 6089
        // No duplicate: distinct variants are fine.
4702 -
        let mut a = testResolver();
4703 -
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }";
4704 -
        let result = try resolveProgramStr(&mut a, program);
4705 -
        try expectNoErrors(&result);
6090 +
        let mut testArena456 = testArena();
6091 +
        let testStorage456: 'test456 = &mut testArena456 in {
6092 +
            let mut a = testResolver(testStorage456);
6093 +
            let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }";
6094 +
            let result = try resolveProgramStr(&mut a, program);
6095 +
            try expectNoErrors(&result);
6096 +
        }
4706 6097
    }
4707 6098
}
4708 6099
4709 6100
/// Test that duplicate bool patterns are detected.
4710 6101
@test unsafe fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) {
4711 6102
    {
4712 -
        let mut a = testResolver();
4713 -
        let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }";
4714 -
        let result = try resolveProgramStr(&mut a, program);
4715 -
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6103 +
        let mut testArena457 = testArena();
6104 +
        let testStorage457: 'test457 = &mut testArena457 in {
6105 +
            let mut a = testResolver(testStorage457);
6106 +
            let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }";
6107 +
            let result = try resolveProgramStr(&mut a, program);
6108 +
            try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6109 +
        }
4716 6110
    } {
4717 -
        let mut a = testResolver();
4718 -
        let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }";
4719 -
        let result = try resolveProgramStr(&mut a, program);
4720 -
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6111 +
        let mut testArena458 = testArena();
6112 +
        let testStorage458: 'test458 = &mut testArena458 in {
6113 +
            let mut a = testResolver(testStorage458);
6114 +
            let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }";
6115 +
            let result = try resolveProgramStr(&mut a, program);
6116 +
            try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6117 +
        }
4721 6118
    }
4722 6119
}
4723 6120
4724 6121
/// Test that duplicate nil patterns in optional match are detected.
4725 6122
@test unsafe fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) {
4726 6123
    {
4727 -
        let mut a = testResolver();
4728 -
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }";
4729 -
        let result = try resolveProgramStr(&mut a, program);
4730 -
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6124 +
        let mut testArena459 = testArena();
6125 +
        let testStorage459: 'test459 = &mut testArena459 in {
6126 +
            let mut a = testResolver(testStorage459);
6127 +
            let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }";
6128 +
            let result = try resolveProgramStr(&mut a, program);
6129 +
            try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6130 +
        }
4731 6131
    } {
4732 6132
        // Duplicate value binding.
4733 -
        let mut a = testResolver();
4734 -
        let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }";
4735 -
        let result = try resolveProgramStr(&mut a, program);
4736 -
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6133 +
        let mut testArena460 = testArena();
6134 +
        let testStorage460: 'test460 = &mut testArena460 in {
6135 +
            let mut a = testResolver(testStorage460);
6136 +
            let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }";
6137 +
            let result = try resolveProgramStr(&mut a, program);
6138 +
            try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
6139 +
        }
4737 6140
    }
4738 6141
}
4739 6142
4740 6143
/// Test that guarded match arms are not considered duplicates.
4741 6144
@test unsafe fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) {
4742 6145
    {
4743 6146
        // Guarded union variant followed by same variant is fine.
4744 -
        let mut a = testResolver();
4745 -
        let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }";
4746 -
        let result = try resolveProgramStr(&mut a, program);
4747 -
        try expectNoErrors(&result);
6147 +
        let mut testArena461 = testArena();
6148 +
        let testStorage461: 'test461 = &mut testArena461 in {
6149 +
            let mut a = testResolver(testStorage461);
6150 +
            let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }";
6151 +
            let result = try resolveProgramStr(&mut a, program);
6152 +
            try expectNoErrors(&result);
6153 +
        }
4748 6154
    } {
4749 6155
        // Guarded bool pattern followed by same bool is fine.
4750 -
        let mut a = testResolver();
4751 -
        let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }";
4752 -
        let result = try resolveProgramStr(&mut a, program);
4753 -
        try expectNoErrors(&result);
6156 +
        let mut testArena462 = testArena();
6157 +
        let testStorage462: 'test462 = &mut testArena462 in {
6158 +
            let mut a = testResolver(testStorage462);
6159 +
            let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }";
6160 +
            let result = try resolveProgramStr(&mut a, program);
6161 +
            try expectNoErrors(&result);
6162 +
        }
4754 6163
    } {
4755 6164
        // Guarded nil pattern followed by nil is fine.
4756 -
        let mut a = testResolver();
4757 -
        let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }";
4758 -
        let result = try resolveProgramStr(&mut a, program);
4759 -
        try expectNoErrors(&result);
6165 +
        let mut testArena463 = testArena();
6166 +
        let testStorage463: 'test463 = &mut testArena463 in {
6167 +
            let mut a = testResolver(testStorage463);
6168 +
            let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }";
6169 +
            let result = try resolveProgramStr(&mut a, program);
6170 +
            try expectNoErrors(&result);
6171 +
        }
4760 6172
    } {
4761 6173
        // Guarded value binding followed by another binding is fine.
4762 -
        let mut a = testResolver();
4763 -
        let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }";
4764 -
        let result = try resolveProgramStr(&mut a, program);
4765 -
        try expectNoErrors(&result);
6174 +
        let mut testArena464 = testArena();
6175 +
        let testStorage464: 'test464 = &mut testArena464 in {
6176 +
            let mut a = testResolver(testStorage464);
6177 +
            let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }";
6178 +
            let result = try resolveProgramStr(&mut a, program);
6179 +
            try expectNoErrors(&result);
6180 +
        }
4766 6181
    }
4767 6182
}
4768 6183
4769 6184
/// Test that unreachable else is detected when all union variants are covered.
4770 6185
@test unsafe fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) {
4771 6186
    {
4772 -
        let mut a = testResolver();
4773 -
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }";
4774 -
        let result = try resolveProgramStr(&mut a, program);
4775 -
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6187 +
        let mut testArena465 = testArena();
6188 +
        let testStorage465: 'test465 = &mut testArena465 in {
6189 +
            let mut a = testResolver(testStorage465);
6190 +
            let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }";
6191 +
            let result = try resolveProgramStr(&mut a, program);
6192 +
            try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6193 +
        }
4776 6194
    } {
4777 6195
        // Partial coverage with else is fine.
4778 -
        let mut a = testResolver();
4779 -
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }";
4780 -
        let result = try resolveProgramStr(&mut a, program);
4781 -
        try expectNoErrors(&result);
6196 +
        let mut testArena466 = testArena();
6197 +
        let testStorage466: 'test466 = &mut testArena466 in {
6198 +
            let mut a = testResolver(testStorage466);
6199 +
            let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }";
6200 +
            let result = try resolveProgramStr(&mut a, program);
6201 +
            try expectNoErrors(&result);
6202 +
        }
4782 6203
    }
4783 6204
}
4784 6205
4785 6206
/// Test that unreachable else is detected when both bool cases are covered.
4786 6207
@test unsafe fn testResolveMatchUnreachableElseBool() throws (testing::TestError) {
4787 6208
    {
4788 -
        let mut a = testResolver();
4789 -
        let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }";
4790 -
        let result = try resolveProgramStr(&mut a, program);
4791 -
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6209 +
        let mut testArena467 = testArena();
6210 +
        let testStorage467: 'test467 = &mut testArena467 in {
6211 +
            let mut a = testResolver(testStorage467);
6212 +
            let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }";
6213 +
            let result = try resolveProgramStr(&mut a, program);
6214 +
            try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6215 +
        }
4792 6216
    } {
4793 6217
        // Only one case with else is fine.
4794 -
        let mut a = testResolver();
4795 -
        let program = "fn f(x: bool) { match x { case true => {}, else => {} } }";
4796 -
        let result = try resolveProgramStr(&mut a, program);
4797 -
        try expectNoErrors(&result);
6218 +
        let mut testArena468 = testArena();
6219 +
        let testStorage468: 'test468 = &mut testArena468 in {
6220 +
            let mut a = testResolver(testStorage468);
6221 +
            let program = "fn f(x: bool) { match x { case true => {}, else => {} } }";
6222 +
            let result = try resolveProgramStr(&mut a, program);
6223 +
            try expectNoErrors(&result);
6224 +
        }
4798 6225
    }
4799 6226
}
4800 6227
4801 6228
/// Test that unreachable else is detected when both optional cases are covered.
4802 6229
@test unsafe fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) {
4803 6230
    {
4804 -
        let mut a = testResolver();
4805 -
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }";
4806 -
        let result = try resolveProgramStr(&mut a, program);
4807 -
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6231 +
        let mut testArena469 = testArena();
6232 +
        let testStorage469: 'test469 = &mut testArena469 in {
6233 +
            let mut a = testResolver(testStorage469);
6234 +
            let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }";
6235 +
            let result = try resolveProgramStr(&mut a, program);
6236 +
            try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
6237 +
        }
4808 6238
    } {
4809 6239
        // Only value binding with else is fine.
4810 -
        let mut a = testResolver();
4811 -
        let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }";
4812 -
        let result = try resolveProgramStr(&mut a, program);
4813 -
        try expectNoErrors(&result);
6240 +
        let mut testArena470 = testArena();
6241 +
        let testStorage470: 'test470 = &mut testArena470 in {
6242 +
            let mut a = testResolver(testStorage470);
6243 +
            let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }";
6244 +
            let result = try resolveProgramStr(&mut a, program);
6245 +
            try expectNoErrors(&result);
6246 +
        }
4814 6247
    }
4815 6248
}
4816 6249
4817 6250
// --- Multi-error typed catch tests ---
4818 6251
4819 6252
@test unsafe fn testTypedCatchExhaustive() throws (testing::TestError) {
4820 -
    let mut a = testResolver();
4821 -
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }";
4822 -
    let result = try resolveProgramStr(&mut a, program);
4823 -
    try expectNoErrors(&result);
6253 +
    let mut testArena471 = testArena();
6254 +
    let testStorage471: 'test471 = &mut testArena471 in {
6255 +
        let mut a = testResolver(testStorage471);
6256 +
        let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }";
6257 +
        let result = try resolveProgramStr(&mut a, program);
6258 +
        try expectNoErrors(&result);
6259 +
    }
4824 6260
}
4825 6261
4826 6262
@test unsafe fn testTypedCatchNonExhaustive() throws (testing::TestError) {
4827 -
    let mut a = testResolver();
4828 -
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }";
4829 -
    let result = try resolveProgramStr(&mut a, program);
4830 -
    try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
6263 +
    let mut testArena472 = testArena();
6264 +
    let testStorage472: 'test472 = &mut testArena472 in {
6265 +
        let mut a = testResolver(testStorage472);
6266 +
        let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }";
6267 +
        let result = try resolveProgramStr(&mut a, program);
6268 +
        try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
6269 +
    }
4831 6270
}
4832 6271
4833 6272
@test unsafe fn testTypedCatchDuplicate() throws (testing::TestError) {
4834 -
    let mut a = testResolver();
4835 -
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }";
4836 -
    let result = try resolveProgramStr(&mut a, program);
4837 -
    try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType);
6273 +
    let mut testArena473 = testArena();
6274 +
    let testStorage473: 'test473 = &mut testArena473 in {
6275 +
        let mut a = testResolver(testStorage473);
6276 +
        let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }";
6277 +
        let result = try resolveProgramStr(&mut a, program);
6278 +
        try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType);
6279 +
    }
4838 6280
}
4839 6281
4840 6282
@test unsafe fn testTypedCatchWithCatchAll() throws (testing::TestError) {
4841 -
    let mut a = testResolver();
4842 -
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }";
4843 -
    let result = try resolveProgramStr(&mut a, program);
4844 -
    try expectNoErrors(&result);
6283 +
    let mut testArena474 = testArena();
6284 +
    let testStorage474: 'test474 = &mut testArena474 in {
6285 +
        let mut a = testResolver(testStorage474);
6286 +
        let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }";
6287 +
        let result = try resolveProgramStr(&mut a, program);
6288 +
        try expectNoErrors(&result);
6289 +
    }
4845 6290
}
4846 6291
4847 6292
@test unsafe fn testTypedCatchWrongType() throws (testing::TestError) {
4848 -
    let mut a = testResolver();
4849 -
    let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }";
4850 -
    let result = try resolveProgramStr(&mut a, program);
4851 -
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
6293 +
    let mut testArena475 = testArena();
6294 +
    let testStorage475: 'test475 = &mut testArena475 in {
6295 +
        let mut a = testResolver(testStorage475);
6296 +
        let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }";
6297 +
        let result = try resolveProgramStr(&mut a, program);
6298 +
        try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
6299 +
    }
4852 6300
}
4853 6301
4854 6302
@test unsafe fn testInferredCatchMultiError() throws (testing::TestError) {
4855 -
    let mut a = testResolver();
4856 -
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }";
4857 -
    let result = try resolveProgramStr(&mut a, program);
4858 -
    try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError);
6303 +
    let mut testArena476 = testArena();
6304 +
    let testStorage476: 'test476 = &mut testArena476 in {
6305 +
        let mut a = testResolver(testStorage476);
6306 +
        let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }";
6307 +
        let result = try resolveProgramStr(&mut a, program);
6308 +
        try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError);
6309 +
    }
4859 6310
}
4860 6311
4861 6312
@test unsafe fn testResolveInstanceMissingMethod() throws (testing::TestError) {
4862 -
    let mut a = testResolver();
4863 -
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}";
4864 -
    let result = try resolveProgramStr(&mut a, program);
4865 -
    try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f"));
6313 +
    let mut testArena477 = testArena();
6314 +
    let testStorage477: 'test477 = &mut testArena477 in {
6315 +
        let mut a = testResolver(testStorage477);
6316 +
        let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}";
6317 +
        let result = try resolveProgramStr(&mut a, program);
6318 +
        try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f"));
6319 +
    }
4866 6320
}
4867 6321
4868 6322
@test unsafe fn testResolveInstanceUnknownMethod() throws (testing::TestError) {
4869 -
    let mut a = testResolver();
4870 -
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }";
4871 -
    let result = try resolveProgramStr(&mut a, program);
4872 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
6323 +
    let mut testArena478 = testArena();
6324 +
    let testStorage478: 'test478 = &mut testArena478 in {
6325 +
        let mut a = testResolver(testStorage478);
6326 +
        let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }";
6327 +
        let result = try resolveProgramStr(&mut a, program);
6328 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
6329 +
    }
4873 6330
}
4874 6331
4875 6332
@test unsafe fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) {
4876 -
    let mut a = testResolver();
4877 -
    let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }";
4878 -
    let result = try resolveProgramStr(&mut a, program);
4879 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add"));
6333 +
    let mut testArena479 = testArena();
6334 +
    let testStorage479: 'test479 = &mut testArena479 in {
6335 +
        let mut a = testResolver(testStorage479);
6336 +
        let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }";
6337 +
        let result = try resolveProgramStr(&mut a, program);
6338 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add"));
6339 +
    }
4880 6340
}
4881 6341
4882 6342
@test unsafe fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) {
4883 -
    let mut a = testResolver();
4884 -
    let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }";
4885 -
    let result = try resolveProgramStr(&mut a, program);
4886 -
    let err = try expectError(&result);
4887 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4888 -
        else throw testing::TestError::Failed;
6343 +
    let mut testArena480 = testArena();
6344 +
    let testStorage480: 'test480 = &mut testArena480 in {
6345 +
        let mut a = testResolver(testStorage480);
6346 +
        let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }";
6347 +
        let result = try resolveProgramStr(&mut a, program);
6348 +
        let err = try expectError(&result);
6349 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
6350 +
            else throw testing::TestError::Failed;
6351 +
    }
4889 6352
}
4890 6353
4891 6354
@test unsafe fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) {
4892 -
    let mut a = testResolver();
4893 -
    let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }";
4894 -
    let result = try resolveProgramStr(&mut a, program);
4895 -
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
6355 +
    let mut testArena481 = testArena();
6356 +
    let testStorage481: 'test481 = &mut testArena481 in {
6357 +
        let mut a = testResolver(testStorage481);
6358 +
        let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }";
6359 +
        let result = try resolveProgramStr(&mut a, program);
6360 +
        try expectErrorKind(&result, super::ErrorKind::MissingTry);
6361 +
    }
4896 6362
}
4897 6363
4898 6364
/// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type).
4899 6365
/// The instance method could mutate through what was originally an immutable pointer.
4900 6366
@test unsafe fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) {
4901 -
    let mut a = testResolver();
4902 -
    let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }";
4903 -
    let result = try resolveProgramStr(&mut a, program);
4904 -
    // Should reject: instance declares *mut receiver but trait only requires immutable.
4905 -
    try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch);
6367 +
    let mut testArena482 = testArena();
6368 +
    let testStorage482: 'test482 = &mut testArena482 in {
6369 +
        let mut a = testResolver(testStorage482);
6370 +
        let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }";
6371 +
        let result = try resolveProgramStr(&mut a, program);
6372 +
        // Should reject: instance declares *mut receiver but trait only requires immutable.
6373 +
        try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch);
6374 +
    }
4906 6375
}
4907 6376
4908 6377
/// Instance method declares different parameter types than the trait.
4909 6378
/// The resolver should reject the mismatch rather than silently using the trait's types.
4910 6379
@test unsafe fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) {
4911 -
    let mut a = testResolver();
4912 -
    let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }";
4913 -
    let result = try resolveProgramStr(&mut a, program);
4914 -
    // Should reject: instance param type u8 doesn't match trait param type i32.
4915 -
    let err = try expectError(&result);
4916 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4917 -
        else throw testing::TestError::Failed;
6380 +
    let mut testArena483 = testArena();
6381 +
    let testStorage483: 'test483 = &mut testArena483 in {
6382 +
        let mut a = testResolver(testStorage483);
6383 +
        let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }";
6384 +
        let result = try resolveProgramStr(&mut a, program);
6385 +
        // Should reject: instance param type u8 doesn't match trait param type i32.
6386 +
        let err = try expectError(&result);
6387 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
6388 +
            else throw testing::TestError::Failed;
6389 +
    }
4918 6390
}
4919 6391
4920 6392
/// Duplicate instance declarations for the same (trait, type) pair should be rejected.
4921 6393
@test unsafe fn testResolveInstanceDuplicateRejected() throws (testing::TestError) {
4922 -
    let mut a = testResolver();
4923 -
    let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }";
4924 -
    let result = try resolveProgramStr(&mut a, program);
4925 -
    // Should reject: duplicate instance for (Adder, Counter).
4926 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateInstance);
6394 +
    let mut testArena484 = testArena();
6395 +
    let testStorage484: 'test484 = &mut testArena484 in {
6396 +
        let mut a = testResolver(testStorage484);
6397 +
        let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }";
6398 +
        let result = try resolveProgramStr(&mut a, program);
6399 +
        // Should reject: duplicate instance for (Adder, Counter).
6400 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateInstance);
6401 +
    }
4927 6402
}
4928 6403
4929 6404
/// Trait method receiver must point to the declaring trait type.
4930 6405
@test unsafe fn testResolveTraitReceiverMismatch() throws (testing::TestError) {
4931 -
    let mut a = testResolver();
4932 -
    let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }";
4933 -
    let result = try resolveProgramStr(&mut a, program);
4934 -
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
6406 +
    let mut testArena485 = testArena();
6407 +
    let testStorage485: 'test485 = &mut testArena485 in {
6408 +
        let mut a = testResolver(testStorage485);
6409 +
        let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }";
6410 +
        let result = try resolveProgramStr(&mut a, program);
6411 +
        try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
6412 +
    }
4935 6413
}
4936 6414
4937 6415
/// Using a trait name as a value expression should be rejected.
4938 6416
@test unsafe fn testResolveTraitNameAsValueRejected() throws (testing::TestError) {
4939 -
    let mut a = testResolver();
4940 -
    let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }";
4941 -
    let result = try resolveProgramStr(&mut a, program);
4942 -
    try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName);
6417 +
    let mut testArena486 = testArena();
6418 +
    let testStorage486: 'test486 = &mut testArena486 in {
6419 +
        let mut a = testResolver(testStorage486);
6420 +
        let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }";
6421 +
        let result = try resolveProgramStr(&mut a, program);
6422 +
        try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName);
6423 +
    }
4943 6424
}
4944 6425
4945 6426
/// Cross-module trait: coerce to trait object and dispatch from a different module.
4946 6427
@test unsafe fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) {
4947 -
    let mut a = testResolver();
4948 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
6428 +
    let mut testArena487 = testArena();
6429 +
    let testStorage487: 'test487 = &mut testArena487 in {
6430 +
        let mut a = testResolver(testStorage487);
6431 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4949 6432
4950 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena);
4951 -
    let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
4952 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
6433 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena);
6434 +
        let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
6435 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4953 6436
4954 -
    let result = try resolveModuleTree(&mut a, rootId);
4955 -
    try expectNoErrors(&result);
6437 +
        let result = try resolveModuleTree(&mut a, rootId);
6438 +
        try expectNoErrors(&result);
6439 +
    }
4956 6440
}
4957 6441
4958 6442
/// Instance in a different module from trait and type.
4959 6443
@test unsafe fn testResolveInstanceCrossModule() throws (testing::TestError) {
4960 -
    let mut a = testResolver();
4961 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
6444 +
    let mut testArena488 = testArena();
6445 +
    let testStorage488: 'test488 = &mut testArena488 in {
6446 +
        let mut a = testResolver(testStorage488);
6447 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4962 6448
4963 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena);
4964 -
    let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena);
4965 -
    let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
4966 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
6449 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena);
6450 +
        let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena);
6451 +
        let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
6452 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4967 6453
4968 -
    let result = try resolveModuleTree(&mut a, rootId);
4969 -
    try expectNoErrors(&result);
6454 +
        let result = try resolveModuleTree(&mut a, rootId);
6455 +
        try expectNoErrors(&result);
6456 +
    }
4970 6457
}
4971 6458
4972 6459
/// Calling a mutable-receiver trait method on an immutable trait object
4973 6460
/// must be rejected.
4974 6461
@test unsafe fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) {
4975 -
    let mut a = testResolver();
4976 -
    let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }";
4977 -
    let result = try resolveProgramStr(&mut a, program);
4978 -
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
6462 +
    let mut testArena489 = testArena();
6463 +
    let testStorage489: 'test489 = &mut testArena489 in {
6464 +
        let mut a = testResolver(testStorage489);
6465 +
        let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }";
6466 +
        let result = try resolveProgramStr(&mut a, program);
6467 +
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
6468 +
    }
4979 6469
}
4980 6470
4981 6471
/// Immutable methods on an immutable trait object should be accepted.
4982 6472
@test unsafe fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) {
4983 -
    let mut a = testResolver();
4984 -
    let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }";
4985 -
    let result = try resolveProgramStr(&mut a, program);
4986 -
    try expectNoErrors(&result);
6473 +
    let mut testArena490 = testArena();
6474 +
    let testStorage490: 'test490 = &mut testArena490 in {
6475 +
        let mut a = testResolver(testStorage490);
6476 +
        let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }";
6477 +
        let result = try resolveProgramStr(&mut a, program);
6478 +
        try expectNoErrors(&result);
6479 +
    }
4987 6480
}
4988 6481
4989 6482
/// Both mutable and immutable methods on a mutable trait object should work.
4990 6483
@test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) {
4991 -
    let mut a = testResolver();
4992 -
    let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }";
4993 -
    let result = try resolveProgramStr(&mut a, program);
4994 -
    try expectNoErrors(&result);
6484 +
    let mut testArena491 = testArena();
6485 +
    let testStorage491: 'test491 = &mut testArena491 in {
6486 +
        let mut a = testResolver(testStorage491);
6487 +
        let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }";
6488 +
        let result = try resolveProgramStr(&mut a, program);
6489 +
        try expectNoErrors(&result);
6490 +
    }
4995 6491
}
4996 6492
4997 6493
/// Instance method body type must match the trait return type.
4998 6494
/// The trait declares `-> i32` but the body returns `bool`.
4999 6495
@test unsafe fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) {
5000 -
    let mut a = testResolver();
5001 -
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }";
5002 -
    let result = try resolveProgramStr(&mut a, program);
5003 -
    let err = try expectError(&result);
5004 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5005 -
        else throw testing::TestError::Failed;
6496 +
    let mut testArena492 = testArena();
6497 +
    let testStorage492: 'test492 = &mut testArena492 in {
6498 +
        let mut a = testResolver(testStorage492);
6499 +
        let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }";
6500 +
        let result = try resolveProgramStr(&mut a, program);
6501 +
        let err = try expectError(&result);
6502 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
6503 +
            else throw testing::TestError::Failed;
6504 +
    }
5006 6505
}
5007 6506
5008 6507
/// Diamond supertrait inheritance: traits B and C both extend A.
5009 6508
/// Declaring them independently should work fine.
5010 6509
@test unsafe fn testResolveTraitDiamondSupertrait() throws (testing::TestError) {
5011 -
    let mut a = testResolver();
5012 -
    let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }";
5013 -
    let result = try resolveProgramStr(&mut a, program);
5014 -
    try expectNoErrors(&result);
6510 +
    let mut testArena493 = testArena();
6511 +
    let testStorage493: 'test493 = &mut testArena493 in {
6512 +
        let mut a = testResolver(testStorage493);
6513 +
        let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }";
6514 +
        let result = try resolveProgramStr(&mut a, program);
6515 +
        try expectNoErrors(&result);
6516 +
    }
5015 6517
}
5016 6518
5017 6519
/// Diamond supertrait with a combined trait that would cause duplicate
5018 6520
/// method names should be detected.
5019 6521
@test unsafe fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) {
5020 -
    let mut a = testResolver();
5021 -
    let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }";
5022 -
    let result = try resolveProgramStr(&mut a, program);
5023 -
    // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`.
5024 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f"));
6522 +
    let mut testArena494 = testArena();
6523 +
    let testStorage494: 'test494 = &mut testArena494 in {
6524 +
        let mut a = testResolver(testStorage494);
6525 +
        let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }";
6526 +
        let result = try resolveProgramStr(&mut a, program);
6527 +
        // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`.
6528 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f"));
6529 +
    }
5025 6530
}
5026 6531
5027 6532
/// Supertrait instance must exist when declaring a combined trait instance.
5028 6533
@test unsafe fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) {
5029 -
    let mut a = testResolver();
5030 -
    let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }";
5031 -
    let result = try resolveProgramStr(&mut a, program);
5032 -
    try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base"));
6534 +
    let mut testArena495 = testArena();
6535 +
    let testStorage495: 'test495 = &mut testArena495 in {
6536 +
        let mut a = testResolver(testStorage495);
6537 +
        let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }";
6538 +
        let result = try resolveProgramStr(&mut a, program);
6539 +
        try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base"));
6540 +
    }
5033 6541
}
5034 6542
5035 6543
/// Instance method omits return type when the trait declares `-> i32`.
5036 6544
/// This is rejected -- the return type must be stated explicitly.
5037 6545
@test unsafe fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) {
5038 -
    let mut a = testResolver();
5039 -
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }";
5040 -
    let result = try resolveProgramStr(&mut a, program);
5041 -
    let err = try expectError(&result);
5042 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5043 -
        else throw testing::TestError::Failed;
6546 +
    let mut testArena496 = testArena();
6547 +
    let testStorage496: 'test496 = &mut testArena496 in {
6548 +
        let mut a = testResolver(testStorage496);
6549 +
        let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }";
6550 +
        let result = try resolveProgramStr(&mut a, program);
6551 +
        let err = try expectError(&result);
6552 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
6553 +
            else throw testing::TestError::Failed;
6554 +
    }
5044 6555
}
5045 6556
5046 6557
/// Instance method declares throws but the trait method does not throw.
5047 6558
@test unsafe fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) {
5048 -
    let mut a = testResolver();
5049 -
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }";
5050 -
    let result = try resolveProgramStr(&mut a, program);
5051 -
    let err = try expectError(&result);
5052 -
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
5053 -
        else throw testing::TestError::Failed;
6559 +
    let mut testArena497 = testArena();
6560 +
    let testStorage497: 'test497 = &mut testArena497 in {
6561 +
        let mut a = testResolver(testStorage497);
6562 +
        let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }";
6563 +
        let result = try resolveProgramStr(&mut a, program);
6564 +
        let err = try expectError(&result);
6565 +
        let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
6566 +
            else throw testing::TestError::Failed;
6567 +
    }
5054 6568
}
5055 6569
5056 6570
/// Instance method declares a different throws type than the trait.
5057 6571
@test unsafe fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) {
5058 -
    let mut a = testResolver();
5059 -
    let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }";
5060 -
    let result = try resolveProgramStr(&mut a, program);
5061 -
    let err = try expectError(&result);
5062 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5063 -
        else throw testing::TestError::Failed;
6572 +
    let mut testArena498 = testArena();
6573 +
    let testStorage498: 'test498 = &mut testArena498 in {
6574 +
        let mut a = testResolver(testStorage498);
6575 +
        let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }";
6576 +
        let result = try resolveProgramStr(&mut a, program);
6577 +
        let err = try expectError(&result);
6578 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
6579 +
            else throw testing::TestError::Failed;
6580 +
    }
5064 6581
}
5065 6582
5066 6583
/// Instance method omits throws clause when trait declares throws.
5067 6584
/// This is rejected -- the throws clause must match exactly.
5068 6585
@test unsafe fn testResolveInstanceThrowsOmitted() throws (testing::TestError) {
5069 -
    let mut a = testResolver();
5070 -
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }";
5071 -
    let result = try resolveProgramStr(&mut a, program);
5072 -
    let err = try expectError(&result);
5073 -
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
5074 -
        else throw testing::TestError::Failed;
6586 +
    let mut testArena499 = testArena();
6587 +
    let testStorage499: 'test499 = &mut testArena499 in {
6588 +
        let mut a = testResolver(testStorage499);
6589 +
        let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }";
6590 +
        let result = try resolveProgramStr(&mut a, program);
6591 +
        let err = try expectError(&result);
6592 +
        let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
6593 +
            else throw testing::TestError::Failed;
6594 +
    }
5075 6595
}
5076 6596
5077 6597
/// Instance method correctly matches the trait's throws clause.
5078 6598
@test unsafe fn testResolveInstanceThrowsMatch() throws (testing::TestError) {
5079 -
    let mut a = testResolver();
5080 -
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }";
5081 -
    let result = try resolveProgramStr(&mut a, program);
5082 -
    try expectNoErrors(&result);
6599 +
    let mut testArena500 = testArena();
6600 +
    let testStorage500: 'test500 = &mut testArena500 in {
6601 +
        let mut a = testResolver(testStorage500);
6602 +
        let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }";
6603 +
        let result = try resolveProgramStr(&mut a, program);
6604 +
        try expectNoErrors(&result);
6605 +
    }
5083 6606
}
5084 6607
5085 6608
// Constant expression folding tests //////////////////////////////////////////
5086 6609
5087 6610
/// Resolve a program and verify that the constant at the given statement index
5088 6611
/// has the expected integer magnitude.
5089 6612
unsafe fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64)
5090 6613
    throws (testing::TestError)
5091 6614
{
5092 -
    let mut a = testResolver();
5093 -
    let result = try resolveProgramStr(&mut a, program);
5094 -
    try expectNoErrors(&result);
6615 +
    let mut testArena501 = testArena();
6616 +
    let testStorage501: 'test501 = &mut testArena501 in {
6617 +
        let mut a = testResolver(testStorage501);
6618 +
        let result = try resolveProgramStr(&mut a, program);
6619 +
        try expectNoErrors(&result);
5095 6620
5096 -
    let stmt = try getBlockStmt(result.root, stmtIdx);
5097 -
    let sym = super::symbolFor(&a, stmt)
5098 -
        else throw testing::TestError::Failed;
5099 -
    let case super::SymbolData::Constant { value, .. } = sym.data
5100 -
        else throw testing::TestError::Failed;
5101 -
    let val = value else throw testing::TestError::Failed;
5102 -
    let case super::ConstValue::Int(intVal) = val
5103 -
        else throw testing::TestError::Failed;
5104 -
    try testing::expect(intVal.magnitude == expected);
6621 +
        let stmt = try getBlockStmt(result.root, stmtIdx);
6622 +
        let sym = super::symbolFor(&a, stmt)
6623 +
            else throw testing::TestError::Failed;
6624 +
        let case super::SymbolData::Constant { value, .. } = sym.data
6625 +
            else throw testing::TestError::Failed;
6626 +
        let val = value else throw testing::TestError::Failed;
6627 +
        let case super::ConstValue::Int(intVal) = val
6628 +
            else throw testing::TestError::Failed;
6629 +
        try testing::expect(intVal.magnitude == expected);
6630 +
    }
5105 6631
}
5106 6632
5107 6633
/// Test arithmetic constant folding: add, sub, mul, div.
5108 6634
@test unsafe fn testConstExprArithmetic() throws (testing::TestError) {
5109 6635
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30);
5130 6656
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60);
5131 6657
}
5132 6658
5133 6659
/// Test constant expression used as array size.
5134 6660
@test unsafe fn testConstExprAsArraySize() throws (testing::TestError) {
5135 -
    let mut a = testResolver();
5136 -
    let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];";
5137 -
    let result = try resolveProgramStr(&mut a, program);
5138 -
    try expectNoErrors(&result);
6661 +
    let mut testArena502 = testArena();
6662 +
    let testStorage502: 'test502 = &mut testArena502 in {
6663 +
        let mut a = testResolver(testStorage502);
6664 +
        let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];";
6665 +
        let result = try resolveProgramStr(&mut a, program);
6666 +
        try expectNoErrors(&result);
5139 6667
5140 -
    let arrStmt = try getBlockStmt(result.root, 3);
5141 -
    let sym = super::symbolFor(&a, arrStmt)
5142 -
        else throw testing::TestError::Failed;
5143 -
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5144 -
        else throw testing::TestError::Failed;
5145 -
    try testing::expect(arrType.length == 5);
6668 +
        let arrStmt = try getBlockStmt(result.root, 3);
6669 +
        let sym = super::symbolFor(&a, arrStmt)
6670 +
            else throw testing::TestError::Failed;
6671 +
        let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
6672 +
            else throw testing::TestError::Failed;
6673 +
        try testing::expect(arrType.length == 5);
6674 +
    }
5146 6675
}
5147 6676
5148 6677
/// Test cross-module constant expression: a constant in one module references
5149 6678
/// a constant from another module via scope access.
5150 6679
@test unsafe fn testCrossModuleConstExpr() throws (testing::TestError) {
5151 -
    let mut a = testResolver();
5152 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
6680 +
    let mut testArena503 = testArena();
6681 +
    let testStorage503: 'test503 = &mut testArena503 in {
6682 +
        let mut a = testResolver(testStorage503);
6683 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5153 6684
5154 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5155 -
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena);
5156 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena);
6685 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
6686 +
        let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena);
6687 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena);
5157 6688
5158 -
    let result = try resolveModuleTree(&mut a, rootId);
5159 -
    try expectNoErrors(&result);
6689 +
        let result = try resolveModuleTree(&mut a, rootId);
6690 +
        try expectNoErrors(&result);
6691 +
    }
5160 6692
}
5161 6693
5162 6694
/// Test cross-module constant expression used as array size.
5163 6695
@test unsafe fn testCrossModuleConstExprArraySize() throws (testing::TestError) {
5164 -
    let mut a = testResolver();
5165 -
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
6696 +
    let mut testArena504 = testArena();
6697 +
    let testStorage504: 'test504 = &mut testArena504 in {
6698 +
        let mut a = testResolver(testStorage504);
6699 +
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5166 6700
5167 -
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5168 -
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena);
5169 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena);
6701 +
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
6702 +
        let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena);
6703 +
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena);
5170 6704
5171 -
    let result = try resolveModuleTree(&mut a, rootId);
5172 -
    try expectNoErrors(&result);
6705 +
        let result = try resolveModuleTree(&mut a, rootId);
6706 +
        try expectNoErrors(&result);
6707 +
    }
5173 6708
}
5174 6709
5175 6710
/// Test that non-constant expressions in constant declarations are still rejected.
5176 6711
@test unsafe fn testConstExprNonConstRejected() throws (testing::TestError) {
5177 -
    let mut a = testResolver();
5178 -
    let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;";
5179 -
    let result = try resolveProgramStr(&mut a, program);
5180 -
    let err = try expectError(&result);
5181 -
    let case super::ErrorKind::ConstExprRequired = err.kind
5182 -
        else throw testing::TestError::Failed;
6712 +
    let mut testArena505 = testArena();
6713 +
    let testStorage505: 'test505 = &mut testArena505 in {
6714 +
        let mut a = testResolver(testStorage505);
6715 +
        let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;";
6716 +
        let result = try resolveProgramStr(&mut a, program);
6717 +
        let err = try expectError(&result);
6718 +
        let case super::ErrorKind::ConstExprRequired = err.kind
6719 +
            else throw testing::TestError::Failed;
6720 +
    }
5183 6721
}
5184 6722
5185 6723
/// Test unary negation in constant expressions.
5186 6724
@test unsafe fn testConstExprUnaryNeg() throws (testing::TestError) {
5187 -
    let mut a = testResolver();
5188 -
    let program = "constant A: i32 = 10; constant B: i32 = -A;";
5189 -
    let result = try resolveProgramStr(&mut a, program);
5190 -
    try expectNoErrors(&result);
6725 +
    let mut testArena506 = testArena();
6726 +
    let testStorage506: 'test506 = &mut testArena506 in {
6727 +
        let mut a = testResolver(testStorage506);
6728 +
        let program = "constant A: i32 = 10; constant B: i32 = -A;";
6729 +
        let result = try resolveProgramStr(&mut a, program);
6730 +
        try expectNoErrors(&result);
6731 +
    }
5191 6732
}
5192 6733
5193 6734
/// Test unary not in constant expressions.
5194 6735
@test unsafe fn testConstExprUnaryNot() throws (testing::TestError) {
5195 -
    let mut a = testResolver();
5196 -
    let program = "constant A: bool = true; constant B: bool = not A;";
5197 -
    let result = try resolveProgramStr(&mut a, program);
5198 -
    try expectNoErrors(&result);
6736 +
    let mut testArena507 = testArena();
6737 +
    let testStorage507: 'test507 = &mut testArena507 in {
6738 +
        let mut a = testResolver(testStorage507);
6739 +
        let program = "constant A: bool = true; constant B: bool = not A;";
6740 +
        let result = try resolveProgramStr(&mut a, program);
6741 +
        try expectNoErrors(&result);
6742 +
    }
5199 6743
}
5200 6744
5201 6745
/// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining.
5202 6746
@test unsafe fn testConstExprCast() throws (testing::TestError) {
5203 6747
    try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42);
5213 6757
    try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14);
5214 6758
}
5215 6759
5216 6760
/// Test `as` cast in constant expressions used as array size.
5217 6761
@test unsafe fn testConstExprCastAsArraySize() throws (testing::TestError) {
5218 -
    let mut a = testResolver();
5219 -
    let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];";
5220 -
    let result = try resolveProgramStr(&mut a, program);
5221 -
    try expectNoErrors(&result);
6762 +
    let mut testArena508 = testArena();
6763 +
    let testStorage508: 'test508 = &mut testArena508 in {
6764 +
        let mut a = testResolver(testStorage508);
6765 +
        let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];";
6766 +
        let result = try resolveProgramStr(&mut a, program);
6767 +
        try expectNoErrors(&result);
5222 6768
5223 -
    let arrStmt = try getBlockStmt(result.root, 2);
5224 -
    let sym = super::symbolFor(&a, arrStmt)
5225 -
        else throw testing::TestError::Failed;
5226 -
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5227 -
        else throw testing::TestError::Failed;
5228 -
    try testing::expect(arrType.length == 4);
6769 +
        let arrStmt = try getBlockStmt(result.root, 2);
6770 +
        let sym = super::symbolFor(&a, arrStmt)
6771 +
            else throw testing::TestError::Failed;
6772 +
        let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
6773 +
            else throw testing::TestError::Failed;
6774 +
        try testing::expect(arrType.length == 4);
6775 +
    }
5229 6776
}
5230 6777
5231 6778
/// Test unsuffixed integer literals in constant expressions.
5232 6779
@test unsafe fn testConstExprUnsuffixedLiterals() throws (testing::TestError) {
5233 6780
    try expectConstFold("constant A: u32 = 4 * 4;", 0, 16);
5237 6784
    try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7);
5238 6785
}
5239 6786
5240 6787
/// References cannot escape through return types.
5241 6788
@test unsafe fn testRefReturnRejected() throws (testing::TestError) {
5242 -
    let mut a = testResolver();
5243 -
    let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }";
5244 -
    let result = try resolveProgramStr(&mut a, program);
5245 -
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
6789 +
    let mut testArena509 = testArena();
6790 +
    let testStorage509: 'test509 = &mut testArena509 in {
6791 +
        let mut a = testResolver(testStorage509);
6792 +
        let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }";
6793 +
        let result = try resolveProgramStr(&mut a, program);
6794 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
6795 +
    }
5246 6796
}
5247 6797
5248 6798
/// Case-pattern fallbacks must terminate instead of synthesizing bindings.
5249 6799
@test unsafe fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5250 -
    let mut a = testResolver();
5251 -
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
5252 -
    let result = try resolveProgramStr(&mut a, program);
5253 -
    try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
6800 +
    let mut testArena510 = testArena();
6801 +
    let testStorage510: 'test510 = &mut testArena510 in {
6802 +
        let mut a = testResolver(testStorage510);
6803 +
        let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
6804 +
        let result = try resolveProgramStr(&mut a, program);
6805 +
        try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
6806 +
    }
5254 6807
}
5255 6808
5256 6809
/// Case bindings are unavailable on the pattern-failure path.
5257 6810
@test unsafe fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5258 -
    let mut a = testResolver();
5259 -
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
5260 -
    let result = try resolveProgramStr(&mut a, program);
5261 -
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
6811 +
    let mut testArena511 = testArena();
6812 +
    let testStorage511: 'test511 = &mut testArena511 in {
6813 +
        let mut a = testResolver(testStorage511);
6814 +
        let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
6815 +
        let result = try resolveProgramStr(&mut a, program);
6816 +
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
6817 +
    }
5262 6818
}
5263 6819
5264 6820
/// Unsafe pointer dereference requires an unsafe declaration.
5265 6821
@test unsafe fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5266 -
    let mut a = testResolver();
5267 -
    let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5268 -
    let result = try resolveProgramStr(&mut a, program);
5269 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
6822 +
    let mut testArena512 = testArena();
6823 +
    let testStorage512: 'test512 = &mut testArena512 in {
6824 +
        let mut a = testResolver(testStorage512);
6825 +
        let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
6826 +
        let result = try resolveProgramStr(&mut a, program);
6827 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
6828 +
    }
5270 6829
}
5271 6830
5272 6831
/// Unsafe pointers remain freely copyable inside an unsafe declaration.
5273 6832
@test unsafe fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5274 6833
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5275 6834
    try expectAnalyzeOk(program);
5276 6835
}
5277 6836
5278 6837
/// Safe code cannot call a function that accepts unsafe operations.
5279 6838
@test unsafe fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5280 -
    let mut a = testResolver();
5281 -
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
5282 -
    let result = try resolveProgramStr(&mut a, program);
5283 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6839 +
    let mut testArena513 = testArena();
6840 +
    let testStorage513: 'test513 = &mut testArena513 in {
6841 +
        let mut a = testResolver(testStorage513);
6842 +
        let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
6843 +
        let result = try resolveProgramStr(&mut a, program);
6844 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6845 +
    }
5284 6846
}
5285 6847
5286 6848
/// Unsafe function values retain their call-site safety requirement.
5287 6849
@test unsafe fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5288 -
    let mut a = testResolver();
5289 -
    let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
5290 -
    let result = try resolveProgramStr(&mut a, program);
5291 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6850 +
    let mut testArena514 = testArena();
6851 +
    let testStorage514: 'test514 = &mut testArena514 in {
6852 +
        let mut a = testResolver(testStorage514);
6853 +
        let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
6854 +
        let result = try resolveProgramStr(&mut a, program);
6855 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6856 +
    }
5292 6857
}
5293 6858
5294 6859
/// References cannot be embedded in aggregate fields.
5295 6860
@test unsafe fn testRefFieldRejected() throws (testing::TestError) {
5296 -
    let mut a = testResolver();
5297 -
    let program = "record Marker: Once {} record Bad { value: &u32 }";
5298 -
    let result = try resolveProgramStr(&mut a, program);
5299 -
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
6861 +
    let mut testArena515 = testArena();
6862 +
    let testStorage515: 'test515 = &mut testArena515 in {
6863 +
        let mut a = testResolver(testStorage515);
6864 +
        let program = "record Marker: Once {} record Bad { value: &u32 }";
6865 +
        let result = try resolveProgramStr(&mut a, program);
6866 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
6867 +
    }
5300 6868
}
5301 6869
5302 6870
/// Trait methods may use reference receivers.
5303 6871
@test unsafe fn testTraitRefReceiver() throws (testing::TestError) {
5304 6872
    let program = "record Marker: Once {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }";
5305 6873
    try expectAnalyzeOk(program);
5306 6874
}
5307 6875
5308 6876
/// Trait implementations must preserve the receiver pointer class.
5309 6877
@test unsafe fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5310 -
    let mut a = testResolver();
5311 -
    let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
5312 -
    let result = try resolveProgramStr(&mut a, program);
5313 -
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
6878 +
    let mut testArena516 = testArena();
6879 +
    let testStorage516: 'test516 = &mut testArena516 in {
6880 +
        let mut a = testResolver(testStorage516);
6881 +
        let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
6882 +
        let result = try resolveProgramStr(&mut a, program);
6883 +
        try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
6884 +
    }
5314 6885
}
5315 6886
5316 6887
/// Unmarked composite values may be discarded.
5317 6888
@test unsafe fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) {
5318 6889
    let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }";
5319 6890
    try expectAnalyzeOk(program);
5320 6891
}
5321 6892
5322 6893
/// A by-value use moves an unmarked composite value.
5323 6894
@test unsafe fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) {
5324 -
    let mut a = testResolver();
5325 -
    let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }";
5326 -
    let result = try resolveProgramStr(&mut a, program);
5327 -
    try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value"));
6895 +
    let mut testArena517 = testArena();
6896 +
    let testStorage517: 'test517 = &mut testArena517 in {
6897 +
        let mut a = testResolver(testStorage517);
6898 +
        let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }";
6899 +
        let result = try resolveProgramStr(&mut a, program);
6900 +
        try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value"));
6901 +
    }
5328 6902
}
5329 6903
5330 6904
/// Affine values may move on only one branch when not used later.
5331 6905
@test unsafe fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) {
5332 6906
    let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }";
5339 6913
    try expectAnalyzeOk(program);
5340 6914
}
5341 6915
5342 6916
/// A `Copy` composite may contain only copy values.
5343 6917
@test unsafe fn testCopyCompositeRejectsAffineField() throws (testing::TestError) {
5344 -
    let mut a = testResolver();
5345 -
    let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }";
5346 -
    let result = try resolveProgramStr(&mut a, program);
5347 -
    try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
6918 +
    let mut testArena518 = testArena();
6919 +
    let testStorage518: 'test518 = &mut testArena518 in {
6920 +
        let mut a = testResolver(testStorage518);
6921 +
        let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }";
6922 +
        let result = try resolveProgramStr(&mut a, program);
6923 +
        try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
6924 +
    }
5348 6925
}
5349 6926
5350 6927
/// A composite cannot carry conflicting ownership markers.
5351 6928
@test unsafe fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) {
5352 -
    let mut a = testResolver();
5353 -
    let program = "record Value: Copy + Once { number: u32 }";
5354 -
    let result = try resolveProgramStr(&mut a, program);
5355 -
    try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers);
6929 +
    let mut testArena519 = testArena();
6930 +
    let testStorage519: 'test519 = &mut testArena519 in {
6931 +
        let mut a = testResolver(testStorage519);
6932 +
        let program = "record Value: Copy + Once { number: u32 }";
6933 +
        let result = try resolveProgramStr(&mut a, program);
6934 +
        try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers);
6935 +
    }
5356 6936
}
5357 6937
5358 6938
/// Linear composites still require one consuming use.
5359 6939
@test unsafe fn testLinearCompositeMustBeConsumed() throws (testing::TestError) {
5360 -
    let mut a = testResolver();
5361 -
    let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }";
5362 -
    let result = try resolveProgramStr(&mut a, program);
5363 -
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token"));
6940 +
    let mut testArena520 = testArena();
6941 +
    let testStorage520: 'test520 = &mut testArena520 in {
6942 +
        let mut a = testResolver(testStorage520);
6943 +
        let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }";
6944 +
        let result = try resolveProgramStr(&mut a, program);
6945 +
        try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token"));
6946 +
    }
5364 6947
}
5365 6948
5366 6949
/// The compiler-known marker cannot be derived more than once.
5367 6950
@test unsafe fn testDuplicateOnceMarkerRejected() throws (testing::TestError) {
5368 -
    let mut a = testResolver();
5369 -
    let program = "record Token: Once + Once { value: u32 }";
5370 -
    let result = try resolveProgramStr(&mut a, program);
5371 -
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once"));
6951 +
    let mut testArena521 = testArena();
6952 +
    let testStorage521: 'test521 = &mut testArena521 in {
6953 +
        let mut a = testResolver(testStorage521);
6954 +
        let program = "record Token: Once + Once { value: u32 }";
6955 +
        let result = try resolveProgramStr(&mut a, program);
6956 +
        try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once"));
6957 +
    }
5372 6958
}
5373 6959
5374 6960
/// Stack storage cannot produce a safe stored pointer or slice.
5375 6961
@test unsafe fn testStackPointerRejected() throws (testing::TestError) {
5376 6962
    let programs = &[
5395 6981
        "record Cell { value: u32 } fn run(value: &Cell) -> *u32 { return &value.value; }",
5396 6982
        "record Cell { value: *u32 } fn run(value: &u32) -> Cell { return Cell { value }; }",
5397 6983
        "fn run(value: &u32) -> *[u32] { return @sliceOf(value, 1); }",
5398 6984
    ];
5399 6985
    for program in programs {
5400 -
        let mut resolver = testResolver();
5401 -
        let result = try resolveProgramStr(&mut resolver, program);
5402 -
        let _ = try expectError(&result);
6986 +
        let mut testArena522 = testArena();
6987 +
        let testStorage522: 'test522 = &mut testArena522 in {
6988 +
            let mut resolver = testResolver(testStorage522);
6989 +
            let result = try resolveProgramStr(&mut resolver, program);
6990 +
            let _ = try expectError(&result);
6991 +
        }
5403 6992
    }
5404 6993
}
5405 6994
5406 6995
/// Stack values can be borrowed for a call.
5407 6996
@test unsafe fn testStackBorrowAllowed() throws (testing::TestError) {
5433 7022
        "fn write(p: &mut [u32]) {} fn run(p: *unsafe mut [u32]) { write(p); }",
5434 7023
        "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} fn run(p: *unsafe opaque Read) { read(p); }",
5435 7024
        "fn read(p: &u32) {} fn run(p: *unsafe u32) { unsafe { read(p); } read(p); }",
5436 7025
    ];
5437 7026
    for program in programs {
5438 -
        let mut a = testResolver();
5439 -
        let result = try resolveProgramStr(&mut a, program);
5440 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7027 +
        let mut testArena523 = testArena();
7028 +
        let testStorage523: 'test523 = &mut testArena523 in {
7029 +
            let mut a = testResolver(testStorage523);
7030 +
            let result = try resolveProgramStr(&mut a, program);
7031 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7032 +
        }
5441 7033
    }
5442 7034
}
5443 7035
5444 7036
/// Raw borrows preserve mutability, storage type, and ownership constraints.
5445 7037
@test unsafe fn testImplicitRawBorrowPreservesTypes() throws (testing::TestError) {
5451 7043
        "fn take(p: *u32) {} unsafe fn run(p: *unsafe u32) { take(p); }",
5452 7044
        "fn take(p: *[u32]) {} unsafe fn run(p: *unsafe [u32]) { take(p); }",
5453 7045
        "fn read(p: &*u32) {} unsafe fn run(p: *unsafe *unsafe u32) { read(p); }",
5454 7046
    ];
5455 7047
    for program in programs {
5456 -
        let mut a = testResolver();
5457 -
        let result = try resolveProgramStr(&mut a, program);
5458 -
        let err = try expectError(&result);
5459 -
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5460 -
            else throw testing::TestError::Failed;
7048 +
        let mut testArena524 = testArena();
7049 +
        let testStorage524: 'test524 = &mut testArena524 in {
7050 +
            let mut a = testResolver(testStorage524);
7051 +
            let result = try resolveProgramStr(&mut a, program);
7052 +
            let err = try expectError(&result);
7053 +
            let case super::ErrorKind::TypeMismatch(_) = err.kind
7054 +
                else throw testing::TestError::Failed;
7055 +
        }
5461 7056
    }
5462 7057
}
5463 7058
5464 7059
/// Implicit raw borrows retain the call's exclusive-borrow checks.
5465 7060
@test unsafe fn testImplicitRawBorrowConflict() throws (testing::TestError) {
5466 -
    let mut a = testResolver();
5467 -
    let result = try resolveProgramStr(&mut a,
5468 -
        "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }");
5469 -
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p"));
7061 +
    let mut testArena525 = testArena();
7062 +
    let testStorage525: 'test525 = &mut testArena525 in {
7063 +
        let mut a = testResolver(testStorage525);
7064 +
        let result = try resolveProgramStr(&mut a,
7065 +
            "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }");
7066 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p"));
7067 +
    }
5470 7068
}
5471 7069
5472 7070
/// Unsafe declarations can store raw pointers to stack values.
5473 7071
@test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) {
5474 7072
    try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }");
5491 7089
}
5492 7090
5493 7091
/// References are rejected from every nested or storable type position.
5494 7092
@test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) {
5495 7093
    {
5496 -
        let mut a = testResolver();
5497 -
        let program = "record Marker: Once {} union Bad { Value(&u32) }";
5498 -
        let result = try resolveProgramStr(&mut a, program);
5499 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7094 +
        let mut testArena526 = testArena();
7095 +
        let testStorage526: 'test526 = &mut testArena526 in {
7096 +
            let mut a = testResolver(testStorage526);
7097 +
            let program = "record Marker: Once {} union Bad { Value(&u32) }";
7098 +
            let result = try resolveProgramStr(&mut a, program);
7099 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7100 +
        }
5500 7101
    } {
5501 -
        let mut a = testResolver();
5502 -
        let program = "record Marker: Once {} fn bad(value: ?&u32) {}";
5503 -
        let result = try resolveProgramStr(&mut a, program);
5504 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7102 +
        let mut testArena527 = testArena();
7103 +
        let testStorage527: 'test527 = &mut testArena527 in {
7104 +
            let mut a = testResolver(testStorage527);
7105 +
            let program = "record Marker: Once {} fn bad(value: ?&u32) {}";
7106 +
            let result = try resolveProgramStr(&mut a, program);
7107 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7108 +
        }
5505 7109
    } {
5506 -
        let mut a = testResolver();
5507 -
        let program = "record Marker: Once {} fn bad(value: [&u32; 1]) {}";
5508 -
        let result = try resolveProgramStr(&mut a, program);
5509 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7110 +
        let mut testArena528 = testArena();
7111 +
        let testStorage528: 'test528 = &mut testArena528 in {
7112 +
            let mut a = testResolver(testStorage528);
7113 +
            let program = "record Marker: Once {} fn bad(value: [&u32; 1]) {}";
7114 +
            let result = try resolveProgramStr(&mut a, program);
7115 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7116 +
        }
5510 7117
    } {
5511 -
        let mut a = testResolver();
5512 -
        let program = "record Marker: Once {} fn bad(value: *&u32) {}";
5513 -
        let result = try resolveProgramStr(&mut a, program);
5514 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7118 +
        let mut testArena529 = testArena();
7119 +
        let testStorage529: 'test529 = &mut testArena529 in {
7120 +
            let mut a = testResolver(testStorage529);
7121 +
            let program = "record Marker: Once {} fn bad(value: *&u32) {}";
7122 +
            let result = try resolveProgramStr(&mut a, program);
7123 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7124 +
        }
5515 7125
    } {
5516 -
        let mut a = testResolver();
5517 -
        let program = "record Marker: Once {} static BAD: &u32 = undefined;";
5518 -
        let result = try resolveProgramStr(&mut a, program);
5519 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7126 +
        let mut testArena530 = testArena();
7127 +
        let testStorage530: 'test530 = &mut testArena530 in {
7128 +
            let mut a = testResolver(testStorage530);
7129 +
            let program = "record Marker: Once {} static BAD: &u32 = undefined;";
7130 +
            let result = try resolveProgramStr(&mut a, program);
7131 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7132 +
        }
5520 7133
    } {
5521 -
        let mut a = testResolver();
5522 -
        let program = "record Marker: Once {} fn bad(callback: fn() -> &u32) {}";
5523 -
        let result = try resolveProgramStr(&mut a, program);
5524 -
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7134 +
        let mut testArena531 = testArena();
7135 +
        let testStorage531: 'test531 = &mut testArena531 in {
7136 +
            let mut a = testResolver(testStorage531);
7137 +
            let program = "record Marker: Once {} fn bad(callback: fn() -> &u32) {}";
7138 +
            let result = try resolveProgramStr(&mut a, program);
7139 +
            try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
7140 +
        }
5525 7141
    }
5526 7142
}
5527 7143
5528 7144
/// Function pointer parameter references remain call-scoped and valid.
5529 7145
@test unsafe fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5532 7148
}
5533 7149
5534 7150
/// Pointer and slice casts cannot change reference ownership.
5535 7151
@test unsafe fn testRefCastClassPreserved() throws (testing::TestError) {
5536 7152
    {
5537 -
        let mut a = testResolver();
5538 -
        let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }";
5539 -
        let result = try resolveProgramStr(&mut a, program);
5540 -
        let err = try expectError(&result);
5541 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5542 -
            else throw testing::TestError::Failed;
7153 +
        let mut testArena532 = testArena();
7154 +
        let testStorage532: 'test532 = &mut testArena532 in {
7155 +
            let mut a = testResolver(testStorage532);
7156 +
            let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }";
7157 +
            let result = try resolveProgramStr(&mut a, program);
7158 +
            let err = try expectError(&result);
7159 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
7160 +
                else throw testing::TestError::Failed;
7161 +
        }
5543 7162
    } {
5544 -
        let mut a = testResolver();
5545 -
        let program = "record Marker: Once {} fn cast(values: &[u32]) { values as *[u32]; }";
5546 -
        let result = try resolveProgramStr(&mut a, program);
5547 -
        let err = try expectError(&result);
5548 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5549 -
            else throw testing::TestError::Failed;
7163 +
        let mut testArena533 = testArena();
7164 +
        let testStorage533: 'test533 = &mut testArena533 in {
7165 +
            let mut a = testResolver(testStorage533);
7166 +
            let program = "record Marker: Once {} fn cast(values: &[u32]) { values as *[u32]; }";
7167 +
            let result = try resolveProgramStr(&mut a, program);
7168 +
            let err = try expectError(&result);
7169 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
7170 +
                else throw testing::TestError::Failed;
7171 +
        }
5550 7172
    }
5551 7173
}
5552 7174
5553 7175
/// Every operation that interprets an unsafe address requires an unsafe declaration.
5554 7176
@test unsafe fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5555 7177
    {
5556 -
        let mut a = testResolver();
5557 -
        let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
5558 -
        let result = try resolveProgramStr(&mut a, program);
5559 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7178 +
        let mut testArena534 = testArena();
7179 +
        let testStorage534: 'test534 = &mut testArena534 in {
7180 +
            let mut a = testResolver(testStorage534);
7181 +
            let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
7182 +
            let result = try resolveProgramStr(&mut a, program);
7183 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7184 +
        }
5560 7185
    } {
5561 -
        let mut a = testResolver();
5562 -
        let program = "record Marker: Once {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }";
5563 -
        let result = try resolveProgramStr(&mut a, program);
5564 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7186 +
        let mut testArena535 = testArena();
7187 +
        let testStorage535: 'test535 = &mut testArena535 in {
7188 +
            let mut a = testResolver(testStorage535);
7189 +
            let program = "record Marker: Once {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }";
7190 +
            let result = try resolveProgramStr(&mut a, program);
7191 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7192 +
        }
5565 7193
    } {
5566 -
        let mut a = testResolver();
5567 -
        let program = "record Marker: Once {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }";
5568 -
        let result = try resolveProgramStr(&mut a, program);
5569 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7194 +
        let mut testArena536 = testArena();
7195 +
        let testStorage536: 'test536 = &mut testArena536 in {
7196 +
            let mut a = testResolver(testStorage536);
7197 +
            let program = "record Marker: Once {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }";
7198 +
            let result = try resolveProgramStr(&mut a, program);
7199 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7200 +
        }
5570 7201
    } {
5571 -
        let mut a = testResolver();
5572 -
        let program = "record Marker: Once {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }";
5573 -
        let result = try resolveProgramStr(&mut a, program);
5574 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7202 +
        let mut testArena537 = testArena();
7203 +
        let testStorage537: 'test537 = &mut testArena537 in {
7204 +
            let mut a = testResolver(testStorage537);
7205 +
            let program = "record Marker: Once {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }";
7206 +
            let result = try resolveProgramStr(&mut a, program);
7207 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7208 +
        }
5575 7209
    } {
5576 -
        let mut a = testResolver();
5577 -
        let program = "record Marker: Once {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }";
5578 -
        let result = try resolveProgramStr(&mut a, program);
5579 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7210 +
        let mut testArena538 = testArena();
7211 +
        let testStorage538: 'test538 = &mut testArena538 in {
7212 +
            let mut a = testResolver(testStorage538);
7213 +
            let program = "record Marker: Once {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }";
7214 +
            let result = try resolveProgramStr(&mut a, program);
7215 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7216 +
        }
5580 7217
    } {
5581 -
        let mut a = testResolver();
5582 -
        let program = "record Marker: Once {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }";
5583 -
        let result = try resolveProgramStr(&mut a, program);
5584 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7218 +
        let mut testArena539 = testArena();
7219 +
        let testStorage539: 'test539 = &mut testArena539 in {
7220 +
            let mut a = testResolver(testStorage539);
7221 +
            let program = "record Marker: Once {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }";
7222 +
            let result = try resolveProgramStr(&mut a, program);
7223 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7224 +
        }
5585 7225
    } {
5586 -
        let mut a = testResolver();
5587 -
        let program = "record Marker: Once {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }";
5588 -
        let result = try resolveProgramStr(&mut a, program);
5589 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7226 +
        let mut testArena540 = testArena();
7227 +
        let testStorage540: 'test540 = &mut testArena540 in {
7228 +
            let mut a = testResolver(testStorage540);
7229 +
            let program = "record Marker: Once {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }";
7230 +
            let result = try resolveProgramStr(&mut a, program);
7231 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7232 +
        }
5590 7233
    }
5591 7234
}
5592 7235
5593 7236
/// Pointer offsets require an unsafe function for either operand order.
5594 7237
@test unsafe fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) {
5598 7241
        "fn run(p: *u8) -> *u8 { return p - 1; }",
5599 7242
        "fn run(input: *mut u8) { let mut p = input; set p += 1; }",
5600 7243
        "static DATA: [u8; 1] = [42]; fn run() -> u8 { let p = &DATA[0]; return *(p + 1); }",
5601 7244
    ];
5602 7245
    for program in programs {
5603 -
        let mut a = testResolver();
5604 -
        let result = try resolveProgramStr(&mut a, program);
5605 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7246 +
        let mut testArena541 = testArena();
7247 +
        let testStorage541: 'test541 = &mut testArena541 in {
7248 +
            let mut a = testResolver(testStorage541);
7249 +
            let result = try resolveProgramStr(&mut a, program);
7250 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7251 +
        }
5606 7252
    }
5607 7253
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return p + 1; }");
5608 7254
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return 1 + p; }");
5609 7255
    try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut u8 { return p - 1; }");
5610 7256
    try expectAnalyzeOk("fn run(value: u32) -> u32 { return value + 1; }");
5622 7268
        "fn run(s: *[opaque]) -> *[u64] { return s as *[u64]; }",
5623 7269
        "fn run(s: &mut [u64]) { let _ = s as &mut [u8]; }",
5624 7270
        "static DATA: [u8; 1] = [42]; fn run() -> u64 { return *(&DATA[0] as *u64); }",
5625 7271
    ];
5626 7272
    for program in programs {
5627 -
        let mut a = testResolver();
5628 -
        let result = try resolveProgramStr(&mut a, program);
5629 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7273 +
        let mut testArena542 = testArena();
7274 +
        let testStorage542: 'test542 = &mut testArena542 in {
7275 +
            let mut a = testResolver(testStorage542);
7276 +
            let result = try resolveProgramStr(&mut a, program);
7277 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7278 +
        }
5630 7279
    }
5631 7280
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u64 { return p as *u64; }");
5632 7281
    try expectAnalyzeOk("unsafe fn run(p: &u8) -> u64 { return *(p as &u64); }");
5633 7282
    try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }");
5634 7283
    try expectAnalyzeOk("fn run(p: *u8) -> *u8 { return p as *u8; }");
5645 7294
        "fn run(p: &u8) -> u8 { return @sliceOf(p, 100)[99]; }",
5646 7295
        "fn run(p: &mut u8) { set @sliceOf(p, 100)[99] = 1; }",
5647 7296
        "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = @sliceOf(&DATA[0], 100); return s[99]; }",
5648 7297
    ];
5649 7298
    for program in programs {
5650 -
        let mut a = testResolver();
5651 -
        let result = try resolveProgramStr(&mut a, program);
5652 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7299 +
        let mut testArena543 = testArena();
7300 +
        let testStorage543: 'test543 = &mut testArena543 in {
7301 +
            let mut a = testResolver(testStorage543);
7302 +
            let result = try resolveProgramStr(&mut a, program);
7303 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7304 +
        }
5653 7305
    }
5654 7306
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }");
5655 7307
    try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }");
5656 7308
    try expectAnalyzeOk("unsafe fn run(p: &u8) -> u8 { return @sliceOf(p, 1)[0]; }");
5657 7309
    try expectAnalyzeOk("fn run(s: *[u8]) -> *[u8] { return &s[..]; }");
5669 7321
        "fn change(p: &mut *u8, q: *u8) { set *p = q; } fn run(q: *u8) { let mut s: *[u8] = &[]; change(&mut s.ptr, q); }",
5670 7322
        "fn run(s: &mut *[u8]) { set s.len = 100; }",
5671 7323
        "static DATA: [u8; 1] = [42]; fn run() -> u8 { let mut s = &DATA[..]; set s.len = 100; return s[99]; }",
5672 7324
    ];
5673 7325
    for program in programs {
5674 -
        let mut a = testResolver();
5675 -
        let result = try resolveProgramStr(&mut a, program);
5676 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7326 +
        let mut testArena544 = testArena();
7327 +
        let testStorage544: 'test544 = &mut testArena544 in {
7328 +
            let mut a = testResolver(testStorage544);
7329 +
            let result = try resolveProgramStr(&mut a, program);
7330 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7331 +
        }
5677 7332
    }
5678 7333
    try expectAnalyzeOk("unsafe fn run(s: *mut [u8]) { set s.len = 0; set s.cap = 0; }");
5679 7334
    try expectAnalyzeOk("unsafe fn run(s: *mut [u8], p: *mut u8) { set s.ptr = p; }");
5680 7335
    try expectAnalyzeOk("fn change(n: &mut u32) { set *n = 0; } unsafe fn run(s: *mut [u8]) { change(&mut s.len); }");
5681 7336
    try expectAnalyzeOk("fn run(s: *mut [u8]) { set s[0] = 1; }");
5697 7352
        "fn run(p: *u8) -> *u8 { unsafe { let q = p + 1; } return p + 1; }",
5698 7353
        "fn run(p: *u8) -> *u8 { unsafe { unsafe {} } return p + 1; }",
5699 7354
        "unsafe fn first(p: *u8) -> *u8 { return p + 1; } fn second(p: *u8) -> *u8 { return p + 1; }",
5700 7355
    ];
5701 7356
    for program in programs {
5702 -
        let mut a = testResolver();
5703 -
        let result = try resolveProgramStr(&mut a, program);
5704 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7357 +
        let mut testArena545 = testArena();
7358 +
        let testStorage545: 'test545 = &mut testArena545 in {
7359 +
            let mut a = testResolver(testStorage545);
7360 +
            let result = try resolveProgramStr(&mut a, program);
7361 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7362 +
        }
7363 +
    }
7364 +
    let mut testArena546 = testArena();
7365 +
    let testStorage546: 'test546 = &mut testArena546 in {
7366 +
        let mut a = testResolver(testStorage546);
7367 +
        let result = try resolveProgramStr(&mut a, "unsafe fn act() {} fn run() { unsafe { act(); } act(); }");
7368 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5705 7369
    }
5706 -
    let mut a = testResolver();
5707 -
    let result = try resolveProgramStr(&mut a, "unsafe fn act() {} fn run() { unsafe { act(); } act(); }");
5708 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5709 7370
}
5710 7371
5711 7372
/// Resolution errors do not extend an unsafe block's permission.
5712 7373
@test unsafe fn testUnsafeBlockErrorRestoresContext() throws (testing::TestError) {
5713 -
    let mut a = testResolver();
5714 -
    let result = try resolveProgramStr(&mut a, "fn run(p: *u8) { unsafe { missing; } let q = p + 1; }");
5715 -
    let mut found = false;
5716 -
    for err in result.diagnostics.errors {
5717 -
        if let case super::ErrorKind::UnsafeOperation = err.kind {
5718 -
            set found = true;
7374 +
    let mut testArena547 = testArena();
7375 +
    let testStorage547: 'test547 = &mut testArena547 in {
7376 +
        let mut a = testResolver(testStorage547);
7377 +
        let result = try resolveProgramStr(&mut a, "fn run(p: *u8) { unsafe { missing; } let q = p + 1; }");
7378 +
        let mut found = false;
7379 +
        for err in result.diagnostics.errors {
7380 +
            if let case super::ErrorKind::UnsafeOperation = err.kind {
7381 +
                set found = true;
7382 +
            }
5719 7383
        }
7384 +
        try testing::expect(found);
5720 7385
    }
5721 -
    try testing::expect(found);
5722 7386
}
5723 7387
5724 7388
/// Unsafe blocks preserve reference lifetime checks.
5725 7389
@test unsafe fn testUnsafeBlockPreservesReferences() throws (testing::TestError) {
5726 -
    let mut a = testResolver();
5727 -
    let result = try resolveProgramStr(&mut a, "fn run() { let mut n: u32 = 1; let p = &n; unsafe { set n = 2; } }");
5728 -
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n"));
7390 +
    let mut testArena548 = testArena();
7391 +
    let testStorage548: 'test548 = &mut testArena548 in {
7392 +
        let mut a = testResolver(testStorage548);
7393 +
        let result = try resolveProgramStr(&mut a, "fn run() { let mut n: u32 = 1; let p = &n; unsafe { set n = 2; } }");
7394 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n"));
7395 +
    }
5729 7396
}
5730 7397
5731 7398
/// Slice pointer access requires an unsafe context for every slice class.
5732 7399
@test unsafe fn testSlicePointerRequiresUnsafe() throws (testing::TestError) {
5733 7400
    let programs = &[
5738 7405
        "fn run(s: &*[u8]) -> *u8 { return s.ptr; }",
5739 7406
        "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = &DATA[1..]; return *s.ptr; }",
5740 7407
        "fn run(s: *[u8]) -> u64 { return s.ptr as u64; }",
5741 7408
    ];
5742 7409
    for program in programs {
5743 -
        let mut a = testResolver();
5744 -
        let result = try resolveProgramStr(&mut a, program);
5745 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7410 +
        let mut testArena549 = testArena();
7411 +
        let testStorage549: 'test549 = &mut testArena549 in {
7412 +
            let mut a = testResolver(testStorage549);
7413 +
            let result = try resolveProgramStr(&mut a, program);
7414 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7415 +
        }
5746 7416
    }
5747 7417
    try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *u8 { return s.ptr; }");
5748 7418
    try expectAnalyzeOk("fn run(s: *[u8]) -> u64 { unsafe { return s.ptr as u64; } }");
5749 7419
    try expectAnalyzeOk("fn run(s: *[u8]) -> *u8 { return &s[0]; }");
5750 7420
    try expectAnalyzeOk("fn run(s: *[u8]) -> u32 { return s.len + s.cap; }");
5761 7431
        "unsafe static DATA: [u8; 1] = [42]; fn run() -> u8 { return DATA[0]; }",
5762 7432
        "unsafe static DATA: [u8; 1] = [42]; fn run() -> u32 { return DATA.len; }",
5763 7433
        "record R { value: u32 } unsafe static DATA: R = R { value: 7 }; fn run() -> u32 { return DATA.value; }",
5764 7434
    ];
5765 7435
    for program in programs {
5766 -
        let mut a = testResolver();
5767 -
        let result = try resolveProgramStr(&mut a, program);
5768 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7436 +
        let mut testArena550 = testArena();
7437 +
        let testStorage550: 'test550 = &mut testArena550 in {
7438 +
            let mut a = testResolver(testStorage550);
7439 +
            let result = try resolveProgramStr(&mut a, program);
7440 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7441 +
        }
5769 7442
    }
5770 7443
}
5771 7444
5772 7445
/// Unsafe blocks and functions can access unsafe statics.
5773 7446
@test unsafe fn testUnsafeStaticAccessAllowed() throws (testing::TestError) {
5783 7456
    let programs = &[
5784 7457
        "use root::storage; fn run() -> u32 { return storage::VALUE; }",
5785 7458
        "use root::storage::*; fn run() -> u32 { return VALUE; }",
5786 7459
    ];
5787 7460
    for program in programs {
5788 -
        let mut a = testResolver();
5789 -
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5790 -
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod storage; mod app;", &mut arena);
5791 -
        let _ = try registerModule(&mut MODULE_GRAPH, rootId, "storage", "export unsafe static VALUE: u32 = 7;", &mut arena);
5792 -
        let _ = try registerModule(&mut MODULE_GRAPH, rootId, "app", program, &mut arena);
5793 -
        let result = try resolveModuleTree(&mut a, rootId);
5794 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7461 +
        let mut testArena551 = testArena();
7462 +
        let testStorage551: 'test551 = &mut testArena551 in {
7463 +
            let mut a = testResolver(testStorage551);
7464 +
            let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
7465 +
            let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod storage; mod app;", &mut arena);
7466 +
            let _ = try registerModule(&mut MODULE_GRAPH, rootId, "storage", "export unsafe static VALUE: u32 = 7;", &mut arena);
7467 +
            let _ = try registerModule(&mut MODULE_GRAPH, rootId, "app", program, &mut arena);
7468 +
            let result = try resolveModuleTree(&mut a, rootId);
7469 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7470 +
        }
5795 7471
    }
5796 7472
}
5797 7473
5798 7474
/// An invalid unsafe static initializer does not grant permission to other initializers.
5799 7475
@test unsafe fn testUnsafeStaticInitializerRestoresContext() throws (testing::TestError) {
5800 -
    let mut a = testResolver();
5801 -
    let result = try resolveProgramStr(&mut a, "unsafe static BAD: u32 = true; static DATA: *[u64] = &[1 as u8] as *[u64];");
5802 -
    let _ = try expectError(&result);
5803 -
    try testing::expect(not a.inUnsafeContext);
7476 +
    let mut testArena552 = testArena();
7477 +
    let testStorage552: 'test552 = &mut testArena552 in {
7478 +
        let mut a = testResolver(testStorage552);
7479 +
        let result = try resolveProgramStr(&mut a, "unsafe static BAD: u32 = true; static DATA: *[u64] = &[1 as u8] as *[u64];");
7480 +
        let _ = try expectError(&result);
7481 +
        try testing::expect(not a.inUnsafeContext);
5804 7482
5805 -
    let mut b = testResolver();
5806 -
    let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];");
5807 -
    try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
7483 +
        let mut testArena553 = testArena();
7484 +
        let testStorage553: 'test553 = &mut testArena553 in {
7485 +
            let mut b = testResolver(testStorage553);
7486 +
            let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];");
7487 +
            try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
7488 +
        }
7489 +
    }
5808 7490
}
5809 7491
5810 7492
/// Uninitialized values require an explicit unsafe context in every value position.
5811 7493
@test unsafe fn testUndefinedRequiresUnsafe() throws (testing::TestError) {
5812 7494
    let programs = &[
5823 7505
        "constant P: *u8 = undefined;",
5824 7506
        "static DATA: [u8; 4] = undefined;",
5825 7507
        "record R: Copy { p: *u8 } static VALUE: R = R { p: undefined };",
5826 7508
    ];
5827 7509
    for program in programs {
5828 -
        let mut a = testResolver();
5829 -
        let result = try resolveProgramStr(&mut a, program);
5830 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7510 +
        let mut testArena554 = testArena();
7511 +
        let testStorage554: 'test554 = &mut testArena554 in {
7512 +
            let mut a = testResolver(testStorage554);
7513 +
            let result = try resolveProgramStr(&mut a, program);
7514 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7515 +
        }
5831 7516
    }
5832 7517
    try expectAnalyzeOk("unsafe fn run() { let p: *u8 = undefined; }");
5833 7518
    try expectAnalyzeOk("fn run() -> u32 { unsafe { let mut n: u32 = undefined; set n = 7; return n; } }");
5834 7519
    try expectAnalyzeOk("unsafe static P: *u8 = undefined;");
5835 7520
    try expectAnalyzeOk("record R: Copy { p: *u8 } unsafe static VALUE: R = R { p: undefined };");
5837 7522
    try expectAnalyzeOk("static DATA: [u8; 4] = [0; 4];");
5838 7523
}
5839 7524
5840 7525
/// Unsafe initialization does not grant permission to subsequent safe expressions.
5841 7526
@test unsafe fn testUndefinedContextRestored() throws (testing::TestError) {
5842 -
    let mut a = testResolver();
5843 -
    let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }");
5844 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5845 -
    let mut b = testResolver();
5846 -
    let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;");
5847 -
    try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
7527 +
    let mut testArena555 = testArena();
7528 +
    let testStorage555: 'test555 = &mut testArena555 in {
7529 +
        let mut a = testResolver(testStorage555);
7530 +
        let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }");
7531 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7532 +
        let mut testArena556 = testArena();
7533 +
        let testStorage556: 'test556 = &mut testArena556 in {
7534 +
            let mut b = testResolver(testStorage556);
7535 +
            let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;");
7536 +
            try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
7537 +
        }
7538 +
    }
5848 7539
}
5849 7540
5850 7541
/// Unsafe declarations may compose unsafe operations and calls.
5851 7542
@test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5852 7543
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }";
5859 7550
    try expectAnalyzeOk(program);
5860 7551
}
5861 7552
5862 7553
/// Dropping a reference to an unsafe pointer cannot add mutability.
5863 7554
@test unsafe fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5864 -
    let mut a = testResolver();
5865 -
    let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
5866 -
    let result = try resolveProgramStr(&mut a, program);
5867 -
    let err = try expectError(&result);
5868 -
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5869 -
        else throw testing::TestError::Failed;
7555 +
    let mut testArena557 = testArena();
7556 +
    let testStorage557: 'test557 = &mut testArena557 in {
7557 +
        let mut a = testResolver(testStorage557);
7558 +
        let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
7559 +
        let result = try resolveProgramStr(&mut a, program);
7560 +
        let err = try expectError(&result);
7561 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
7562 +
            else throw testing::TestError::Failed;
7563 +
    }
5870 7564
}
5871 7565
5872 7566
/// Recursive cast validation cannot hide a checked-to-unsafe transition.
5873 7567
@test unsafe fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5874 -
    let mut a = testResolver();
5875 -
    let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }";
5876 -
    let result = try resolveProgramStr(&mut a, program);
5877 -
    let err = try expectError(&result);
5878 -
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5879 -
        else throw testing::TestError::Failed;
7568 +
    let mut testArena558 = testArena();
7569 +
    let testStorage558: 'test558 = &mut testArena558 in {
7570 +
        let mut a = testResolver(testStorage558);
7571 +
        let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }";
7572 +
        let result = try resolveProgramStr(&mut a, program);
7573 +
        let err = try expectError(&result);
7574 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
7575 +
            else throw testing::TestError::Failed;
7576 +
    }
5880 7577
}
5881 7578
5882 7579
/// Unsafe code may drop a checked slice reference to an unsafe slice.
5883 7580
@test unsafe fn testUnsafeSliceFromReference() throws (testing::TestError) {
5884 7581
    let program = "record Marker: Once {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }";
5885 7582
    try expectAnalyzeOk(program);
5886 7583
}
5887 7584
5888 7585
/// Slice casts cannot add mutability.
5889 7586
@test unsafe fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5890 -
    let mut a = testResolver();
5891 -
    let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }";
5892 -
    let result = try resolveProgramStr(&mut a, program);
5893 -
    let err = try expectError(&result);
5894 -
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5895 -
        else throw testing::TestError::Failed;
7587 +
    let mut testArena559 = testArena();
7588 +
    let testStorage559: 'test559 = &mut testArena559 in {
7589 +
        let mut a = testResolver(testStorage559);
7590 +
        let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }";
7591 +
        let result = try resolveProgramStr(&mut a, program);
7592 +
        let err = try expectError(&result);
7593 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
7594 +
            else throw testing::TestError::Failed;
7595 +
    }
5896 7596
}
5897 7597
5898 7598
/// Mutable unsafe receivers do not create checked exclusive loans.
5899 7599
@test unsafe fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5900 7600
    let program = "record Marker: Once {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }";
5907 7607
    try expectAnalyzeOk(program);
5908 7608
}
5909 7609
5910 7610
/// Unsafe instance methods cannot implement safe trait contracts.
5911 7611
@test unsafe fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5912 -
    let mut a = testResolver();
5913 -
    let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
5914 -
    let result = try resolveProgramStr(&mut a, program);
5915 -
    try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
7612 +
    let mut testArena560 = testArena();
7613 +
    let testStorage560: 'test560 = &mut testArena560 in {
7614 +
        let mut a = testResolver(testStorage560);
7615 +
        let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
7616 +
        let result = try resolveProgramStr(&mut a, program);
7617 +
        try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
7618 +
    }
5916 7619
}
5917 7620
5918 7621
/// Unsafe trait methods retain their call-site requirement through dispatch.
5919 7622
@test unsafe fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5920 -
    let mut a = testResolver();
5921 -
    let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }";
5922 -
    let result = try resolveProgramStr(&mut a, program);
5923 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
7623 +
    let mut testArena561 = testArena();
7624 +
    let testStorage561: 'test561 = &mut testArena561 in {
7625 +
        let mut a = testResolver(testStorage561);
7626 +
        let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }";
7627 +
        let result = try resolveProgramStr(&mut a, program);
7628 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
7629 +
    }
5924 7630
}
5925 7631
5926 7632
/// An unsafe callback retains its requirement at an indirect call.
5927 7633
@test unsafe fn testUnsafeCallbackCallRejected() throws (testing::TestError) {
5928 -
    let mut a = testResolver();
5929 -
    let result = try resolveProgramStr(&mut a,
5930 -
        "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }");
5931 -
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
7634 +
    let mut testArena562 = testArena();
7635 +
    let testStorage562: 'test562 = &mut testArena562 in {
7636 +
        let mut a = testResolver(testStorage562);
7637 +
        let result = try resolveProgramStr(&mut a,
7638 +
            "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }");
7639 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
7640 +
    }
5932 7641
}
5933 7642
5934 7643
/// An unsafe function cannot enter a safe callback slot.
5935 7644
@test unsafe fn testUnsafeCallbackAssignmentRejected() throws (testing::TestError) {
5936 -
    let mut a = testResolver();
5937 -
    let result = try resolveProgramStr(&mut a,
5938 -
        "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }");
5939 -
    let err = try expectError(&result);
5940 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5941 -
        else throw testing::TestError::Failed;
7645 +
    let mut testArena563 = testArena();
7646 +
    let testStorage563: 'test563 = &mut testArena563 in {
7647 +
        let mut a = testResolver(testStorage563);
7648 +
        let result = try resolveProgramStr(&mut a,
7649 +
            "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }");
7650 +
        let err = try expectError(&result);
7651 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
7652 +
            else throw testing::TestError::Failed;
7653 +
    }
5942 7654
}
5943 7655
5944 7656
/// A safe function can enter an unsafe callback slot.
5945 7657
@test unsafe fn testSafeCallbackIntoUnsafeSlot() throws (testing::TestError) {
5946 7658
    try expectAnalyzeOk(
5947 7659
        "fn load() -> u32 { return 1; } unsafe fn run() -> u32 { let callback: unsafe fn() -> u32 = load; return callback(); }");
5948 7660
}
5949 7661
5950 7662
/// Borrowed callback storage must preserve its function safety type.
5951 7663
@test unsafe fn testBorrowedCallbackSafetyInvariant() throws (testing::TestError) {
5952 -
    let mut a = testResolver();
5953 -
    let result = try resolveProgramStr(&mut a,
5954 -
        "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }");
5955 -
    let err = try expectError(&result);
5956 -
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5957 -
        else throw testing::TestError::Failed;
7664 +
    let mut testArena564 = testArena();
7665 +
    let testStorage564: 'test564 = &mut testArena564 in {
7666 +
        let mut a = testResolver(testStorage564);
7667 +
        let result = try resolveProgramStr(&mut a,
7668 +
            "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }");
7669 +
        let err = try expectError(&result);
7670 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
7671 +
            else throw testing::TestError::Failed;
7672 +
    }
5958 7673
}
5959 7674
5960 7675
/// Referenced storage must preserve its exact element type in every context.
5961 7676
@test unsafe fn testPointerStorageCoercionsRejected() throws (testing::TestError) {
5962 7677
    let programs = &[
5968 7683
        "fn take(p: *[?*u8]) {} fn run(p: *[*u8]) { take(p); }",
5969 7684
        "unsafe fn take(p: *mut [?*u8]) {} unsafe fn run(p: *mut [*u8]) { take(p); }",
5970 7685
        "fn take(p: &mut **u8) {} fn run(p: &mut *mut *u8) { take(p); }",
5971 7686
    ];
5972 7687
    for program in programs {
5973 -
        let mut a = testResolver();
5974 -
        let result = try resolveProgramStr(&mut a, program);
5975 -
        let err = try expectError(&result);
5976 -
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5977 -
            else throw testing::TestError::Failed;
7688 +
        let mut testArena565 = testArena();
7689 +
        let testStorage565: 'test565 = &mut testArena565 in {
7690 +
            let mut a = testResolver(testStorage565);
7691 +
            let result = try resolveProgramStr(&mut a, program);
7692 +
            let err = try expectError(&result);
7693 +
            let case super::ErrorKind::TypeMismatch(_) = err.kind
7694 +
                else throw testing::TestError::Failed;
7695 +
        }
5978 7696
    }
5979 7697
    try expectAnalyzeOk("fn take(p: &mut ?*u8) {} fn run() { let mut p: ?*u8 = nil; take(&mut p); }");
5980 7698
    try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p; }");
5981 7699
    try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p; }");
5982 7700
}
5990 7708
        "unsafe fn run(p: *mut ?*u8) { p as *mut *u8; }",
5991 7709
        "unsafe fn run(p: *unsafe mut *u8) { p as *unsafe mut ?*u8; }",
5992 7710
        "unsafe fn run(p: *mut [*u8]) { p as *mut [?*u8]; }",
5993 7711
    ];
5994 7712
    for program in programs {
5995 -
        let mut a = testResolver();
5996 -
        let result = try resolveProgramStr(&mut a, program);
5997 -
        let err = try expectError(&result);
5998 -
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5999 -
            else throw testing::TestError::Failed;
7713 +
        let mut testArena566 = testArena();
7714 +
        let testStorage566: 'test566 = &mut testArena566 in {
7715 +
            let mut a = testResolver(testStorage566);
7716 +
            let result = try resolveProgramStr(&mut a, program);
7717 +
            let err = try expectError(&result);
7718 +
            let case super::ErrorKind::InvalidAsCast(_) = err.kind
7719 +
                else throw testing::TestError::Failed;
7720 +
        }
6000 7721
    }
6001 7722
}
6002 7723
6003 7724
/// Pattern references prevent replacement of their source in every context.
6004 7725
@test unsafe fn testPatternLoanMutationRejected() throws (testing::TestError) {
6008 7729
        "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &mut u { case U::A(p) => { unsafe { set u = U::B(1); } *p; } else => {} } }",
6009 7730
        "union U: Copy { A(u64), B(u64) } fn change(u: &mut U) { set *u = U::B(1); } fn run() { let mut u = U::A(7); match &u { case U::A(p) => { change(&mut u); *p; } else => {} } }",
6010 7731
        "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); if let case U::A(p) = &u { set u = U::B(1); *p; } }",
6011 7732
        "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); while let case U::A(p) = &u { set u = U::B(1); *p; } }",
6012 7733
        "union U: Copy { A(u64), B(u64) } fn run(u: *mut U) { match u { case U::A(p) => { let moved = u; *p; } else => {} } }",
7734 +
        "record R: 'r + Copy { p: &'r u32 } fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }",
7735 +
        "record R: 'r + Copy { p: &'r u32 } unsafe fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }",
6013 7736
    ];
6014 7737
    for program in programs {
6015 -
        let mut a = testResolver();
6016 -
        let result = try resolveProgramStr(&mut a, program);
6017 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7738 +
        let mut testArena567 = testArena();
7739 +
        let testStorage567: 'test567 = &mut testArena567 in {
7740 +
            let mut a = testResolver(testStorage567);
7741 +
            let result = try resolveProgramStr(&mut a, program);
7742 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7743 +
        }
6018 7744
    }
6019 7745
}
6020 7746
6021 7747
/// Pattern loans end at their scope and permit writes through mutable payload references.
6022 7748
@test unsafe fn testPatternLoanScopeAllowed() throws (testing::TestError) {
6030 7756
    let programs = &[
6031 7757
        "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }",
6032 7758
        "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } unsafe fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }",
6033 7759
    ];
6034 7760
    for program in programs {
6035 -
        let mut a = testResolver();
6036 -
        let result = try resolveProgramStr(&mut a, program);
6037 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7761 +
        let mut testArena568 = testArena();
7762 +
        let testStorage568: 'test568 = &mut testArena568 in {
7763 +
            let mut a = testResolver(testStorage568);
7764 +
            let result = try resolveProgramStr(&mut a, program);
7765 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7766 +
        }
6038 7767
    }
6039 7768
}
6040 7769
6041 7770
/// Guards, nested patterns, and unsafe calls must preserve pattern source storage.
6042 7771
@test unsafe fn testPatternLoanIndirectMutationRejected() throws (testing::TestError) {
6047 7776
        "union U: Copy { A(u64), B(u64) } record R: Copy { value: U } fn run() { let mut u = R { value: U::A(7) }; match &u.value { case U::A(p) => { set u.value = U::B(1); *p; } else => {} } }",
6048 7777
        "union U: Copy { A(u64), B(u64) } fn run() { let mut u = [U::A(7)]; match &u[0] { case U::A(p) => { set u[0] = U::B(1); *p; } else => {} } }",
6049 7778
        "union U: Copy { A(u64), B(u64) } unsafe fn (u: *unsafe mut U) change() { set *u = U::B(1); } unsafe fn run(u: *unsafe mut U) { match u { case U::A(p) => { u.change(); *p; } else => {} } }",
6050 7779
    ];
6051 7780
    for program in programs {
6052 -
        let mut a = testResolver();
6053 -
        let result = try resolveProgramStr(&mut a, program);
6054 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7781 +
        let mut testArena569 = testArena();
7782 +
        let testStorage569: 'test569 = &mut testArena569 in {
7783 +
            let mut a = testResolver(testStorage569);
7784 +
            let result = try resolveProgramStr(&mut a, program);
7785 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u"));
7786 +
        }
6055 7787
    }
6056 7788
}
6057 7789
6058 7790
/// Binding mutability does not permit writes through immutable pointers.
6059 7791
@test unsafe fn testMutableBindingImmutableTargetRejected() throws (testing::TestError) {
6065 7797
        "static DATA: [u8; 1] = [0]; fn run() { let mut p = &DATA; let q = &mut p[0]; }",
6066 7798
        "record R: Copy { n: u8 } fn (r: &mut R) change() { set r.n = 1; } fn run(input: *R) { let mut p = input; p.change(); }",
6067 7799
        "fn run(p: *mut u8, q: *mut u8) { set p = q; }",
6068 7800
    ];
6069 7801
    for program in programs {
6070 -
        let mut a = testResolver();
6071 -
        let result = try resolveProgramStr(&mut a, program);
6072 -
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
7802 +
        let mut testArena570 = testArena();
7803 +
        let testStorage570: 'test570 = &mut testArena570 in {
7804 +
            let mut a = testResolver(testStorage570);
7805 +
            let result = try resolveProgramStr(&mut a, program);
7806 +
            try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
7807 +
        }
6073 7808
    }
6074 7809
}
6075 7810
6076 7811
/// Mutable targets support access through fixed and mutable pointer bindings.
6077 7812
@test unsafe fn testMutablePointerTargetsAllowed() throws (testing::TestError) {
6087 7822
        "union U: Copy { A(u8), B } fn run(p: *unsafe U) { if let case U::A(n) = p { *n; } }",
6088 7823
        "union U: Copy { A(u8), B } fn run(p: *unsafe U) { while let case U::A(n) = p { *n; break; } }",
6089 7824
        "record R: Copy { n: u8 } fn run(p: *unsafe R) { let case R { n } = p else return; }",
6090 7825
    ];
6091 7826
    for program in programs {
6092 -
        let mut a = testResolver();
6093 -
        let result = try resolveProgramStr(&mut a, program);
6094 -
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7827 +
        let mut testArena571 = testArena();
7828 +
        let testStorage571: 'test571 = &mut testArena571 in {
7829 +
            let mut a = testResolver(testStorage571);
7830 +
            let result = try resolveProgramStr(&mut a, program);
7831 +
            try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
7832 +
        }
6095 7833
    }
6096 7834
}
6097 7835
6098 7836
/// Unsafe contexts permit pattern access through raw pointers.
6099 7837
@test unsafe fn testRawPointerPatternsAllowed() throws (testing::TestError) {
6111 7849
        "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6112 7850
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6113 7851
        "union E: Copy { Bad } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6114 7852
    ];
6115 7853
    for program in programs {
6116 -
        let mut a = testResolver();
6117 -
        let result = try resolveProgramStr(&mut a, program);
6118 -
        try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator);
7854 +
        let mut testArena572 = testArena();
7855 +
        let testStorage572: 'test572 = &mut testArena572 in {
7856 +
            let mut a = testResolver(testStorage572);
7857 +
            let result = try resolveProgramStr(&mut a, program);
7858 +
            try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator);
7859 +
        }
6119 7860
    }
6120 7861
}
6121 7862
6122 7863
/// Slice allocators can use raw context pointers.
6123 7864
@test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) {
6131 7872
        "union U: Copy { A(*u8), B(u64) } fn run(p: *mut U) -> u8 { let q = p; match p { case U::A(n) => { set *q = U::B(1); return **n; } else => return 0, } }",
6132 7873
        "union U: Copy { A(u8), B } fn change(p: &mut U) { set *p = U::B; } fn run(p: *mut U) { let q = p; if let case U::A(n) = p { change(q); *n; } }",
6133 7874
        "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; let r = q; while let case U::A(n) = p { set *r = U::B; *n; break; } }",
6134 7875
    ];
6135 7876
    for program in programs {
6136 -
        let mut a = testResolver();
6137 -
        let result = try resolveProgramStr(&mut a, program);
6138 -
        let _ = try expectError(&result);
7877 +
        let mut testArena573 = testArena();
7878 +
        let testStorage573: 'test573 = &mut testArena573 in {
7879 +
            let mut a = testResolver(testStorage573);
7880 +
            let result = try resolveProgramStr(&mut a, program);
7881 +
            let _ = try expectError(&result);
7882 +
        }
6139 7883
    }
6140 7884
}
6141 7885
6142 7886
/// Every access to a moved safe mutable pointer is rejected.
6143 7887
@test unsafe fn testMutablePointerMovesRejected() throws (testing::TestError) {
6153 7897
        "trait T { fn (&T) read(); } fn run(p: *mut opaque T) { let q = p; p.read(); }",
6154 7898
        "fn run(p: *mut u8, flag: bool) { if flag { let q = p; } *p; }",
6155 7899
        "fn run(p: *mut u8) { loop { let q = p; } }",
6156 7900
    ];
6157 7901
    for program in programs {
6158 -
        let mut a = testResolver();
6159 -
        let result = try resolveProgramStr(&mut a, program);
6160 -
        let _ = try expectError(&result);
7902 +
        let mut testArena574 = testArena();
7903 +
        let testStorage574: 'test574 = &mut testArena574 in {
7904 +
            let mut a = testResolver(testStorage574);
7905 +
            let result = try resolveProgramStr(&mut a, program);
7906 +
            let _ = try expectError(&result);
7907 +
        }
6161 7908
    }
6162 7909
}
6163 7910
6164 7911
/// Copy composites cannot contain safe mutable owners or their containers.
6165 7912
@test unsafe fn testCopyMutablePointerFieldsRejected() throws (testing::TestError) {
6171 7918
        "record R: Copy { p: *mut [u8] }",
6172 7919
        "trait T {} record R: Copy { p: *mut opaque T }",
6173 7920
        "record Inner { p: *mut u8 } record Outer: Copy { inner: Inner }",
6174 7921
    ];
6175 7922
    for program in programs {
6176 -
        let mut a = testResolver();
6177 -
        let result = try resolveProgramStr(&mut a, program);
6178 -
        try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
7923 +
        let mut testArena575 = testArena();
7924 +
        let testStorage575: 'test575 = &mut testArena575 in {
7925 +
            let mut a = testResolver(testStorage575);
7926 +
            let result = try resolveProgramStr(&mut a, program);
7927 +
            try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
7928 +
        }
6179 7929
    }
6180 7930
}
6181 7931
6182 7932
/// Moves transfer safe mutable pointers, and calls can borrow them temporarily.
6183 7933
@test unsafe fn testMutablePointerMovesAllowed() throws (testing::TestError) {
6188 7938
    try expectAnalyzeOk("record R: Copy { p: *unsafe mut u8 } unsafe fn run(p: *unsafe mut u8) { let q = p; set *p = 1; set *q = 2; }");
6189 7939
    try expectAnalyzeOk("record R: Copy { p: *unsafe mut [u8] } unsafe fn run(p: *unsafe mut [u8]) { let q = p; set p[0] = 1; set q[0] = 2; }");
6190 7940
}
6191 7941
6192 7942
/// Function signature inspection uses immutable descriptors in safe code.
6193 -
fn expectImmutableFunctionSignatures(res: &super::Resolver, root: *ast::Node) throws (testing::TestError) {
7943 +
fn expectImmutableFunctionSignatures 'arena (res: &super::Resolver 'arena, root: *ast::Node) throws (testing::TestError) {
6194 7944
    let first = try typeOf(res, try getBlockStmt(root, 0));
6195 7945
    let equivalent = try typeOf(res, try getBlockStmt(root, 1));
6196 7946
    let throwing = try typeOf(res, try getBlockStmt(root, 2));
6197 7947
    let unsafeCall = try typeOf(res, try getBlockStmt(root, 3));
6198 7948
    try testing::expect(super::typesEqual(first, equivalent));
6200 7950
    try testing::expect(not super::typesEqual(first, unsafeCall));
6201 7951
}
6202 7952
6203 7953
/// Local analysis state does not change function signature identity.
6204 7954
@test unsafe fn testImmutableFunctionSignatures() throws (testing::TestError) {
6205 -
    let mut res = testResolver();
6206 -
    let result = try resolveProgramStr(&mut res,
6207 -
        "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }");
6208 -
    try expectNoErrors(&result);
6209 -
    try expectImmutableFunctionSignatures(&res, result.root);
7955 +
    let mut testArena576 = testArena();
7956 +
    let testStorage576: 'test576 = &mut testArena576 in {
7957 +
        let mut res = testResolver(testStorage576);
7958 +
        let result = try resolveProgramStr(&mut res,
7959 +
            "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }");
7960 +
        try expectNoErrors(&result);
7961 +
        try expectImmutableFunctionSignatures(&res, result.root);
7962 +
    }
6210 7963
}
6211 7964
6212 7965
/// A diagnostic snapshot keeps its contents when the resolver buffer changes.
6213 7966
@test unsafe fn testDiagnosticSnapshotOwnsItsErrors() throws (testing::TestError) {
6214 -
    let mut res = testResolver();
6215 -
    let result = try resolveProgramStr(&mut res, "fn run() { missing; }");
6216 -
    let snapshot = result.diagnostics;
6217 -
    let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
6218 -
    set res.errors.entries[0] = super::Error {
6219 -
        kind: super::ErrorKind::Internal, node: nil, moduleId: 0,
6220 -
    };
6221 -
    let later = super::diagnostics(&mut res);
6222 -
    try testing::expect(snapshot.errors.len == 1);
6223 -
    try testing::expect(later.errors.len == 1);
6224 -
    let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
6225 -
    try testing::expect(first.kind == original.kind);
6226 -
    try testing::expect(later.errors[0].kind == super::ErrorKind::Internal);
6227 -
    try testing::expect(super::errorAt(snapshot.errors, 1) == nil);
7967 +
    let mut testArena577 = testArena();
7968 +
    let testStorage577: 'test577 = &mut testArena577 in {
7969 +
        let mut res = testResolver(testStorage577);
7970 +
        let result = try resolveProgramStr(&mut res, "fn run() { missing; }");
7971 +
        let snapshot = result.diagnostics;
7972 +
        let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
7973 +
        set res.errors.entries[0] = super::Error {
7974 +
            kind: super::ErrorKind::Internal, node: nil, moduleId: 0,
7975 +
        };
7976 +
        let later = super::diagnostics(&mut res);
7977 +
        try testing::expect(snapshot.errors.len == 1);
7978 +
        try testing::expect(later.errors.len == 1);
7979 +
        let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
7980 +
        try testing::expect(first.kind == original.kind);
7981 +
        try testing::expect(later.errors[0].kind == super::ErrorKind::Internal);
7982 +
        try testing::expect(super::errorAt(snapshot.errors, 1) == nil);
7983 +
    }
6228 7984
}
6229 7985
6230 7986
/// Prefixes, equal fields, and uncertain element locations must conflict.
6231 7987
@test unsafe fn testOverlappingFieldBorrows() throws (testing::TestError) {
6232 7988
    let programs = &[
6234 7990
        "record R: Copy { a: u32, b: u32 } fn take(a: &mut R, b: &u32) {} fn run(r: &mut R) { take(r, &r.b); }",
6235 7991
        "record R: Copy { a: [u8; 2], b: [u8; 2] } fn take(a: &mut u8, b: &u8) {} fn run(r: &mut R) { take(&mut r.a[0], &r.a[1]); }",
6236 7992
        "record R: Copy { a: u32, b: u32 } fn take(a: &mut u32, b: u32) {} fn run(r: &mut R) { take(&mut r.a, r.a); }",
6237 7993
    ];
6238 7994
    for program in programs {
6239 -
        let mut a = testResolver();
6240 -
        let result = try resolveProgramStr(&mut a, program);
6241 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
7995 +
        let mut testArena578 = testArena();
7996 +
        let testStorage578: 'test578 = &mut testArena578 in {
7997 +
            let mut a = testResolver(testStorage578);
7998 +
            let result = try resolveProgramStr(&mut a, program);
7999 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
8000 +
        }
6242 8001
    }
6243 8002
}
6244 8003
6245 8004
/// Local loans exclude writes, competing references, and source reads.
6246 8005
@test unsafe fn testLocalReferenceConflicts() throws (testing::TestError) {
6254 8013
        "fn take(p: &mut u32) {} fn run() { let mut n: u32 = 4; let p = &n; take(&mut n); }",
6255 8014
        "fn run() { let mut n: u32 = 4; let p = &n; unsafe { set n = 1; } }",
6256 8015
        "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &*p; let moved = p; }",
6257 8016
    ];
6258 8017
    for program in programs {
6259 -
        let mut a = testResolver();
6260 -
        let result = try resolveProgramStr(&mut a, program);
6261 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n"));
8018 +
        let mut testArena579 = testArena();
8019 +
        let testStorage579: 'test579 = &mut testArena579 in {
8020 +
            let mut a = testResolver(testStorage579);
8021 +
            let result = try resolveProgramStr(&mut a, program);
8022 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n"));
8023 +
        }
6262 8024
    }
6263 8025
}
6264 8026
6265 8027
/// Reference bindings cannot outlive temporary storage or change their source.
6266 8028
@test unsafe fn testLocalReferenceStorage() throws (testing::TestError) {
6267 8029
    let programs = &[
8030 +
        "fn f 'r (p: &'r mut u32) { let mut cursor = p; }",
8031 +
        "unsafe fn f 'r (p: &'r mut u32) { let mut cursor = p; }",
6268 8032
        "fn run() { let n: u32 = 4; let mut p: &u32 = &n; }",
6269 8033
        "record R: Copy { n: u32 } fn make() -> R { return R { n: 4 }; } fn run() { let p: &u32 = &make().n; }",
6270 8034
    ];
6271 8035
    for program in programs {
6272 -
        let mut a = testResolver();
6273 -
        let result = try resolveProgramStr(&mut a, program);
6274 -
        try expectErrorKind(&result, super::ErrorKind::RefBinding);
8036 +
        let mut testArena580 = testArena();
8037 +
        let testStorage580: 'test580 = &mut testArena580 in {
8038 +
            let mut a = testResolver(testStorage580);
8039 +
            let result = try resolveProgramStr(&mut a, program);
8040 +
            try expectErrorKind(&result, super::ErrorKind::RefBinding);
8041 +
        }
6275 8042
    }
6276 8043
}
6277 8044
6278 8045
/// Stored and returned values cannot contain a local reference.
6279 8046
@test unsafe fn testLocalReferenceEscapeRejected() throws (testing::TestError) {
6282 8049
        "fn run() -> *u32 { let n: u32 = 1; let p = &n; return p as *u32; }",
6283 8050
        "static DATA: u32 = 0; static P: *u32 = &DATA; fn run() { let n: u32 = 1; let p = &n; set P = p; }",
6284 8051
        "record R: Copy { p: *u32 } fn run() { let n: u32 = 1; let p = &n; let r = R { p }; }",
6285 8052
    ];
6286 8053
    for program in programs {
6287 -
        let mut a = testResolver();
6288 -
        let result = try resolveProgramStr(&mut a, program);
6289 -
        let _ = try expectError(&result);
8054 +
        let mut testArena581 = testArena();
8055 +
        let testStorage581: 'test581 = &mut testArena581 in {
8056 +
            let mut a = testResolver(testStorage581);
8057 +
            let result = try resolveProgramStr(&mut a, program);
8058 +
            let _ = try expectError(&result);
8059 +
        }
6290 8060
    }
6291 8061
}
6292 8062
6293 8063
/// Pointer indirection cannot prove that sibling pointees are disjoint.
6294 8064
@test unsafe fn testIndirectFieldBorrowConflict() throws (testing::TestError) {
6295 8065
    let programs = &[
6296 8066
        "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } fn take(a: &mut u32, b: &mut u32) {} unsafe fn run(r: &mut R) { take(&mut *r.a, &mut *r.b); }",
6297 8067
        "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } unsafe fn run(r: &mut R) { let a: &mut u32 = &mut *r.a; let b: &mut u32 = &mut *r.b; }",
6298 8068
    ];
6299 8069
    for program in programs {
6300 -
        let mut a = testResolver();
6301 -
        let result = try resolveProgramStr(&mut a, program);
6302 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
8070 +
        let mut testArena582 = testArena();
8071 +
        let testStorage582: 'test582 = &mut testArena582 in {
8072 +
            let mut a = testResolver(testStorage582);
8073 +
            let result = try resolveProgramStr(&mut a, program);
8074 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
8075 +
        }
6303 8076
    }
6304 8077
}
6305 8078
6306 8079
/// Pattern references protect their field and permit writes to disjoint fields.
6307 8080
@test unsafe fn testDisjointPatternFieldBorrow() throws (testing::TestError) {
6308 -
    let mut a = testResolver();
6309 -
    let result = try resolveProgramStr(&mut a,
6310 -
        "union U: Copy { A(u32), B } record R: Copy { u: U, n: u32 } fn run(r: &mut R) { let alias = &mut r.u; match alias { case U::A(p) => { set *alias = U::B; *p; } else => {} } }");
6311 -
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
8081 +
    let mut testArena583 = testArena();
8082 +
    let testStorage583: 'test583 = &mut testArena583 in {
8083 +
        let mut a = testResolver(testStorage583);
8084 +
        let result = try resolveProgramStr(&mut a,
8085 +
            "union U: Copy { A(u32), B } record R: Copy { u: U, n: u32 } fn run(r: &mut R) { let alias = &mut r.u; match alias { case U::A(p) => { set *alias = U::B; *p; } else => {} } }");
8086 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r"));
8087 +
    }
6312 8088
}
6313 8089
6314 8090
/// Slice mutations must preserve storage held by a local reference.
6315 8091
@test unsafe fn testLocalReferenceSliceMutationRejected() throws (testing::TestError) {
6316 8092
    let programs = &[
6317 8093
        "fn run(s: *mut [u8]) { let p: &u8 = &s[0]; s.delete(0); }",
6318 8094
        "record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { let p: &u8 = &s[0]; s.append(1, a); }",
6319 8095
    ];
6320 8096
    for program in programs {
6321 -
        let mut a = testResolver();
6322 -
        let result = try resolveProgramStr(&mut a, program);
6323 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s"));
8097 +
        let mut testArena584 = testArena();
8098 +
        let testStorage584: 'test584 = &mut testArena584 in {
8099 +
            let mut a = testResolver(testStorage584);
8100 +
            let result = try resolveProgramStr(&mut a, program);
8101 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s"));
8102 +
        }
6324 8103
    }
6325 8104
}
6326 8105
6327 8106
/// Owner moves and mutable methods cannot invalidate a local loan.
6328 8107
@test unsafe fn testLocalReferenceOwnerMutationRejected() throws (testing::TestError) {
6329 8108
    let programs = &[
6330 8109
        "fn run(p: *mut u32) { let r: &u32 = &*p; let moved = p; }",
6331 8110
        "record R: Copy { a: u32 } fn (p: &mut R) change() { set p.a = 1; } fn run(p: &mut R) { let r = &p.a; p.change(); }",
6332 8111
    ];
6333 8112
    for program in programs {
6334 -
        let mut a = testResolver();
6335 -
        let result = try resolveProgramStr(&mut a, program);
6336 -
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p"));
8113 +
        let mut testArena585 = testArena();
8114 +
        let testStorage585: 'test585 = &mut testArena585 in {
8115 +
            let mut a = testResolver(testStorage585);
8116 +
            let result = try resolveProgramStr(&mut a, program);
8117 +
            try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p"));
8118 +
        }
6337 8119
    }
6338 8120
}
6339 8121
6340 8122
/// Reference locals require a function scope and a reachable initializer result.
6341 8123
@test unsafe fn testLocalReferenceDeclarationContext() throws (testing::TestError) {
6342 -
    let mut a = testResolver();
6343 -
    let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;");
6344 -
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
8124 +
    let mut testArena586 = testArena();
8125 +
    let testStorage586: 'test586 = &mut testArena586 in {
8126 +
        let mut a = testResolver(testStorage586);
8127 +
        let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;");
8128 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
8129 +
    }
8130 +
}
8131 +
8132 +
/// Test-only imports do not require their modules in a normal build.
8133 +
@test unsafe fn testConditionalUse() throws (testing::TestError) {
8134 +
    let mut testArena587 = testArena();
8135 +
    let testStorage587: 'test587 = &mut testArena587 in {
8136 +
        let mut a = testResolver(testStorage587);
8137 +
        set a.config.buildTest = false;
8138 +
        let source = "@test use missing::testing; fn value() -> u32 { return 1; }";
8139 +
        let result = try resolveProgramStr(&mut a, source);
8140 +
        try expectNoErrors(&result);
8141 +
        set a.config.buildTest = true;
8142 +
        let enabled = try resolveProgramStr(&mut a, source);
8143 +
        try testing::expect(enabled.diagnostics.errors.len > 0);
8144 +
    }
8145 +
}
8146 +
8147 +
/// Resolver metadata retains its caller's arena when the context moves.
8148 +
unsafe fn relocateResolver 'arena (context: super::Resolver 'arena) -> super::Resolver 'arena {
8149 +
    return context;
8150 +
}
8151 +
8152 +
@test unsafe fn testResolverRelocation() throws (testing::TestError) {
8153 +
    static DATA: [u8; 65536] = [0; 65536];
8154 +
    static AST_DATA: [u8; 8192] = [0; 8192];
8155 +
    static NODES: [super::NodeData; 128] = undefined;
8156 +
    static ERRORS: [super::Error; 8] = undefined;
8157 +
    static SCOPE: super::Scope = undefined;
8158 +
    static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
8159 +
    let mut arena = alloc::new(&mut DATA[..]);
8160 +
    let mut astArena = ast::nodeArena(&mut AST_DATA[..]);
8161 +
    let root = try! parser::parse(scanner::SourceLoc::String,
8162 +
        "fn answer() -> u32 { return 42; }", &mut astArena, &mut POOL);
8163 +
    let storage = super::ResolverStorage {
8164 +
        nodeData: &mut NODES[..], pkgScope: &mut SCOPE, errors: &mut ERRORS[..],
8165 +
    };
8166 +
    let arenaRef: 'arena = &mut arena in {
8167 +
        let context = super::resolver(arenaRef, storage, super::Config { buildTest: false });
8168 +
        let initialized = alloc::used(context.arena);
8169 +
        try testing::expect(initialized > 0);
8170 +
        let mut moved = relocateResolver(context);
8171 +
        let diagnostics = try! super::resolveModuleRoot(&mut moved, root);
8172 +
        try testing::expect(super::success(&diagnostics));
8173 +
        try testing::expect(alloc::used(moved.arena) > initialized);
8174 +
        try testing::expect(moved.arena.offset == alloc::used(moved.arena));
8175 +
        let output = try! alloc::alloc(&mut *moved.arena, @sizeOf(u32), @alignOf(u32)) as *mut u32;
8176 +
        set *output = 42;
8177 +
        try testing::expect(*output == 42);
8178 +
    }
6345 8179
}
lib/std/lang/resolver/tests/regions.rad added +1251 -0
1 +
//! Lexical region resolver tests.
2 +
3 +
use std::mem;
4 +
use std::testing;
5 +
use std::lang::types;
6 +
use std::lang::resolver;
7 +
8 +
/// Equal region spellings in separate functions have distinct identities.
9 +
@test unsafe fn testRegionIdentities() throws (testing::TestError) {
10 +
    let mut testArena1 = super::testArena();
11 +
    let testStorage1: 'test1 = &mut testArena1 in {
12 +
        let mut res = super::testResolver(testStorage1);
13 +
        let result = try super::resolveProgramStr(&mut res,
14 +
            "fn first 'r (p: &'r u8) -> u8 { return *p; }\nfn second 'r (p: &'r u8) -> u8 { return *p; }"
15 +
        );
16 +
        try super::expectNoErrors(&result);
17 +
        let first = try super::getBlockStmt(result.root, 0);
18 +
        let second = try super::getBlockStmt(result.root, 1);
19 +
        let firstType = resolver::typeFor(&res, first) else throw testing::TestError::Failed;
20 +
        let secondType = resolver::typeFor(&res, second) else throw testing::TestError::Failed;
21 +
        let case resolver::Type::Fn(a) = firstType else throw testing::TestError::Failed;
22 +
        let case resolver::Type::Fn(b) = secondType else throw testing::TestError::Failed;
23 +
        let case resolver::Type::Pointer { class: types::PointerClass::Region(ar), .. } = *a.paramTypes[0]
24 +
            else throw testing::TestError::Failed;
25 +
        let case resolver::Type::Pointer { class: types::PointerClass::Region(br), .. } = *b.paramTypes[0]
26 +
            else throw testing::TestError::Failed;
27 +
        try testing::expect(mem::eq(ar.name, br.name));
28 +
        try testing::expect(ar.id <> br.id);
29 +
        try testing::expect(not resolver::typesEqual(*a.paramTypes[0], *b.paramTypes[0]));
30 +
        try testing::expect(resolver::getTypeLayout(*a.paramTypes[0]).size == 8);
31 +
    }
32 +
}
33 +
34 +
/// Parent links resolve forward declarations and preserve transitive ancestry.
35 +
@test unsafe fn testRegionParents() throws (testing::TestError) {
36 +
    let mut testArena2 = super::testArena();
37 +
    let testStorage2: 'test2 = &mut testArena2 in {
38 +
        let mut res = super::testResolver(testStorage2);
39 +
        let result = try super::resolveProgramStr(&mut res,
40 +
            "fn read 'child 'middle 'root (p: &'child u8, q: &'root u8) where 'middle: 'child, 'root: 'middle {}"
41 +
        );
42 +
        try super::expectNoErrors(&result);
43 +
        let func = try super::getBlockStmt(result.root, 0);
44 +
        let ty = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
45 +
        let case resolver::Type::Fn(info) = ty else throw testing::TestError::Failed;
46 +
        let case resolver::Type::Pointer { class: types::PointerClass::Region(child), .. } = *info.paramTypes[0]
47 +
            else throw testing::TestError::Failed;
48 +
        let case resolver::Type::Pointer { class: types::PointerClass::Region(root), .. } = *info.paramTypes[1]
49 +
            else throw testing::TestError::Failed;
50 +
        try testing::expect(types::regionContains(root, child));
51 +
        try testing::expect(types::regionContains(child, child));
52 +
        try testing::expect(not types::regionContains(child, root));
53 +
    }
54 +
}
55 +
56 +
/// Region declarations reject duplicate names, missing parents, and cycles.
57 +
@test unsafe fn testInvalidRegionDeclarations() throws (testing::TestError) {
58 +
    for program in [
59 +
        "fn f 'r 'r () {}",
60 +
        "fn f 'r () where 'missing: 'r {}",
61 +
        "fn f 'r () where 'r: 'r {}",
62 +
        "fn f 'a 'b 'c () where 'b: 'a, 'c: 'b, 'a: 'c {}",
63 +
    ] {
64 +
        let mut testArena3 = super::testArena();
65 +
        let testStorage3: 'test3 = &mut testArena3 in {
66 +
            let mut res = super::testResolver(testStorage3);
67 +
            let result = try super::resolveProgramStr(&mut res, program);
68 +
            let _ = try super::expectError(&result);
69 +
        }
70 +
    }
71 +
}
72 +
73 +
/// Regions from another declaration cannot enter a function signature or body.
74 +
@test unsafe fn testUnknownRegionNames() throws (testing::TestError) {
75 +
    for program in [
76 +
        "fn f(p: &'missing u8) {}",
77 +
        "fn f 'r (p: &'r u8) {} fn g(p: &'r u8) {}",
78 +
        "unsafe fn f(p: &'missing u8) {}",
79 +
        "fn f 'r (p: &'r u8) { let q: &'missing u8 = p; }",
80 +
    ] {
81 +
        let mut testArena4 = super::testArena();
82 +
        let testStorage4: 'test4 = &mut testArena4 in {
83 +
            let mut res = super::testResolver(testStorage4);
84 +
            let result = try super::resolveProgramStr(&mut res, program);
85 +
            let err = try super::expectError(&result);
86 +
            let case resolver::ErrorKind::UnknownRegion(_) = err.kind
87 +
                else throw testing::TestError::Failed;
88 +
        }
89 +
    }
90 +
}
91 +
92 +
/// An ancestor relation does not make distinct reference regions assignable.
93 +
@test unsafe fn testRegionInvariance() throws (testing::TestError) {
94 +
    for program in [
95 +
        "fn f 'a 'b (p: &'a u8) where 'a: 'b { let q: &'b u8 = p; }",
96 +
        "unsafe fn f 'a 'b (p: &'b u8) where 'a: 'b { let q: &'a u8 = p; }",
97 +
    ] {
98 +
        let mut testArena5 = super::testArena();
99 +
        let testStorage5: 'test5 = &mut testArena5 in {
100 +
            let mut res = super::testResolver(testStorage5);
101 +
            let result = try super::resolveProgramStr(&mut res, program);
102 +
            let err = try super::expectError(&result);
103 +
            let case resolver::ErrorKind::TypeMismatch(_) = err.kind
104 +
                else throw testing::TestError::Failed;
105 +
        }
106 +
    }
107 +
}
108 +
109 +
/// Nominal region environments diagnose their own invalid parent relations.
110 +
@test unsafe fn testNominalRegionNames() throws (testing::TestError) {
111 +
    for program in [
112 +
        "record R: 'r where 'missing: 'r { value: u8 }",
113 +
        "union U: 'r where 'missing: 'r { Empty }",
114 +
        "record R: 'r { value: &'other u8 }",
115 +
        "union U: 'r { Value(&'other u8) }",
116 +
    ] {
117 +
        let mut testArena6 = super::testArena();
118 +
        let testStorage6: 'test6 = &mut testArena6 in {
119 +
            let mut res = super::testResolver(testStorage6);
120 +
            let result = try super::resolveProgramStr(&mut res, program);
121 +
            let err = try super::expectError(&result);
122 +
            let case resolver::ErrorKind::UnknownRegion(_) = err.kind
123 +
                else throw testing::TestError::Failed;
124 +
        }
125 +
    }
126 +
    for program in [
127 +
        "record R: 'r + 'r { value: u8 }",
128 +
        "union U: 'r + 'r { Empty }",
129 +
    ] {
130 +
        let mut testArena7 = super::testArena();
131 +
        let testStorage7: 'test7 = &mut testArena7 in {
132 +
            let mut res = super::testResolver(testStorage7);
133 +
            let result = try super::resolveProgramStr(&mut res, program);
134 +
            let _ = try super::expectErrorKind(&result, resolver::ErrorKind::DuplicateBinding("'r"));
135 +
        }
136 +
    }
137 +
}
138 +
139 +
/// Region declarations restore their environment after a signature error.
140 +
@test unsafe fn testRegionErrorScopeRestoration() throws (testing::TestError) {
141 +
    let mut testArena8 = super::testArena();
142 +
    let testStorage8: 'test8 = &mut testArena8 in {
143 +
        let mut res = super::testResolver(testStorage8);
144 +
        let result = try super::resolveProgramStr(&mut res,
145 +
            "fn f 'r (p: &'missing u8) {} fn g 'other (p: &'r u8) {}"
146 +
        );
147 +
        let err = try super::expectError(&result);
148 +
        let case resolver::ErrorKind::UnknownRegion(name) = err.kind
149 +
            else throw testing::TestError::Failed;
150 +
        try testing::expect(mem::eq(name, "'missing"));
151 +
        try testing::expect(res.regionScope == nil);
152 +
        try testing::expect(res.currentFn == nil);
153 +
        try testing::expect(res.currentFnNode == nil);
154 +
    }
155 +
}
156 +
157 +
/// Regional exclusive references retain affine ownership and arithmetic checks.
158 +
@test unsafe fn testRegionExclusiveOwnership() throws (testing::TestError) {
159 +
    for program in [
160 +
        "fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }",
161 +
        "unsafe fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }",
162 +
        "unsafe fn f 'r (p: &'r mut u8) { let q = p + 1; }",
163 +
        "unsafe fn f 'r (p: &'r mut u8) { let q = p as *mut u8; }",
164 +
    ] {
165 +
        let mut testArena9 = super::testArena();
166 +
        let testStorage9: 'test9 = &mut testArena9 in {
167 +
            let mut res = super::testResolver(testStorage9);
168 +
            let result = try super::resolveProgramStr(&mut res, program);
169 +
            let _ = try super::expectError(&result);
170 +
        }
171 +
    }
172 +
}
173 +
174 +
/// Callback signatures preserve the same region substitution as data arguments.
175 +
@test unsafe fn testRegionCallbackSubstitution() throws (testing::TestError) {
176 +
    let mut testArena10 = super::testArena();
177 +
    let testStorage10: 'test10 = &mut testArena10 in {
178 +
        let mut res = super::testResolver(testStorage10);
179 +
        let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) -> u32 { return *p; } fn useOp 'r (p: &'r u32, op: fn(&'r u32) -> u32) -> u32 { return op(p); } fn relay 'a 'b (p: &'a u32) -> u32 { return useOp(p, read 'b); }");
180 +
        let err = try super::expectError(&result);
181 +
        let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed;
182 +
    }
183 +
}
184 +
185 +
/// A single region parameter requires one consistent named argument.
186 +
@test unsafe fn testRegionCallInferenceFailures() throws (testing::TestError) {
187 +
    for program in [
188 +
        "fn pair 'r (a: &'r u32, b: &'r u32) {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }",
189 +
        "fn read 'r (p: &'r u32) {} fn relay(p: &u32) { read(p); }",
190 +
        "unsafe fn read 'r (p: &'r u32) {} unsafe fn relay(p: *unsafe u32) { read(p); }",
191 +
        "fn answer 'r () -> u32 { return 42; } fn relay() -> u32 { return answer(); }",
192 +
        "fn read 'r (p: ?&'r u32) {} fn relay() { read(nil); }",
193 +
        "unsafe fn read 'r (p: ?&'r u32) {} unsafe fn relay() { read(nil); }",
194 +
    ] {
195 +
        let mut testArena11 = super::testArena();
196 +
        let testStorage11: 'test11 = &mut testArena11 in {
197 +
            let mut res = super::testResolver(testStorage11);
198 +
            let result = try super::resolveProgramStr(&mut res, program);
199 +
            let err = try super::expectError(&result);
200 +
            let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed;
201 +
        }
202 +
    }
203 +
}
204 +
205 +
/// Inferred and explicit region arguments must satisfy declared parent relations.
206 +
@test unsafe fn testRegionCallParents() throws (testing::TestError) {
207 +
    for program in [
208 +
        "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }",
209 +
        "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} unsafe fn relay 'a 'b (a: &'a u32, b: &'b u32) where 'a: 'b { pair 'b 'a (b, a); }",
210 +
    ] {
211 +
        let mut testArena12 = super::testArena();
212 +
        let testStorage12: 'test12 = &mut testArena12 in {
213 +
            let mut res = super::testResolver(testStorage12);
214 +
            let result = try super::resolveProgramStr(&mut res, program);
215 +
            let err = try super::expectError(&result);
216 +
            let case resolver::ErrorKind::RegionParent(_) = err.kind else throw testing::TestError::Failed;
217 +
        }
218 +
    }
219 +
}
220 +
221 +
/// Region applications require the declared arity and accessible names.
222 +
@test unsafe fn testExplicitRegionApplicationErrors() throws (testing::TestError) {
223 +
    for program in [
224 +
        "fn read 'r (p: &'r u32) {} fn relay 'a 'b (p: &'a u32) { read 'a 'b (p); }",
225 +
        "fn read(p: &u32) {} fn relay 'a (p: &'a u32) { read 'a (p); }",
226 +
    ] {
227 +
        let mut testArena13 = super::testArena();
228 +
        let testStorage13: 'test13 = &mut testArena13 in {
229 +
            let mut res = super::testResolver(testStorage13);
230 +
            let result = try super::resolveProgramStr(&mut res, program);
231 +
            let err = try super::expectError(&result);
232 +
            let case resolver::ErrorKind::RegionArgumentCount(_) = err.kind else throw testing::TestError::Failed;
233 +
        }
234 +
    }
235 +
    let mut testArena14 = super::testArena();
236 +
    let testStorage14: 'test14 = &mut testArena14 in {
237 +
        let mut res = super::testResolver(testStorage14);
238 +
        let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) {} fn relay 'a (p: &'a u32) { read 'missing (p); }");
239 +
        let err = try super::expectError(&result);
240 +
        let case resolver::ErrorKind::UnknownRegion(_) = err.kind else throw testing::TestError::Failed;
241 +
    }
242 +
}
243 +
244 +
/// Region header loans stay active through moves and nested exclusive reborrows.
245 +
@test unsafe fn testConcreteBorrowConflicts() throws (testing::TestError) {
246 +
    for program in [
247 +
        "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }",
248 +
        "unsafe fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }",
249 +
        "fn run() { let mut x: u32 = 1; let p: 'r = &mut x, q = &mut x in {} }",
250 +
        "fn run() { let mut x: u32 = 1; let p: 'r = &x, q = &mut x in {} }",
251 +
        "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { { let q = p; set *q = 2; } set x = 3; } }",
252 +
        "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { set *p = 3; } } }",
253 +
        "fn run(i: u32, j: u32) { let mut x: [u32; 2] = [1, 2]; let p: 'r = &mut x[i], q = &mut x[j] in {} }",
254 +
    ] {
255 +
        let mut testArena15 = super::testArena();
256 +
        let testStorage15: 'test15 = &mut testArena15 in {
257 +
            let mut res = super::testResolver(testStorage15);
258 +
            let result = try super::resolveProgramStr(&mut res, program);
259 +
            let err = try super::expectError(&result);
260 +
            let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed;
261 +
        }
262 +
    }
263 +
}
264 +
265 +
/// Borrow headers require existing places and accessible, distinct region names.
266 +
@test unsafe fn testConcreteRegionNamesAndSources() throws (testing::TestError) {
267 +
    for program in [
268 +
        "fn run() { let p: 'r = &1 in {} }",
269 +
        "fn value() -> u32 { return 1; } fn run() { let p: 'r = &value() in {} }",
270 +
        "fn run() { let x: u32 = 1; let p: 'r = &x in { let q: 'r = &x in {} } }",
271 +
        "fn run() { let x: u32 = 1; let p: 'r = &x in {} let q: &'r u32 = &x; }",
272 +
        "fn run 'a (p: &'a mut u32) { let q: 'b = &mut *p in {} }",
273 +
    ] {
274 +
        let mut testArena16 = super::testArena();
275 +
        let testStorage16: 'test16 = &mut testArena16 in {
276 +
            let mut res = super::testResolver(testStorage16);
277 +
            let result = try super::resolveProgramStr(&mut res, program);
278 +
            let _ = try super::expectError(&result);
279 +
        }
280 +
    }
281 +
282 +
}
283 +
284 +
/// A block-local value cannot supply storage for the whole enclosing region.
285 +
@test unsafe fn testRegionLocalStorageBoundary() throws (testing::TestError) {
286 +
    for program in [
287 +
        "fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }",
288 +
        "unsafe fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }",
289 +
        "fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }",
290 +
        "unsafe fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }",
291 +
        "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { let outer: &'a mut u32 = q; } } }",
292 +
    ] {
293 +
        let mut testArena17 = super::testArena();
294 +
        let testStorage17: 'test17 = &mut testArena17 in {
295 +
            let mut res = super::testResolver(testStorage17);
296 +
            let result = try super::resolveProgramStr(&mut res, program);
297 +
            let err = try super::expectError(&result);
298 +
            let case resolver::ErrorKind::TypeMismatch(_) = err.kind else throw testing::TestError::Failed;
299 +
        }
300 +
    }
301 +
302 +
}
303 +
304 +
/// Generic calls keep the exclusive-argument overlap check after substitution.
305 +
@test unsafe fn testRegionCallBorrowOverlap() throws (testing::TestError) {
306 +
    let mut testArena18 = super::testArena();
307 +
    let testStorage18: 'test18 = &mut testArena18 in {
308 +
        let mut res = super::testResolver(testStorage18);
309 +
        let result = try super::resolveProgramStr(&mut res, "fn pair 'r (a: &'r mut u32, b: &'r mut u32) {} fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { pair(p, p); } }");
310 +
        let err = try super::expectError(&result);
311 +
        let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed;
312 +
    }
313 +
}
314 +
315 +
/// Exact arguments share one descriptor and all applications share the source layout.
316 +
@test unsafe fn testNominalRegionApplications() throws (testing::TestError) {
317 +
    let mut testArena19 = super::testArena();
318 +
    let testStorage19: 'test19 = &mut testArena19 in {
319 +
        let mut res = super::testResolver(testStorage19);
320 +
        let result = try super::resolveProgramStr(&mut res,
321 +
            "record N: 'r + Copy { value: u32 } fn f 'a 'b (x: N 'a, y: N 'a, z: N 'b) {}"
322 +
        );
323 +
        try super::expectNoErrors(&result);
324 +
        let func = try super::getBlockStmt(result.root, 1);
325 +
        let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
326 +
        let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed;
327 +
        let case resolver::Type::Nominal(x) = *info.paramTypes[0] else throw testing::TestError::Failed;
328 +
        let case resolver::Type::Nominal(y) = *info.paramTypes[1] else throw testing::TestError::Failed;
329 +
        let case resolver::Type::Nominal(z) = *info.paramTypes[2] else throw testing::TestError::Failed;
330 +
        try testing::expect(x == y);
331 +
        try testing::expect(x <> z);
332 +
        try testing::expect(not resolver::typesEqual(*info.paramTypes[0], *info.paramTypes[2]));
333 +
        let a = resolver::nominalApplication(x) else throw testing::TestError::Failed;
334 +
        let b = resolver::nominalApplication(z) else throw testing::TestError::Failed;
335 +
        try testing::expect(a.base == b.base);
336 +
        try testing::expect(a.arguments[0].id <> b.arguments[0].id);
337 +
        let case resolver::NominalType::Record(base) = *a.base else throw testing::TestError::Failed;
338 +
        let case resolver::NominalType::Record(first) = *x else throw testing::TestError::Failed;
339 +
        let case resolver::NominalType::Record(second) = *z else throw testing::TestError::Failed;
340 +
        try testing::expect(base.layout == first.layout);
341 +
        try testing::expect(first.layout == second.layout);
342 +
        try testing::expect(first.layout.size == 4);
343 +
    }
344 +
}
345 +
346 +
/// Recursive fields refer to their own exact applied descriptor.
347 +
@test unsafe fn testRecursiveNominalApplications() throws (testing::TestError) {
348 +
    let mut testArena20 = super::testArena();
349 +
    let testStorage20: 'test20 = &mut testArena20 in {
350 +
        let mut res = super::testResolver(testStorage20);
351 +
        let result = try super::resolveProgramStr(&mut res,
352 +
            "record N: 'r + Copy { next: ?*N 'r } fn f 'a 'b (x: N 'a, y: N 'b) {}"
353 +
        );
354 +
        try super::expectNoErrors(&result);
355 +
        let func = try super::getBlockStmt(result.root, 1);
356 +
        let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
357 +
        let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed;
358 +
        for parameter in info.paramTypes {
359 +
            let case resolver::Type::Nominal(descriptor) = *parameter else throw testing::TestError::Failed;
360 +
            let case resolver::NominalType::Record(body) = *descriptor else throw testing::TestError::Failed;
361 +
            let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed;
362 +
            let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed;
363 +
            try testing::expect(resolver::typesEqual(*target, *parameter));
364 +
            try testing::expect(body.layout.size == 8);
365 +
        }
366 +
    }
367 +
}
368 +
369 +
/// An inline union payload retains its enclosing application's region arguments.
370 +
@test unsafe fn testNominalPayloadRegionCapture() throws (testing::TestError) {
371 +
    let mut testArena21 = super::testArena();
372 +
    let testStorage21: 'test21 = &mut testArena21 in {
373 +
        let mut res = super::testResolver(testStorage21);
374 +
        let result = try super::resolveProgramStr(&mut res,
375 +
            "record N: 'r + Copy { value: u32 } union U: 'r + Copy { Link { node: *N 'r }, Empty } fn f 'a (u: U 'a, n: N 'a) {}"
376 +
        );
377 +
        try super::expectNoErrors(&result);
378 +
        let func = try super::getBlockStmt(result.root, 2);
379 +
        let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
380 +
        let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed;
381 +
        let case resolver::Type::Nominal(resolver::NominalType::Union(body)) = *info.paramTypes[0]
382 +
            else throw testing::TestError::Failed;
383 +
        let case resolver::Type::Nominal(resolver::NominalType::Record(payload)) = body.variants[0].valueType
384 +
            else throw testing::TestError::Failed;
385 +
        let case resolver::Type::Pointer { target, .. } = payload.fields[0].fieldType
386 +
            else throw testing::TestError::Failed;
387 +
        try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1]));
388 +
        let applied = body.application else throw testing::TestError::Failed;
389 +
        let case resolver::NominalType::Union(base) = *applied.base else throw testing::TestError::Failed;
390 +
        try testing::expect(body.layout == base.layout);
391 +
    }
392 +
}
393 +
394 +
/// Nominal applications require exactly the declared number of region arguments.
395 +
@test unsafe fn testNominalRegionArgumentCount() throws (testing::TestError) {
396 +
    for program in [
397 +
        "record N: 'r { value: u32 } fn f(n: N) {}",
398 +
        "record N { value: u32 } fn f 'r (n: N 'r) {}",
399 +
        "record N: 'r + 's { value: u32 } fn f 'a (n: N 'a) {}",
400 +
        "record N: 'r { value: u32 } fn f 'a 'b (n: N 'a 'b) {}",
401 +
        "record N: 'r { value: u32 } fn f() { let n = N { value: 1 }; }",
402 +
        "union U: 'r { Empty } fn f() { let u = U::Empty; }",
403 +
    ] {
404 +
        let mut testArena22 = super::testArena();
405 +
        let testStorage22: 'test22 = &mut testArena22 in {
406 +
            let mut res = super::testResolver(testStorage22);
407 +
            let result = try super::resolveProgramStr(&mut res, program);
408 +
            let error = try super::expectError(&result);
409 +
            let case resolver::ErrorKind::RegionArgumentCount(_) = error.kind else throw testing::TestError::Failed;
410 +
        }
411 +
    }
412 +
}
413 +
414 +
/// Type arguments are invariant in values, constructor hints, and patterns.
415 +
@test unsafe fn testNominalRegionInvariance() throws (testing::TestError) {
416 +
    for program in [
417 +
        "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let x: N 'b = n; }",
418 +
        "record N: 'r + Copy { value: u32 } unsafe fn f 'a 'b (n: N 'a) { let x: N 'b = n; }",
419 +
        "record N: 'r + Copy { value: u32 } fn f 'a 'b () { let n: N 'a = N 'b { value: 1 }; }",
420 +
        "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let case N 'b { value } = n else panic; }",
421 +
        "record N: 'r + Copy (u32); fn f 'a 'b (n: N 'a) { let case N 'b (value) = n else panic; }",
422 +
        "record N: 'r + Copy { value: u32 } record M: 'r + Copy { value: u32 } fn f 'a (n: N 'a) { let case M 'a { value } = n else panic; }",
423 +
    ] {
424 +
        let mut testArena23 = super::testArena();
425 +
        let testStorage23: 'test23 = &mut testArena23 in {
426 +
            let mut res = super::testResolver(testStorage23);
427 +
            let result = try super::resolveProgramStr(&mut res, program);
428 +
            let error = try super::expectError(&result);
429 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
430 +
        }
431 +
    }
432 +
}
433 +
434 +
/// Region inference must agree between an outer reference and its embedded nominal type.
435 +
@test unsafe fn testNominalRegionInferenceConflict() throws (testing::TestError) {
436 +
    for program in [
437 +
        "record N: 'r + Copy { value: u32 } fn take 'r (n: &'r N 'r) {} fn f 'a 'b (n: &'a N 'b) { take(n); }",
438 +
        "record N: 'r + Copy { value: u32 } fn take 'r (n: N 'r, m: N 'r) {} fn f 'a 'b (n: N 'a, m: N 'b) { take(n, m); }",
439 +
    ] {
440 +
        let mut testArena24 = super::testArena();
441 +
        let testStorage24: 'test24 = &mut testArena24 in {
442 +
            let mut res = super::testResolver(testStorage24);
443 +
            let result = try super::resolveProgramStr(&mut res, program);
444 +
            let error = try super::expectError(&result);
445 +
            let case resolver::ErrorKind::RegionInference(_) = error.kind else throw testing::TestError::Failed;
446 +
        }
447 +
    }
448 +
}
449 +
450 +
/// Applications must satisfy the source declaration's explicit parent relations.
451 +
@test unsafe fn testNominalRegionParentConstraint() throws (testing::TestError) {
452 +
    let mut testArena25 = super::testArena();
453 +
    let testStorage25: 'test25 = &mut testArena25 in {
454 +
        let mut res = super::testResolver(testStorage25);
455 +
        let result = try super::resolveProgramStr(&mut res,
456 +
            "record N: 'r + 's where 'r: 's { value: u32 } fn f 'a 'b (n: N 'a 'b) {}"
457 +
        );
458 +
        let error = try super::expectError(&result);
459 +
        let case resolver::ErrorKind::RegionParent(_) = error.kind else throw testing::TestError::Failed;
460 +
    }
461 +
}
462 +
463 +
/// An applied affine type retains the source declaration's move rule.
464 +
@test unsafe fn testNominalRegionMoveRule() throws (testing::TestError) {
465 +
    let mut testArena26 = super::testArena();
466 +
    let testStorage26: 'test26 = &mut testArena26 in {
467 +
        let mut res = super::testResolver(testStorage26);
468 +
        let result = try super::resolveProgramStr(&mut res,
469 +
            "record N: 'r { value: u32 } fn f 'a (n: N 'a) { let first = n; let second = n; }"
470 +
        );
471 +
        let error = try super::expectError(&result);
472 +
        let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
473 +
    }
474 +
}
475 +
476 +
/// By-value cycles have no layout, including cycles through exact applications.
477 +
@test unsafe fn testNominalRegionValueCycle() throws (testing::TestError) {
478 +
    for program in [
479 +
        "record N: 'r { next: N 'r }",
480 +
        "record N: 'r { next: ?N 'r }",
481 +
        "union U: 'r { More(U 'r), Empty }",
482 +
        "record N: 'r { m: M 'r } record M: 'r { n: N 'r }",
483 +
    ] {
484 +
        let mut testArena27 = super::testArena();
485 +
        let testStorage27: 'test27 = &mut testArena27 in {
486 +
            let mut res = super::testResolver(testStorage27);
487 +
            let result = try super::resolveProgramStr(&mut res, program);
488 +
            let error = try super::expectError(&result);
489 +
            let case resolver::ErrorKind::RecursiveType = error.kind else throw testing::TestError::Failed;
490 +
        }
491 +
    }
492 +
}
493 +
494 +
/// Recursive substitutions can permute arguments without expanding the type graph.
495 +
@test unsafe fn testNominalRegionPermutation() throws (testing::TestError) {
496 +
    let mut testArena28 = super::testArena();
497 +
    let testStorage28: 'test28 = &mut testArena28 in {
498 +
        let mut res = super::testResolver(testStorage28);
499 +
        let result = try super::resolveProgramStr(&mut res,
500 +
            "record N: 'r + 's + Copy { next: ?*N 's 'r } fn f 'a 'b (x: N 'a 'b, y: N 'b 'a) {}"
501 +
        );
502 +
        try super::expectNoErrors(&result);
503 +
        let func = try super::getBlockStmt(result.root, 1);
504 +
        let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
505 +
        let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed;
506 +
        for parameter, i in info.paramTypes {
507 +
            let case resolver::Type::Nominal(resolver::NominalType::Record(body)) = *parameter
508 +
                else throw testing::TestError::Failed;
509 +
            let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed;
510 +
            let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed;
511 +
            try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1 - i]));
512 +
        }
513 +
    }
514 +
}
515 +
516 +
/// Calls transfer named exclusive references on normal and error paths.
517 +
@test unsafe fn testRegionalCallOwnershipTransfer() throws (testing::TestError) {
518 +
    for program in [
519 +
        "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { take(p); take(p); }",
520 +
        "fn take 'r (p: &'r mut u32) {} unsafe fn run 'r (p: &'r mut u32) { take(p); set *p = 1; }",
521 +
        "fn take 'r (p: &'r mut [u32]) {} fn run 'r (p: &'r mut [u32]) { take(p); p[0]; }",
522 +
        "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { let f = take 'r; f(p); set *p = 1; }",
523 +
        "union E { Bad } fn take 'r (p: &'r mut u32) throws (E) { throw E::Bad; } fn run 'r (p: &'r mut u32) { try take(p) catch {}; set *p = 1; }",
524 +
        "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32, c: bool) { if c { take(p); } set *p = 1; }",
525 +
    ] {
526 +
        let mut testArena29 = super::testArena();
527 +
        let testStorage29: 'test29 = &mut testArena29 in {
528 +
            let mut res = super::testResolver(testStorage29);
529 +
            let result = try super::resolveProgramStr(&mut res, program);
530 +
            let error = try super::expectError(&result);
531 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
532 +
        }
533 +
    }
534 +
}
535 +
536 +
/// Full-region projections protect their source after a local binding leaves scope.
537 +
@test unsafe fn testRegionalProjectionLifetime() throws (testing::TestError) {
538 +
    for program in [
539 +
        "fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }",
540 +
        "unsafe fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }",
541 +
        "fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }",
542 +
        "unsafe fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }",
543 +
        "fn f 'r (p: &'r mut u32) { { let q = &mut *p; set *q = 1; } set *p = 2; }",
544 +
        "fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }",
545 +
        "unsafe fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }",
546 +
        "fn f 'r (p: &'r mut u32) { { let q = &mut *p; let moved = q; } *p; }",
547 +
        "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &mut p.a; } p.a; }",
548 +
        "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &p.a; } set p.a = 2; }",
549 +
    ] {
550 +
        let mut testArena30 = super::testArena();
551 +
        let testStorage30: 'test30 = &mut testArena30 in {
552 +
            let mut res = super::testResolver(testStorage30);
553 +
            let result = try super::resolveProgramStr(&mut res, program);
554 +
            let error = try super::expectError(&result);
555 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
556 +
        }
557 +
    }
558 +
}
559 +
560 +
/// A regional loan on any live branch remains active at the branch join.
561 +
@test unsafe fn testRegionalProjectionBranchJoin() throws (testing::TestError) {
562 +
    for program in [
563 +
        "fn f 'r (p: &'r mut u32, c: bool) { if c { let q = &mut *p; } set *p = 2; }",
564 +
        "fn f 'r (p: &'r mut u32, c: bool) { if c {} else { let q = &*p; } set *p = 2; }",
565 +
        "fn f 'r (p: &'r mut u32, c: bool) { match c { case true => { let q = &mut *p; } else => {} } *p; }",
566 +
        "fn f 'r (p: &'r mut u32, c: ?u32) { if let n = c { let q = &*p; } set *p = 2; }",
567 +
    ] {
568 +
        let mut testArena31 = super::testArena();
569 +
        let testStorage31: 'test31 = &mut testArena31 in {
570 +
            let mut res = super::testResolver(testStorage31);
571 +
            let result = try super::resolveProgramStr(&mut res, program);
572 +
            let error = try super::expectError(&result);
573 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
574 +
        }
575 +
    }
576 +
}
577 +
578 +
/// Loop back edges carry regional loans into later iterations.
579 +
@test unsafe fn testRegionalProjectionLoopBackEdge() throws (testing::TestError) {
580 +
    for program in [
581 +
        "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &mut *p; } }",
582 +
        "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; } }",
583 +
        "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; continue; } }",
584 +
        "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &mut *p; } }",
585 +
        "fn f 'r (p: &'r mut u32, c: ?u32) { while let n = c { let q = &mut *p; } }",
586 +
        "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; } }",
587 +
        "fn f 'r (p: &'r mut u32, c: bool) { loop { if c { set *p = 1; break; } let q = &*p; } }",
588 +
    ] {
589 +
        let mut testArena32 = super::testArena();
590 +
        let testStorage32: 'test32 = &mut testArena32 in {
591 +
            let mut res = super::testResolver(testStorage32);
592 +
            let result = try super::resolveProgramStr(&mut res, program);
593 +
            let error = try super::expectError(&result);
594 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
595 +
        }
596 +
    }
597 +
}
598 +
599 +
/// Loans in caller-supplied regions survive loop and nested-region exits.
600 +
@test unsafe fn testRegionalProjectionLoopExit() throws (testing::TestError) {
601 +
    for program in [
602 +
        "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; break; } set *p = 2; }",
603 +
        "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &*p; break; } else {} set *p = 2; }",
604 +
        "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &*p; break; } else {} set *p = 2; }",
605 +
        "fn f 'r (p: &'r mut u32) { let x: u32 = 0; let v: 'child = &x where 'r: 'child in { let q = &*p; } set *p = 2; }",
606 +
    ] {
607 +
        let mut testArena33 = super::testArena();
608 +
        let testStorage33: 'test33 = &mut testArena33 in {
609 +
            let mut res = super::testResolver(testStorage33);
610 +
            let result = try super::resolveProgramStr(&mut res, program);
611 +
            let error = try super::expectError(&result);
612 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
613 +
        }
614 +
    }
615 +
}
616 +
617 +
/// Region erasure cannot merge distinct error alternatives in one signature.
618 +
@test unsafe fn testRegionalErrorAmbiguity() throws (testing::TestError) {
619 +
    for program in [
620 +
        "record E: 'r + Copy {} fn f 'a 'b () throws (E 'a, E 'b) {}",
621 +
        "record E: 'r + Copy {} unsafe fn f 'a 'b () throws (E 'a, E 'b) {}",
622 +
        "record E: 'r + Copy {} record Callback: 'a + 'b { f: fn() throws (E 'a, E 'b) }",
623 +
        "fn f 'a 'b () throws (&'a u32, &'b u32) {}",
624 +
    ] {
625 +
        let mut testArena34 = super::testArena();
626 +
        let testStorage34: 'test34 = &mut testArena34 in {
627 +
            let mut res = super::testResolver(testStorage34);
628 +
            let result = try super::resolveProgramStr(&mut res, program);
629 +
            try super::expectErrorKind(&result, resolver::ErrorKind::AmbiguousRegionalError);
630 +
        }
631 +
    }
632 +
}
633 +
634 +
/// Moving an exclusive reference into an aggregate transfers its ownership.
635 +
@test unsafe fn testRegionalAggregateMove() throws (testing::TestError) {
636 +
    for program in [
637 +
        "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }",
638 +
        "record W: 'r { p: &'r mut u32 } unsafe fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }",
639 +
        "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let case W { p } = w else panic; let again = w; }",
640 +
        "union W: 'r { P { p: &'r mut u32 } } fn f 'r (p: &'r mut u32) { let w = W 'r::P { p }; set *p = 1; }",
641 +
        "fn f 'r (p: &'r mut u32) { let a: [&'r mut u32; 1] = [p]; set *p = 1; }",
642 +
        "fn f 'r (p: &'r mut u32) { let a: ?&'r mut u32 = p; set *p = 1; }",
643 +
    ] {
644 +
        let mut testArena35 = super::testArena();
645 +
        let testStorage35: 'test35 = &mut testArena35 in {
646 +
            let mut res = super::testResolver(testStorage35);
647 +
            let result = try super::resolveProgramStr(&mut res, program);
648 +
            let error = try super::expectError(&result);
649 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
650 +
        }
651 +
    }
652 +
}
653 +
654 +
/// Aggregate transfers do not end a projection loan on its source.
655 +
@test unsafe fn testRegionalAggregateLoan() throws (testing::TestError) {
656 +
    for program in [
657 +
        "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { { let w = W 'r { p: &mut *p }; } set *p = 1; }",
658 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r mut u32) { { let r = R 'r { p: &*p }; } set *p = 1; }",
659 +
        "record R: 'r + Copy { p: &'r u32 } fn take 'r (r: R 'r) {} fn f 'r (p: &'r mut u32) { take(R 'r { p: &*p }); set *p = 1; }",
660 +
        "fn id 'r (p: &'r mut u32) -> &'r mut u32 { return p; } fn f 'r (p: &'r mut u32) { let q = id(p); { let other = &mut *q; } set *q = 1; }",
661 +
    ] {
662 +
        let mut testArena36 = super::testArena();
663 +
        let testStorage36: 'test36 = &mut testArena36 in {
664 +
            let mut res = super::testResolver(testStorage36);
665 +
            let result = try super::resolveProgramStr(&mut res, program);
666 +
            let error = try super::expectError(&result);
667 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
668 +
        }
669 +
    }
670 +
}
671 +
672 +
/// Local storage cannot acquire a caller region through returns or aggregate hints.
673 +
@test unsafe fn testRegionalStorageEscape() throws (testing::TestError) {
674 +
    for program in [
675 +
        "fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }",
676 +
        "unsafe fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }",
677 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; return R 'r { p: &x }; }",
678 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; let p: 'short = &x in { return R 'short { p }; } }",
679 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) -> &'r R 'r { let r = R 'r { p }; return &r; }",
680 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) { let mut outer = R 'r { p }; let x: u32 = 1; let q: 'short = &x in { set outer = R 'short { p: q }; } }",
681 +
    ] {
682 +
        let mut testArena37 = super::testArena();
683 +
        let testStorage37: 'test37 = &mut testArena37 in {
684 +
            let mut res = super::testResolver(testStorage37);
685 +
            let result = try super::resolveProgramStr(&mut res, program);
686 +
            let error = try super::expectError(&result);
687 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
688 +
        }
689 +
    }
690 +
}
691 +
692 +
/// Reference fields retain their ownership and storage restrictions.
693 +
@test unsafe fn testRegionalFieldRestrictions() throws (testing::TestError) {
694 +
    {
695 +
        let mut testArena38 = super::testArena();
696 +
        let testStorage38: 'test38 = &mut testArena38 in {
697 +
            let mut res = super::testResolver(testStorage38);
698 +
            let result = try super::resolveProgramStr(&mut res, "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let p = w.p; }");
699 +
            try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove);
700 +
        }
701 +
    }
702 +
    {
703 +
        let mut testArena39 = super::testArena();
704 +
        let testStorage39: 'test39 = &mut testArena39 in {
705 +
            let mut res = super::testResolver(testStorage39);
706 +
            let result = try super::resolveProgramStr(&mut res, "record R: 'r { p: &u32 }");
707 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition);
708 +
        }
709 +
    }
710 +
    {
711 +
        let mut testArena40 = super::testArena();
712 +
        let testStorage40: 'test40 = &mut testArena40 in {
713 +
            let mut res = super::testResolver(testStorage40);
714 +
            let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { static r: &'r u32 = undefined; }");
715 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition);
716 +
        }
717 +
    }
718 +
}
719 +
720 +
/// Regional projection tracking limits simultaneous live projections.
721 +
@test unsafe fn testRegionalLoanCapacity() throws (testing::TestError) {
722 +
    let mut reuseArena = super::testArena();
723 +
    let reuseStorage: 'reuse = &mut reuseArena in {
724 +
        let mut res = super::testResolver(reuseStorage);
725 +
        let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { let outer: 'outer = &*p where 'r: 'outer in { let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } } }");
726 +
        try super::expectNoErrors(&result);
727 +
    }
728 +
    let mut testArena41 = super::testArena();
729 +
    let testStorage41: 'test41 = &mut testArena41 in {
730 +
        let mut res = super::testResolver(testStorage41);
731 +
        let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { [&*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p]; }");
732 +
        try super::expectErrorKind(&result, resolver::ErrorKind::RegionalLoanOverflow);
733 +
    }
734 +
}
735 +
736 +
/// Shared regional parameters consume exclusive arguments that can be retained.
737 +
@test unsafe fn testRegionalSharedCallTransfer() throws (testing::TestError) {
738 +
    for program in [
739 +
        "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) -> u32 { let q = read(p); set *p = 2; return *q; }",
740 +
        "fn read 'r (p: &'r u32) -> &'r u32 { return p; } unsafe fn f 'r (p: &'r mut u32) { let q = read(p); set *p = 2; }",
741 +
        "fn read 'r (p: &'r [u32]) -> &'r [u32] { return p; } fn f 'r (p: &'r mut [u32]) { let q = read(p); set p[0] = 2; }",
742 +
        "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) { let callback = read 'r; let q = callback(p); set *p = 2; }",
743 +
    ] {
744 +
        let mut testArena42 = super::testArena();
745 +
        let testStorage42: 'test42 = &mut testArena42 in {
746 +
            let mut res = super::testResolver(testStorage42);
747 +
            let result = try super::resolveProgramStr(&mut res, program);
748 +
            let error = try super::expectError(&result);
749 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
750 +
        }
751 +
    }
752 +
}
753 +
754 +
/// Values stored in a regional destination must cover its full lifetime.
755 +
@test unsafe fn testRegionalStoreLifetime() throws (testing::TestError) {
756 +
    for program in [
757 +
        "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }",
758 +
        "unsafe fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }",
759 +
        "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) where 'a: 'b { set *dst = p; }",
760 +
        "record R: 'r + Copy { p: &'r u32 } fn f 'a 'b (dst: &'a mut R 'b, p: &'b u32) { set *dst = R 'b { p }; }",
761 +
        "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[0] = p; }",
762 +
        "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[..] = p; }",
763 +
        "fn f 'a 'b (dst: &'a mut fn(&'b u32), callback: fn(&'b u32)) { set *dst = callback; }",
764 +
    ] {
765 +
        let mut testArena43 = super::testArena();
766 +
        let testStorage43: 'test43 = &mut testArena43 in {
767 +
            let mut res = super::testResolver(testStorage43);
768 +
            let result = try super::resolveProgramStr(&mut res, program);
769 +
            let error = try super::expectError(&result);
770 +
            let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed;
771 +
        }
772 +
    }
773 +
}
774 +
775 +
/// A session holds the arena loan after the interface is moved or discarded.
776 +
@test unsafe fn testSessionArenaLoan() throws (testing::TestError) {
777 +
    for program in [
778 +
        "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }",
779 +
        "use std::lang::alloc; unsafe fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }",
780 +
        "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let moved = s; alloc::reset(arena); } }",
781 +
        "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let _ = s; set arena.offset = 0; } }",
782 +
        "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { use *arena as t in {} } }",
783 +
    ] {
784 +
        let mut testArena44 = super::testArena();
785 +
        let testStorage44: 'test44 = &mut testArena44 in {
786 +
            let mut res = super::testResolver(testStorage44);
787 +
            let result = try super::resolveSessionProgramStr(&mut res, program);
788 +
            let error = try super::expectError(&result);
789 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
790 +
        }
791 +
    }
792 +
}
793 +
794 +
/// Session interfaces have affine ownership and invariant region arguments.
795 +
@test unsafe fn testSessionOwnership() throws (testing::TestError) {
796 +
    for program in [
797 +
        "fn f 'r (s: Session 'r) { let moved = s; let again = s; }",
798 +
        "unsafe fn f 'r (s: Session 'r) { let moved = s; let again = s; }",
799 +
        "fn take 'r (s: Session 'r) {} fn f 'r (s: Session 'r) { take(s); take(s); }",
800 +
    ] {
801 +
        let mut testArena45 = super::testArena();
802 +
        let testStorage45: 'test45 = &mut testArena45 in {
803 +
            let mut res = super::testResolver(testStorage45);
804 +
            let result = try super::resolveProgramStr(&mut res, program);
805 +
            let error = try super::expectError(&result);
806 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
807 +
        }
808 +
    }
809 +
    {
810 +
        let mut testArena46 = super::testArena();
811 +
        let testStorage46: 'test46 = &mut testArena46 in {
812 +
            let mut res = super::testResolver(testStorage46);
813 +
            let result = try super::resolveProgramStr(&mut res,
814 +
                "fn f 'r 's (s: Session 's) -> Session 'r where 'r: 's { return s; }");
815 +
            let error = try super::expectError(&result);
816 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
817 +
        }
818 +
    }
819 +
}
820 +
821 +
/// Regional `use` blocks require a mutable allocation trait implementer.
822 +
@test unsafe fn testSessionSource() throws (testing::TestError) {
823 +
    for program in [
824 +
        "record Arena { data: *mut [u8], offset: u32 } fn f(arena: &mut Arena) { use *arena as s in {} }",
825 +
        "fn f(value: &mut u32) { use *value as s in {} }",
826 +
    ] {
827 +
        let mut testArena47 = super::testArena();
828 +
        let testStorage47: 'test47 = &mut testArena47 in {
829 +
            let mut res = super::testResolver(testStorage47);
830 +
            let result = try super::resolveSessionProgramStr(&mut res, program);
831 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidSessionSource);
832 +
        }
833 +
    }
834 +
    let mut customArena = super::testArena();
835 +
    let customStorage: 'custom = &mut customArena in {
836 +
        let mut res = super::testResolver(customStorage);
837 +
        let result = try super::resolveSessionProgramStr(&mut res,
838 +
            "use std::lang::alloc; record Wrapper: 'r { arena: &'r mut alloc::Arena } instance alloc::Alloc for Wrapper 'r { unsafe fn (wrapper: &mut Wrapper 'r) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (alloc::AllocError) { panic; } unsafe fn (wrapper: &mut Wrapper 'r) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (alloc::AllocError) { panic; } } fn f 'r (wrapper: &'r mut Wrapper 'r) { use *wrapper as session in { try! session.new(1 as u32); } }");
839 +
        try super::expectNoErrors(&result);
840 +
    }
841 +
}
842 +
843 +
/// Region-dependent values cannot be manufactured from uninitialized storage.
844 +
@test unsafe fn testRegionalUndefined() throws (testing::TestError) {
845 +
    for program in [
846 +
        "unsafe fn f 'r () -> Session 'r { return undefined; }",
847 +
        "record R: 'r { s: Session 'r } unsafe fn f 'r () -> R 'r { return undefined; }",
848 +
        "unsafe fn f 'r () -> *unsafe Session 'r { return undefined; }",
849 +
        "unsafe fn f 'r () -> &'r u32 { return undefined; }",
850 +
    ] {
851 +
        let mut testArena48 = super::testArena();
852 +
        let testStorage48: 'test48 = &mut testArena48 in {
853 +
            let mut res = super::testResolver(testStorage48);
854 +
            let result = try super::resolveProgramStr(&mut res, program);
855 +
            let error = try super::expectError(&result);
856 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
857 +
        }
858 +
    }
859 +
}
860 +
861 +
/// Pointer casts cannot introduce session or aggregate region dependencies.
862 +
@test unsafe fn testRegionalCastForgery() throws (testing::TestError) {
863 +
    for program in [
864 +
        "unsafe fn f 'r (p: *unsafe opaque) -> *unsafe Session 'r { return p as *unsafe Session 'r; }",
865 +
        "unsafe fn f 'r (p: *unsafe [opaque]) -> *unsafe [Session 'r] { return p as *unsafe [Session 'r]; }",
866 +
        "record R: 'r { s: Session 'r } unsafe fn f 'r (p: *unsafe opaque) -> *unsafe R 'r { return p as *unsafe R 'r; }",
867 +
        "record R: 'r + Copy { p: &'r u32 } unsafe fn f 'r 's (p: *unsafe R 'r) -> *unsafe R 's { return p as *unsafe R 's; }",
868 +
    ] {
869 +
        let mut testArena49 = super::testArena();
870 +
        let testStorage49: 'test49 = &mut testArena49 in {
871 +
            let mut res = super::testResolver(testStorage49);
872 +
            let result = try super::resolveProgramStr(&mut res, program);
873 +
            let error = try super::expectError(&result);
874 +
            let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed;
875 +
        }
876 +
    }
877 +
}
878 +
879 +
/// Bulk allocation rejects resources and values that need ownership cleanup.
880 +
@test unsafe fn testSessionAllocationValue() throws (testing::TestError) {
881 +
    for program in [
882 +
        "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }",
883 +
        "record R: Once { n: u32 } unsafe fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }",
884 +
        "record R: Once { n: u32 } record Outer { value: R } fn f 'r (s: &Session 'r, value: Outer) { try! s.new(value); }",
885 +
        "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: ?R) { try! s.new(value); }",
886 +
        "fn f 'r (s: &Session 'r, value: Session 'r) { try! s.new(value); }",
887 +
        "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: [R; 1]) { try! s.new(value); }",
888 +
        "fn f 'r (s: &Session 'r, value: &'r mut u32) { try! s.fill(value, 2); }",
889 +
        "fn f 'r (s: &Session 'r, value: &[&'r mut u32]) { try! s.copy(value); }",
890 +
    ] {
891 +
        let mut testArena50 = super::testArena();
892 +
        let testStorage50: 'test50 = &mut testArena50 in {
893 +
            let mut res = super::testResolver(testStorage50);
894 +
            let result = try super::resolveSessionProgramStr(&mut res, program);
895 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationValue);
896 +
        }
897 +
    }
898 +
}
899 +
900 +
/// Allocated values must retain dependencies that cover the destination session.
901 +
@test unsafe fn testSessionAllocationDependency() throws (testing::TestError) {
902 +
    for program in [
903 +
        "fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }",
904 +
        "unsafe fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }",
905 +
        "record R: 's + Copy { p: &'s u32 } fn f 'r 's (a: &Session 'r, p: R 's) where 'r: 's { try! a.new(p); }",
906 +
        "fn f 'r 's (a: &Session 'r, p: &[&'s u32]) where 'r: 's { try! a.copy(p); }",
907 +
    ] {
908 +
        let mut testArena51 = super::testArena();
909 +
        let testStorage51: 'test51 = &mut testArena51 in {
910 +
            let mut res = super::testResolver(testStorage51);
911 +
            let result = try super::resolveSessionProgramStr(&mut res, program);
912 +
            let error = try super::expectError(&result);
913 +
            let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed;
914 +
        }
915 +
    }
916 +
}
917 +
918 +
/// Allocation operations require error handling and exact source region results.
919 +
@test unsafe fn testSessionAllocationContract() throws (testing::TestError) {
920 +
    {
921 +
        let mut testArena52 = super::testArena();
922 +
        let testStorage52: 'test52 = &mut testArena52 in {
923 +
            let mut res = super::testResolver(testStorage52);
924 +
            let result = try super::resolveSessionProgramStr(&mut res,
925 +
                "fn f 'r (s: &Session 'r) { s.new(1 as u32); }");
926 +
            try super::expectErrorKind(&result, resolver::ErrorKind::MissingTry);
927 +
        }
928 +
    }
929 +
    {
930 +
        let mut testArena53 = super::testArena();
931 +
        let testStorage53: 'test53 = &mut testArena53 in {
932 +
            let mut res = super::testResolver(testStorage53);
933 +
            let result = try super::resolveSessionProgramStr(&mut res,
934 +
                "fn f 'r 's (a: &Session 'r) -> &'s mut u32 { return try! a.new(1 as u32); }");
935 +
            let error = try super::expectError(&result);
936 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
937 +
        }
938 +
    }
939 +
    {
940 +
        let mut testArena54 = super::testArena();
941 +
        let testStorage54: 'test54 = &mut testArena54 in {
942 +
            let mut res = super::testResolver(testStorage54);
943 +
            let result = try super::resolveSessionProgramStr(&mut res,
944 +
                "fn f 'r (a: &Session 'r, p: &u32) { try! a.new(p); }");
945 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition);
946 +
        }
947 +
    }
948 +
}
949 +
950 +
/// Typed reservations reject array products, aggregate sums, and field-offset overflow.
951 +
@test unsafe fn testSessionAllocationLayout() throws (testing::TestError) {
952 +
    for program in [
953 +
        "fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }",
954 +
        "unsafe fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }",
955 +
        "record R { a: [u8; 4294967290], b: u64 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }",
956 +
        "record R { a: [u8; 2147483648], b: u8 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }",
957 +
        "fn f 'r (s: &Session 'r, value: ?[u8; 4294967295]) { try! s.new(value); }",
958 +
        "union R { Data([u8; 4294967295]) } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }",
959 +
    ] {
960 +
        let mut testArena55 = super::testArena();
961 +
        let testStorage55: 'test55 = &mut testArena55 in {
962 +
            let mut res = super::testResolver(testStorage55);
963 +
            let result = try super::resolveSessionProgramStr(&mut res, program);
964 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationLayout);
965 +
        }
966 +
    }
967 +
}
968 +
969 +
/// Cell payloads must permit plain value copies.
970 +
@test unsafe fn testCellPointerPayload() throws (testing::TestError) {
971 +
    for program in [
972 +
        "union State: 'r { Next(&'r cell State 'r) }",
973 +
        "union State: 'r { Ready(&'r u32) } fn f 'r (state: &'r cell State 'r) {}",
974 +
        "union State: 'r { Ready(&'r u32) } unsafe fn f 'r (state: &'r cell State 'r) {}",
975 +
        "fn f(p: *cell *mut u32) {}",
976 +
        "unsafe fn f(p: *cell *mut u32) {}",
977 +
        "record R: Once { n: u32 } fn f(p: *cell R) {}",
978 +
        "record R { n: u32 } fn f(p: *cell R) {}",
979 +
        "fn f 'r (p: &'r cell &'r mut u32) {}",
980 +
    ] {
981 +
        let mut testArena56 = super::testArena();
982 +
        let testStorage56: 'test56 = &mut testArena56 in {
983 +
            let mut res = super::testResolver(testStorage56);
984 +
            let result = try super::resolveProgramStr(&mut res, program);
985 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload);
986 +
        }
987 +
    }
988 +
}
989 +
990 +
/// Cell access cannot expose payload references or restore exclusive pointers.
991 +
@test unsafe fn testCellPointerAccess() throws (testing::TestError) {
992 +
    for program in [
993 +
        "fn f(p: *cell u32) { let r = &*p; }",
994 +
        "unsafe fn f(p: *cell u32) { let r = &*p; }",
995 +
        "record R: Copy { n: u32 } fn f(p: *cell R) { let r = &(*p).n; }",
996 +
        "fn f(p: *cell [u32; 2]) { let r = &(*p)[0]; }",
997 +
        "record R: Copy { n: u32 } fn f(p: *cell R) { set (*p).n = 1; }",
998 +
        "fn f(p: *cell [u32; 2]) { set (*p)[0] = 1; }",
999 +
    ] {
1000 +
        let mut testArena57 = super::testArena();
1001 +
        let testStorage57: 'test57 = &mut testArena57 in {
1002 +
            let mut res = super::testResolver(testStorage57);
1003 +
            let result = try super::resolveProgramStr(&mut res, program);
1004 +
            try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding);
1005 +
        }
1006 +
    }
1007 +
    for program in [
1008 +
        "fn f(p: *cell u32) -> *mut u32 { return p as *mut u32; }",
1009 +
        "unsafe fn f(p: *cell u32) -> *unsafe mut u32 { return p as *unsafe mut u32; }",
1010 +
        "fn f(p: *u32) -> *cell u32 { return p as *cell u32; }",
1011 +
        "unsafe fn f(p: *unsafe mut u32) -> *cell u32 { return p as *cell u32; }",
1012 +
        "fn f(p: *mut u64) -> *cell u32 { return p as *cell u32; }",
1013 +
        "fn f 'r (p: &'r mut u32) -> *cell u32 { return p as *cell u32; }",
1014 +
    ] {
1015 +
        let mut testArena58 = super::testArena();
1016 +
        let testStorage58: 'test58 = &mut testArena58 in {
1017 +
            let mut res = super::testResolver(testStorage58);
1018 +
            let result = try super::resolveProgramStr(&mut res, program);
1019 +
            let error = try super::expectError(&result);
1020 +
            let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed;
1021 +
        }
1022 +
    }
1023 +
}
1024 +
1025 +
/// An exclusive payload borrow cannot be made from shared cell access.
1026 +
@test unsafe fn testCellPointerMutablePayload() throws (testing::TestError) {
1027 +
    let mut testArena59 = super::testArena();
1028 +
    let testStorage59: 'test59 = &mut testArena59 in {
1029 +
        let mut res = super::testResolver(testStorage59);
1030 +
        let result = try super::resolveProgramStr(&mut res,
1031 +
            "fn f(p: *cell u32) { let r = &mut *p; }");
1032 +
        try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding);
1033 +
    }
1034 +
}
1035 +
1036 +
/// Cell conversion consumes exclusive access, including call arguments.
1037 +
@test unsafe fn testCellPointerOwnership() throws (testing::TestError) {
1038 +
    for program in [
1039 +
        "fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }",
1040 +
        "unsafe fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }",
1041 +
        "fn read(p: &cell u32) {} fn f(p: &mut u32) { read(p as &cell u32); set *p = 1; }",
1042 +
    ] {
1043 +
        let mut testArena60 = super::testArena();
1044 +
        let testStorage60: 'test60 = &mut testArena60 in {
1045 +
            let mut res = super::testResolver(testStorage60);
1046 +
            let result = try super::resolveProgramStr(&mut res, program);
1047 +
            let error = try super::expectError(&result);
1048 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
1049 +
        }
1050 +
    }
1051 +
    for program in [
1052 +
        "fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }",
1053 +
        "unsafe fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }",
1054 +
    ] {
1055 +
        let mut testArena61 = super::testArena();
1056 +
        let testStorage61: 'test61 = &mut testArena61 in {
1057 +
            let mut res = super::testResolver(testStorage61);
1058 +
            let result = try super::resolveProgramStr(&mut res, program);
1059 +
            let error = try super::expectError(&result);
1060 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
1061 +
        }
1062 +
    }
1063 +
}
1064 +
1065 +
/// Cell writes preserve exact payload regions and destination lifetimes.
1066 +
@test unsafe fn testCellPointerRegion() throws (testing::TestError) {
1067 +
    for program in [
1068 +
        "fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }",
1069 +
        "unsafe fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }",
1070 +
        "fn f 'r 's (p: &'r cell u32) -> &'s cell u32 { return p; }",
1071 +
    ] {
1072 +
        let mut testArena62 = super::testArena();
1073 +
        let testStorage62: 'test62 = &mut testArena62 in {
1074 +
            let mut res = super::testResolver(testStorage62);
1075 +
            let result = try super::resolveProgramStr(&mut res, program);
1076 +
            let error = try super::expectError(&result);
1077 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
1078 +
        }
1079 +
    }
1080 +
    for program in [
1081 +
        "fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }",
1082 +
        "unsafe fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }",
1083 +
    ] {
1084 +
        let mut testArena63 = super::testArena();
1085 +
        let testStorage63: 'test63 = &mut testArena63 in {
1086 +
            let mut res = super::testResolver(testStorage63);
1087 +
            let result = try super::resolveProgramStr(&mut res, program);
1088 +
            let error = try super::expectError(&result);
1089 +
            let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed;
1090 +
        }
1091 +
    }
1092 +
}
1093 +
1094 +
/// Raw cell operations require an unsafe execution context.
1095 +
@test unsafe fn testCellPointerRawAccess() throws (testing::TestError) {
1096 +
    for program in [
1097 +
        "fn f(p: *unsafe cell u32) -> u32 { return *p; }",
1098 +
        "fn f(p: *unsafe cell u32) { set *p = 1; }",
1099 +
        "fn f(p: &*unsafe cell u32) { set **p = 1; }",
1100 +
    ] {
1101 +
        let mut testArena64 = super::testArena();
1102 +
        let testStorage64: 'test64 = &mut testArena64 in {
1103 +
            let mut res = super::testResolver(testStorage64);
1104 +
            let result = try super::resolveProgramStr(&mut res, program);
1105 +
            try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation);
1106 +
        }
1107 +
    }
1108 +
}
1109 +
1110 +
/// A checked buffer context cannot be copied while its exclusive storage is live.
1111 +
@test unsafe fn testCheckedBufferOwnership() throws (testing::TestError) {
1112 +
    for program in [
1113 +
        "record Buffer: 'r { words: &'r mut [u32] } fn observe 'r (b: &Buffer 'r) {} fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }",
1114 +
        "record Buffer: 'r { words: &'r mut [u32] } unsafe fn observe 'r (b: &Buffer 'r) {} unsafe fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }",
1115 +
    ] {
1116 +
        let mut testArena65 = super::testArena();
1117 +
        let testStorage65: 'test65 = &mut testArena65 in {
1118 +
            let mut res = super::testResolver(testStorage65);
1119 +
            let result = try super::resolveProgramStr(&mut res, program);
1120 +
            let error = try super::expectError(&result);
1121 +
            let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed;
1122 +
        }
1123 +
    }
1124 +
}
1125 +
1126 +
/// An iterator's stored shared borrow excludes writes through the buffer owner.
1127 +
@test unsafe fn testCheckedIteratorLoan() throws (testing::TestError) {
1128 +
    for program in [
1129 +
        "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }",
1130 +
        "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } unsafe fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }",
1131 +
    ] {
1132 +
        let mut testArena66 = super::testArena();
1133 +
        let testStorage66: 'test66 = &mut testArena66 in {
1134 +
            let mut res = super::testResolver(testStorage66);
1135 +
            let result = try super::resolveProgramStr(&mut res, program);
1136 +
            let error = try super::expectError(&result);
1137 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
1138 +
        }
1139 +
    }
1140 +
}
1141 +
1142 +
/// An inferred raw pointer argument requires an unsafe address operation.
1143 +
@test unsafe fn testRegionCallRawAddress() throws (testing::TestError) {
1144 +
    let mut arena = super::testArena();
1145 +
    let storage: 'arena = &mut arena in {
1146 +
        let mut res = super::testResolver(storage);
1147 +
        let result = try super::resolveProgramStr(&mut res,
1148 +
            "record View: 'r + Copy { value: &'r u32 } fn read 'r (view: *unsafe View 'r) {} fn f 'r (view: View 'r) { read(&view); }");
1149 +
        try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation);
1150 +
    }
1151 +
}
1152 +
1153 +
/// Shared aggregate access cannot expose mutable access to an exclusive field.
1154 +
@test unsafe fn testSharedExclusiveFieldAccess() throws (testing::TestError) {
1155 +
    for program in [
1156 +
        "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &H 'r) { set h.items[0] = 42; }",
1157 +
        "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &H 'r) { set h.items[0] = 42; }",
1158 +
        "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }",
1159 +
        "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }",
1160 +
        "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} fn f 'r (h: &H 'r) { write(h.items); }",
1161 +
        "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} unsafe fn f 'r (h: &H 'r) { write(h.items); }",
1162 +
        "record H: 'r { item: &'r mut u32 } fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }",
1163 +
        "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }",
1164 +
        "record H { item: *mut u32 } fn f(h: &H) { set *h.item = 42; }",
1165 +
        "record H { item: *mut u32 } unsafe fn f(h: &H) { set *h.item = 42; }",
1166 +
        "record H: 'r { items: [&'r mut u32; 1] } fn f 'r (h: &H 'r) { set *h.items[0] = 42; }",
1167 +
        "record H: 'r { items: [&'r mut u32; 1] } unsafe fn f 'r (h: &H 'r) { set *h.items[0] = 42; }",
1168 +
        "fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }",
1169 +
        "unsafe fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }",
1170 +
        "fn f 'r (p: &'r &'r mut u32) { set **p = 42; }",
1171 +
        "unsafe fn f 'r (p: &'r &'r mut u32) { set **p = 42; }",
1172 +
        "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }",
1173 +
        "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} unsafe fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }",
1174 +
        "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } fn f(h: &H) { h.item.write(); }",
1175 +
        "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } unsafe fn f(h: &H) { h.item.write(); }",
1176 +
        "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } fn f 'r (h: &H 'r) { h.object.write(); }",
1177 +
        "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } unsafe fn f 'r (h: &H 'r) { h.object.write(); }",
1178 +
    ] {
1179 +
        let mut arena = super::testArena();
1180 +
        let storage: 'test = &mut arena in {
1181 +
            let mut res = super::testResolver(storage);
1182 +
            let result = try super::resolveProgramStr(&mut res, program);
1183 +
            try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding);
1184 +
        }
1185 +
    }
1186 +
}
1187 +
1188 +
/// A reference through an exclusive field cannot outlive the owner's borrow.
1189 +
@test unsafe fn testExclusiveOwnerBorrowLifetimes() throws (testing::TestError) {
1190 +
    for program in [
1191 +
        "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }",
1192 +
        "record H: 'r { item: ?&'r mut u32 } fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }",
1193 +
        "record H: 'r { item: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }",
1194 +
        "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }",
1195 +
        "record H: 'r { item: ?&'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }",
1196 +
        "record H: 'r { item: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }",
1197 +
        "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }",
1198 +
        "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }",
1199 +
        "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }",
1200 +
        "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }",
1201 +
        "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }",
1202 +
        "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }",
1203 +
        "record H { item: *mut u32 } fn f(h: &H) -> *u32 { return &*h.item; }",
1204 +
        "record H { item: *mut u32 } unsafe fn f(h: &H) -> *u32 { return &*h.item; }",
1205 +
        "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }",
1206 +
        "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }",
1207 +
    ] {
1208 +
        let mut arena = super::testArena();
1209 +
        let storage: 'test = &mut arena in {
1210 +
            let mut res = super::testResolver(storage);
1211 +
            let result = try super::resolveProgramStr(&mut res, program);
1212 +
            let error = try super::expectError(&result);
1213 +
            let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed;
1214 +
        }
1215 +
    }
1216 +
}
1217 +
1218 +
/// A shared binding cannot hide a move from an exclusive field.
1219 +
@test unsafe fn testSharedBindingPreservesSourceOwnership() throws (testing::TestError) {
1220 +
    for program in [
1221 +
        "record H { item: *mut u32 } fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }",
1222 +
        "record H { item: ?*mut u32 } fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }",
1223 +
        "record H: 'r { item: &'r mut u32 } fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }",
1224 +
        "record H { item: *mut u32 } unsafe fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }",
1225 +
        "record H { item: ?*mut u32 } unsafe fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }",
1226 +
        "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }",
1227 +
    ] {
1228 +
        let mut arena = super::testArena();
1229 +
        let storage: 'test = &mut arena in {
1230 +
            let mut res = super::testResolver(storage);
1231 +
            let result = try super::resolveProgramStr(&mut res, program);
1232 +
            try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove);
1233 +
        }
1234 +
    }
1235 +
}
1236 +
1237 +
/// Methods and trait instances resolve declaration region parameters.
1238 +
@test unsafe fn testRegionalMethods() throws (testing::TestError) {
1239 +
    for program in [
1240 +
        "record View: 'r { value: &'r u32 } fn (view: &View 'r) get 'r () -> &'r u32 { return view.value; } fn read 'x (view: &View 'x) -> u32 { return *view.get(); }",
1241 +
        "trait Pick { fn (&Pick) pick 'r (value: &'r u32) -> &'r u32; } record Picker {} instance Pick for Picker { fn (picker: &Picker) pick 'r (value: &'r u32) -> &'r u32 { return value; } } fn choose 'x (picker: &opaque Pick, value: &'x u32) -> &'x u32 { return picker.pick(value); }",
1242 +
        "trait Read { fn (&Read) read() -> u32; } record Regional: 'r { value: &'r u32 } instance Read for Regional 'r { fn (value: &Regional 'r) read() -> u32 { return *value.value; } } fn inspect(value: &opaque Read) -> u32 { return value.read(); } fn call 'x (value: &Regional 'x) -> u32 { return inspect(value); }",
1243 +
    ] {
1244 +
        let mut arena = super::testArena();
1245 +
        let storage: 'method = &mut arena in {
1246 +
            let mut res = super::testResolver(storage);
1247 +
            let result = try super::resolveProgramStr(&mut res, program);
1248 +
            try super::expectNoErrors(&result);
1249 +
        }
1250 +
    }
1251 +
}
lib/std/lang/scanner.rad +15 -3
71 71
72 72
    /// Eg. `fnord`
73 73
    Ident,
74 74
    /// Eg. `@default`
75 75
    AtIdent,
76 +
    /// Region name with an apostrophe prefix.
77 +
    Region,
76 78
    /// The `log` keyword.
77 79
    Log,
78 80
79 81
    // Literals.
80 82
    String,     // "fnord"
103 105
    // Trait-related tokens.
104 106
    Trait, Instance,
105 107
106 108
    // Type-related tokens.
107 109
    I8, I16, I32, I64, U8, U16, U32, U64,
108 -
    Opaque, Fn, Bool, Union, Record, As, Unsafe
110 +
    Opaque, Fn, Bool, Union, Record, As, Unsafe, Where
109 111
}
110 112
111 113
/// A reserved keyword.
112 114
record Keyword: Copy {
113 115
    /// Keyword string.
115 117
    /// Corresponding token.
116 118
    tok: TokenKind,
117 119
}
118 120
119 121
/// Sorted keyword table for binary search.
120 -
constant KEYWORDS: [Keyword; 52] = [
122 +
constant KEYWORDS: [Keyword; 53] = [
121 123
    { name: "align", tok: TokenKind::Align },
122 124
    { name: "and", tok: TokenKind::And },
123 125
    { name: "as", tok: TokenKind::As },
124 126
    { name: "assert", tok: TokenKind::Assert },
125 127
    { name: "bool", tok: TokenKind::Bool },
167 169
    { name: "u8", tok: TokenKind::U8 },
168 170
    { name: "undefined", tok: TokenKind::Undefined },
169 171
    { name: "union", tok: TokenKind::Union },
170 172
    { name: "unsafe", tok: TokenKind::Unsafe },
171 173
    { name: "use", tok: TokenKind::Use },
174 +
    { name: "where", tok: TokenKind::Where },
172 175
    { name: "while", tok: TokenKind::While },
173 176
];
174 177
175 178
/// Describes where source code originated from.
176 179
export union SourceLoc: Copy {
371 374
        return tok;
372 375
    }
373 376
    return invalid(s.token, "unterminated string");
374 377
}
375 378
376 -
/// Scan character literal enclosed in single quotes.
379 +
/// Scan an apostrophe-prefixed region name or a quoted character literal.
377 380
fn scanChar(s: &mut Scanner) -> Token {
381 +
    if let ch = current(s); char::isAlpha(ch) or ch == '_' {
382 +
        while let ch = current(s); char::isAlpha(ch) or ch == '_' or char::isDigit(ch) {
383 +
            advance(s);
384 +
        }
385 +
        if consume(s, '\'') {
386 +
            return tok(s, TokenKind::Char);
387 +
        }
388 +
        return tok(s, TokenKind::Region);
389 +
    }
378 390
    if let tok = scanDelimited(s, '\'', TokenKind::Char) {
379 391
        return tok;
380 392
    }
381 393
    return invalid(s.token, "unterminated character");
382 394
}
lib/std/lang/scanner/tests.rad +30 -1
219 219
        try testing::expect(super::next(&mut s).kind == expectedKind);
220 220
    }
221 221
}
222 222
223 223
@test unsafe fn testScanKeywords() throws (testing::TestError) {
224 -
    let mut s = testScanner("nil mod not static unsafe");
224 +
    let mut s = testScanner("nil mod not static unsafe where");
225 225
    let tok1: super::Token = super::next(&mut s);
226 226
227 227
    try testing::expect(tok1.kind == super::TokenKind::Nil);
228 228
    try testing::expect(tok1.source.len == 3);
229 229
240 240
    try testing::expect(tok4.source.len == 6);
241 241
242 242
    let tok5: super::Token = super::next(&mut s);
243 243
    try testing::expect(tok5.kind == super::TokenKind::Unsafe);
244 244
    try testing::expect(tok5.source.len == 6);
245 +
246 +
    let tok6: super::Token = super::next(&mut s);
247 +
    try testing::expect(tok6.kind == super::TokenKind::Where);
248 +
    try testing::expect(tok6.source.len == 5);
245 249
}
246 250
247 251
@test unsafe fn testScanVoidAsIdent() throws (testing::TestError) {
248 252
    let mut s = testScanner("void");
249 253
    let tok: super::Token = super::next(&mut s);
318 322
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
319 323
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Semicolon);
320 324
    try testing::expect(super::next(&mut s).kind == super::TokenKind::RBrace);
321 325
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
322 326
}
327 +
328 +
/// Region tokens preserve their names and do not consume character literals.
329 +
@test unsafe fn testScanRegions() throws (testing::TestError) {
330 +
    let mut s = testScanner("'x 'long_2 'x' '\\n' 'outer<'inner");
331 +
    let first = super::next(&mut s);
332 +
    try testing::expect(first.kind == super::TokenKind::Region);
333 +
    try testing::expect(mem::eq(first.source, "'x"));
334 +
    let second = super::next(&mut s);
335 +
    try testing::expect(second.kind == super::TokenKind::Region);
336 +
    try testing::expect(mem::eq(second.source, "'long_2"));
337 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Char);
338 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Char);
339 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Region);
340 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Lt);
341 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Region);
342 +
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
343 +
}
344 +
345 +
/// An apostrophe without a name or a closing quote is invalid.
346 +
@test unsafe fn testScanMalformedRegions() throws (testing::TestError) {
347 +
    for text in ["'", "'1", "'\\", "'\n"] {
348 +
        let mut s = testScanner(text);
349 +
        try testing::expect(super::next(&mut s).kind == super::TokenKind::Invalid);
350 +
    }
351 +
}
lib/std/lang/types.rad +43 -0
4 4
export union PointerClass: Copy {
5 5
    /// Owned pointer, eg. `*T`.
6 6
    Owned,
7 7
    /// Reference, borrowed pointer, eg. `&T`.
8 8
    Ref,
9 +
    /// Reference retained by one resolved lexical region.
10 +
    Region(*unsafe Region),
9 11
    /// Unsafe, raw pointer, eg. `*unsafe T`.
10 12
    Unsafe,
11 13
}
14 +
15 +
/// Source construct that introduces a region identity.
16 +
export union RegionOrigin: Copy {
17 +
    /// Caller-supplied declaration parameter.
18 +
    Parameter,
19 +
    /// Concrete region delimited by a lexical block.
20 +
    Block,
21 +
}
22 +
23 +
/// Semantic identity and ancestry of a source region.
24 +
export record Region: Copy {
25 +
    /// Globally unique AST node ID of the region declaration.
26 +
    id: u32,
27 +
    /// Source construct that supplies the region lifetime.
28 +
    origin: RegionOrigin,
29 +
    /// Source spelling used in diagnostics.
30 +
    name: *[u8],
31 +
    /// Proven enclosing region, if one is declared.
32 +
    parent: ?*unsafe Region,
33 +
}
34 +
35 +
/// Return whether a pointer class denotes checked borrowed storage.
36 +
export fn isReference(class: PointerClass) -> bool {
37 +
    match class {
38 +
        case PointerClass::Ref, PointerClass::Region(_) => return true,
39 +
        else => return false,
40 +
    }
41 +
}
42 +
43 +
/// Return whether a region is equal to, or contains, another region.
44 +
/// Parent links must form an acyclic graph within one resolver.
45 +
export unsafe fn regionContains(parent: *unsafe Region, child: *unsafe Region) -> bool {
46 +
    let mut current: ?*unsafe Region = child;
47 +
    while let region = current {
48 +
        if region.id == parent.id {
49 +
            return true;
50 +
        }
51 +
        set current = region.parent;
52 +
    }
53 +
    return false;
54 +
}
seed/radiance.rv64 +0 -0

Binary file changed.

seed/radiance.rv64.git +1 -1
1 -
cbc895eb5c6c569c7648e51c5e4ff0579de8b925415ceb368100d07370b9fd7e
1 +
4a24fa5e2f0b82d0aa1f922caa85dcf831d22cb8828365820197f8ebb13f55b4
std.lib +1 -0
51 51
lib/std/lang/gen/regalloc.rad
52 52
lib/std/lang/gen/regalloc/liveness.rad
53 53
lib/std/lang/gen/regalloc/spill.rad
54 54
lib/std/lang/gen/regalloc/assign.rad
55 55
lib/std/arch/rv64/shared.rad
56 +
lib/std/arch/rv64/shared/catalog.rad
56 57
lib/std/arch/rv64/atomics.rad
std.lib.test +4 -1
3 3
lib/std/char/tests.rad
4 4
lib/std/arch/rv64/tests.rad
5 5
lib/std/arch/rv64/asm/tests.rad
6 6
lib/std/arch/rv64/asm/scanner/tests.rad
7 7
lib/std/lang/alloc/tests.rad
8 +
lib/std/lang/gen/bitset/tests.rad
9 +
lib/std/lang/gen/regalloc/liveness/tests.rad
10 +
lib/std/lang/il/tests.rad
8 11
lib/std/lang/parser/tests.rad
9 12
lib/std/lang/module/tests.rad
10 13
lib/std/lang/scanner/tests.rad
11 14
lib/std/lang/resolver/tests.rad
12 -
lib/std/lang/gen/bitset/tests.rad
13 15
lib/std/lang/il/binary/tests.rad
14 16
lib/std/lang/il/binary/decodeTests.rad
15 17
lib/std/arch/rv64/image/tests.rad
16 18
lib/std/arch/rv64/shared/tests.rad
17 19
lib/std/arch/rv64/bounds.rad
18 20
lib/std/arch/rv64/atomicTests.rad
21 +
lib/std/lang/resolver/tests/regions.rad
test/runner.rad +30 -28
259 259
        io::printLn("error: parsing failed");
260 260
        return false;
261 261
    };
262 262
263 263
    // Run resolver.
264 +
    let mut resolverArena = alloc::new(&mut RESOLVER_ARENA_STORAGE[..]);
264 265
    let storage = resolver::ResolverStorage {
265 -
        arena: alloc::new(&mut RESOLVER_ARENA_STORAGE[..]),
266 266
        nodeData: &mut NODE_DATA_STORAGE[..],
267 267
        pkgScope: &mut pkgScope,
268 268
        errors: &mut ERROR_STORAGE[..],
269 269
    };
270 270
    let config = resolver::Config { buildTest: false };
271 -
    let mut res = resolver::resolver(storage, config);
272 -
    let diag = try resolver::resolveModuleRoot(&mut res, root) catch {
273 -
        io::printLn("error: resolver failed");
274 -
        return false;
275 -
    };
276 -
    if not resolver::success(&diag) {
277 -
        io::printLn("error: resolver failed");
278 -
        return false;
279 -
    }
271 +
    let arenaRef: 'arena = &mut resolverArena in {
272 +
        let mut res = resolver::resolver(arenaRef, storage, config);
273 +
        let diag = try resolver::resolveModuleRoot(&mut res, root) catch {
274 +
            io::printLn("error: resolver failed");
275 +
            return false;
276 +
        };
277 +
        if not resolver::success(&diag) {
278 +
            io::printLn("error: resolver failed");
279 +
            return false;
280 +
        }
280 281
281 -
    // Lower to IL.
282 -
    let mut ilArena = alloc::new(&mut IL_ARENA_STORAGE[..]);
283 -
    let program = try lower::lower(&res, root, "test", &mut ilArena) catch err {
284 -
        io::print("error: lowering failed: ");
285 -
        lower::printError(err);
286 -
        io::printLn("");
287 -
        return false;
288 -
    };
282 +
        // Lower to IL.
283 +
        let mut ilArena = alloc::new(&mut IL_ARENA_STORAGE[..]);
284 +
        let program = try lower::lower(&res, root, "test", &mut ilArena) catch err {
285 +
            io::print("error: lowering failed: ");
286 +
            lower::printError(err);
287 +
            io::printLn("");
288 +
            return false;
289 +
        };
289 290
290 -
    // Print IL to buffer.
291 -
    let actual = printer::printProgramToBuffer(&program, &mut OUTPUT_BUF[..]);
291 +
        // Print IL to buffer.
292 +
        let actual = printer::printProgramToBuffer(&program, &mut OUTPUT_BUF[..]);
292 293
293 -
    // Compare ignoring comments.
294 -
    if not stringsEqual(actual, expected) {
295 -
        io::printLn("FAILED");
296 -
        printDiff(expected, actual);
297 -
        return false;
298 -
    }
299 -
    io::printLn("ok");
294 +
        // Compare ignoring comments.
295 +
        if not stringsEqual(actual, expected) {
296 +
            io::printLn("FAILED");
297 +
            printDiff(expected, actual);
298 +
            return false;
299 +
        }
300 +
        io::printLn("ok");
300 301
301 -
    return true;
302 +
        return true;
303 +
    }
302 304
}
303 305
304 306
/// Run a single test specified as an argument.
305 307
@default unsafe fn main(env: *sys::Env) -> i32 {
306 308
    let args = env.args;
test/tests/const.record.mutcopy.ril +5 -2
18 18
  @then1
19 19
    copy %6 $SCRATCH2;
20 20
    blit %2 %6 1;
21 21
    jmp @merge2;
22 22
  @merge2
23 -
    load w64 %8 %2 0;
24 -
    ret %8;
23 +
    reserve %8 8 8;
24 +
    store w64 0 %8 0;
25 +
    blit %8 %2 1;
26 +
    load w64 %9 %8 0;
27 +
    ret %9;
25 28
}
test/tests/field.aggregate.ril +5 -2
19 19
    store w8 1 %2 0;
20 20
    add w64 %3 %2 4;
21 21
    blit %3 %1 4;
22 22
    blit %0 %2 8;
23 23
    store w32 99 %0 8;
24 -
    load w64 %4 %0 0;
25 -
    ret %4;
24 +
    reserve %4 8 8;
25 +
    store w64 0 %4 0;
26 +
    blit %4 %0 8;
27 +
    load w64 %5 %4 0;
28 +
    ret %5;
26 29
}
27 30
28 31
fn w32 $accessSliceField() {
29 32
  @entry0
30 33
    reserve %0 12 4;
test/tests/let.copy.semantics.ril +5 -2
1 1
fn w64 $makePoint() {
2 2
  @entry0
3 3
    reserve %0 8 4;
4 4
    store w32 1 %0 0;
5 5
    store w32 2 %0 4;
6 -
    load w64 %1 %0 0;
7 -
    ret %1;
6 +
    reserve %1 8 8;
7 +
    store w64 0 %1 0;
8 +
    blit %1 %0 8;
9 +
    load w64 %2 %1 0;
10 +
    ret %2;
8 11
}
9 12
10 13
fn w32 $letFromCall() {
11 14
  @entry0
12 15
    call w64 %0 $makePoint();
test/tests/record.ctor.tuple.ril +5 -2
1 1
fn w64 $make() {
2 2
  @entry0
3 3
    reserve %0 8 4;
4 4
    store w32 1 %0 0;
5 5
    store w32 2 %0 4;
6 -
    load w64 %1 %0 0;
7 -
    ret %1;
6 +
    reserve %1 8 8;
7 +
    store w64 0 %1 0;
8 +
    blit %1 %0 8;
9 +
    load w64 %2 %1 0;
10 +
    ret %2;
8 11
}
test/tests/slice.append.rad +1 -1
9 9
10 10
fn newArena(data: *mut [u8]) -> Arena {
11 11
    return Arena { data, offset: 0 };
12 12
}
13 13
14 -
fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque {
14 +
unsafe fn arenaAlloc(arena: *unsafe mut Arena, size: u32, al: u32) -> *mut opaque {
15 15
    let aligned = (arena.offset + al - 1) / al * al;
16 16
    let newOffset = aligned + size;
17 17
18 18
    assert newOffset <= arena.data.len as u32;
19 19
test/tests/union.ctor.ril +10 -4
14 14
    store w32 7 %0 0;
15 15
    reserve %1 8 4;
16 16
    store w8 1 %1 0;
17 17
    add w64 %2 %1 4;
18 18
    blit %2 %0 4;
19 -
    load w64 %3 %1 0;
20 -
    ret %3;
19 +
    reserve %3 8 8;
20 +
    store w64 0 %3 0;
21 +
    blit %3 %1 8;
22 +
    load w64 %4 %3 0;
23 +
    ret %4;
21 24
}
22 25
23 26
fn w64 $makeNone() {
24 27
  @entry0
25 28
    reserve %0 8 4;
26 29
    store w8 0 %0 0;
27 -
    load w64 %1 %0 0;
28 -
    ret %1;
30 +
    reserve %1 8 8;
31 +
    store w64 0 %1 0;
32 +
    blit %1 %0 8;
33 +
    load w64 %2 %1 0;
34 +
    ret %2;
29 35
}
test/tests/union.variant.access.ril +15 -6
10 10
11 11
fn w64 $makeNone() {
12 12
  @entry0
13 13
    reserve %0 8 4;
14 14
    store w8 0 %0 0;
15 -
    load w64 %1 %0 0;
16 -
    ret %1;
15 +
    reserve %1 8 8;
16 +
    store w64 0 %1 0;
17 +
    blit %1 %0 8;
18 +
    load w64 %2 %1 0;
19 +
    ret %2;
17 20
}
18 21
19 22
fn w64 $makeSome(w32 %0) {
20 23
  @entry0
21 24
    reserve %1 4 4;
22 25
    store w32 %0 %1 0;
23 26
    reserve %2 8 4;
24 27
    store w8 1 %2 0;
25 28
    add w64 %3 %2 4;
26 29
    blit %3 %1 4;
27 -
    load w64 %4 %2 0;
28 -
    ret %4;
30 +
    reserve %4 8 8;
31 +
    store w64 0 %4 0;
32 +
    blit %4 %2 8;
33 +
    load w64 %5 %4 0;
34 +
    ret %5;
29 35
}
30 36
31 37
fn w64 $assignNone() {
32 38
  @entry0
33 39
    reserve %0 8 4;
34 40
    store w8 0 %0 0;
35 -
    load w64 %1 %0 0;
36 -
    ret %1;
41 +
    reserve %1 8 8;
42 +
    store w64 0 %1 0;
43 +
    blit %1 %0 8;
44 +
    load w64 %2 %1 0;
45 +
    ret %2;
37 46
}
vim/radiance.vim +7 -2
16 16
17 17
" Keywords
18 18
syntax keyword radianceKeyword mod fn return if else while true false and or not case align static
19 19
syntax keyword radianceKeyword export break continue use loop in for match nil undefined
20 20
syntax keyword radianceKeyword let mut set as register device constant log record union trait instance
21 -
syntax keyword radianceKeyword throws throw try catch panic assert super unsafe
21 +
syntax keyword radianceKeyword throws throw try catch panic assert super unsafe where
22 22
syntax keyword radianceType i8 i16 i32 i64 u8 u16 u32 u64 f32 void bool bit opaque Copy Once
23 23
24 24
" Double-quoted strings
25 25
syntax region radianceString start=/"/ skip=/\\"/ end=/"/ contains=radianceEscape
26 26
" Characters
27 -
syntax region radianceCharacter start=/'/ skip=/\\'|\\\\/ end=/'/ oneline contains=radianceEscape
27 +
syntax match radianceCharacter /'\h\w*'/ contains=radianceEscape
28 +
syntax region radianceCharacter start=/'\%(\h\)\@!/ skip=/\\'|\\\\/ end=/'/ oneline contains=radianceEscape
29 +
" Lexical regions
30 +
syntax match radianceRegion "'\h\w*\%('\)\@!"
28 31
" String escapes
29 32
syntax match radianceEscape contained /\\["'nrtvfab\\]/
30 33
" Compiler built-in
31 34
syntax match radianceBuiltin "@\h\w\+"
32 35
36 39
syntax match radianceNumber "\<\d\+\>"
37 40
syntax match radiancePlaceholder "\<_\>"
38 41
39 42
" Function names
40 43
syntax match radianceFunction "\<\h\w*\>\s*?("
44 +
syntax match radianceFunction "\<\h\w*\>\ze\s*\%('\h\w*\s*\)*("
41 45
42 46
" Operators
43 47
syntax match radianceOperator "[:!?&\-+*/=<>]"
44 48
syntax match radianceOperator "/"  contains=radianceComment
45 49
66 70
highlight default link radianceBraces Delimiter
67 71
highlight default link radianceParens Delimiter
68 72
highlight default link radianceBrackets Delimiter
69 73
highlight default link radianceString String
70 74
highlight default link radianceCharacter Character
75 +
highlight default link radianceRegion Type
71 76
highlight default link radianceEscape SpecialChar
72 77
highlight default link radianceNamespaceAccess Normal
73 78
74 79
let b:current_syntax = "radiance"