Enforce move semantics for safe mutable pointers

27ce65c61e48bbadad9bf415f1c80ccedacd3220b63120c4d7e46d8a8176c407
Treat safe mutable pointers, slices, and trait objects as move-only values.
Reject Copy implementations that contain them and preserve temporary call
borrows. Use raw pointers for shared mutable compiler graphs and owned or
immutable storage for tables, signatures, and diagnostic snapshots.

Validate the slice.append allocator contract and use implicit raw borrows
throughout compiler call sites.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent 3b15770b
compiler/radiance.rad +50 -40
142 142
    /// Number of assembly source paths.
143 143
    asmPathCount: u32,
144 144
}
145 145
146 146
/// Compilation context.
147 -
record CompileContext: Copy {
147 +
record CompileContext {
148 148
    /// Array of packages to compile.
149 149
    packages: [package::Package; MAX_PACKAGES],
150 150
    /// Driver inputs for each package slot.
151 151
    inputs: [PackageInput; MAX_PACKAGES],
152 152
    /// Number of packages.
166 166
}
167 167
168 168
/// Root module info for a package.
169 169
record RootModule: Copy {
170 170
    entry: *module::ModuleEntry,
171 -
    ast: *mut ast::Node,
171 +
    ast: *ast::Node,
172 172
}
173 173
174 174
/// Entry handling for streamed code generation.
175 175
union CodegenEntryMode: Copy {
176 176
    /// Do not reserve an entry jump.
245 245
    graph: &mut module::ModuleGraph,
246 246
    path: *[u8],
247 247
    nodeArena: &mut ast::NodeArena,
248 248
    sourceArena: &mut alloc::Arena
249 249
) throws (Error) {
250 -
    pkgLog(&*pkg, &["parsing", "(", path, ")", ".."]);
250 +
    pkgLog(pkg, &["parsing", "(", path, ")", ".."]);
251 251
252 -
    let moduleId = try package::registerModule(&mut *pkg, graph, path) catch {
252 +
    let moduleId = try package::registerModule(pkg, graph, &mut STRING_POOL, path) catch {
253 253
        throw error(&["error registering module"]);
254 254
    };
255 255
    // Read file into remaining arena space.
256 256
    let buffer = alloc::remainingBuf(sourceArena);
257 257
    if buffer.len == 0 {
415 415
    for i in 0..pkgCount {
416 416
        if i <> entryIdx and inputs[i].startupPath <> nil {
417 417
            throw error(&["`-start` is only supported on the entry package"]);
418 418
        }
419 419
    }
420 -
    let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut STRING_POOL, arena);
420 +
    let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena);
421 421
    let mut ctx = CompileContext {
422 422
        packages: undefined,
423 423
        inputs,
424 424
        packageCount: pkgCount,
425 425
        entryPkgIdx,
442 442
    }
443 443
    return ctx;
444 444
}
445 445
446 446
/// Get the entry package from the context.
447 -
unsafe fn getEntryPackage(ctx: *unsafe CompileContext) -> *unsafe package::Package throws (Error) {
447 +
fn getEntryPackage(ctx: &CompileContext) -> package::Package throws (Error) {
448 448
    let entryIdx = ctx.entryPkgIdx else {
449 449
        throw error(&["no entry package specified"]);
450 450
    };
451 -
    return &ctx.packages[entryIdx];
451 +
    return ctx.packages[entryIdx];
452 452
}
453 453
454 454
/// Return the startup assembly path for the entry package, if one was supplied.
455 455
fn getEntryStartupPath(ctx: &CompileContext) -> ?*[u8] {
456 456
    let entryIdx = ctx.entryPkgIdx else {
478 478
    let mut arena = alloc::new(&mut MAIN_ARENA[..]);
479 479
    module::printer::printGraph(&ctx.graph, &mut arena);
480 480
}
481 481
482 482
/// Dump the parsed AST.
483 -
unsafe fn dumpAst(ctx: *unsafe CompileContext) throws (Error) {
483 +
unsafe fn dumpAst(ctx: &CompileContext) throws (Error) {
484 484
    let pkg = try getEntryPackage(ctx);
485 -
    let root = try getRootModule(&*pkg, &ctx.graph);
485 +
    let root = try getRootModule(&pkg, &ctx.graph);
486 486
    let mut arena = alloc::new(&mut MAIN_ARENA[..]);
487 487
488 488
    ast::printer::printTree(root.ast, &mut arena);
489 489
}
490 490
505 505
        res, &ctx.graph, entryPkg.name, &mut res.arena, &mut res.arena, options
506 506
    );
507 507
    try lowerAllPackagesInto(ctx, res, &mut low);
508 508
509 509
    // Finalize and return the unified program.
510 -
    return lower::finalize(&low);
510 +
    return lower::finalize(low);
511 511
}
512 512
513 513
/// Lower all packages into an existing lowerer.
514 514
unsafe fn lowerAllPackagesInto(
515 515
    ctx: *unsafe mut CompileContext,
520 520
        panic "lowerAllPackagesInto: no entry package";
521 521
    };
522 522
    // Lower all packages except entry.
523 523
    for i in 0..ctx.packageCount {
524 524
        if i <> entryIdx {
525 -
            try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[i], false);
525 +
            try lowerPackage(ctx, res, low, &ctx.packages[i], false);
526 526
        }
527 527
    }
528 528
    // Lower entry package.
529 -
    try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[entryIdx], true);
529 +
    try lowerPackage(ctx, res, low, &ctx.packages[entryIdx], true);
530 530
}
531 531
532 532
/// Lower all modules in a package into the lowerer accumulator.
533 533
unsafe fn lowerPackage(
534 -
    ctx: *unsafe CompileContext,
534 +
    ctx: &CompileContext,
535 535
    res: *unsafe mut resolver::Resolver,
536 536
    low: &mut lower::Lowerer,
537 -
    pkg: &mut package::Package,
537 +
    pkg: &package::Package,
538 538
    isEntry: bool
539 539
) throws (Error) {
540 540
    let rootId = pkg.rootModuleId else {
541 541
        throw error(&["no root module found"]);
542 542
    };
543 543
    // Set lowerer's package context for qualified name generation.
544 544
    // TODO: We shouldn't have to call this manually.
545 -
    lower::setPackage(&mut *low, &ctx.graph, pkg.name);
545 +
    lower::setPackage(low, &ctx.graph, pkg.name);
546 546
547 -
    try lowerModuleTreeInto(ctx, &mut *low, &ctx.graph, rootId, isEntry, pkg);
547 +
    try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg);
548 548
}
549 549
550 550
/// Recursively lower a module and all its children into the accumulator.
551 551
unsafe fn lowerModuleTreeInto(
552 -
    ctx: *unsafe CompileContext,
552 +
    ctx: &CompileContext,
553 553
    low: &mut lower::Lowerer,
554 554
    graph: &module::ModuleGraph,
555 555
    modId: u16,
556 556
    isRoot: bool,
557 557
    pkg: &package::Package
560 560
        throw error(&["module entry not found"]);
561 561
    };
562 562
    let modAst = entry.ast else {
563 563
        throw error(&["module has no AST"]);
564 564
    };
565 -
    pkgLog(&*pkg, &["lowering", "(", entry.filePath, ")", ".."]);
565 +
    pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]);
566 566
567 -
    try lower::lowerModule(&mut *low, modId, modAst, isRoot) catch err {
567 +
    try lower::lowerModule(low, modId, modAst, isRoot) catch err {
568 568
        io::printError("radiance: ");
569 569
        io::printError("internal error during lowering: ");
570 570
        lower::printError(err);
571 571
        io::printError("\n");
572 572
573 573
        throw Error::Other;
574 574
    };
575 575
    // Recurse into children.
576 576
    for i in 0..entry.childrenLen {
577 577
        let childId = module::childAt(entry, i);
578 -
        try lowerModuleTreeInto(ctx, &mut *low, graph, childId, false, pkg);
578 +
        try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg);
579 579
    }
580 580
}
581 581
582 582
/// Build a scope access chain: a::b::c from a slice of identifiers.
583 583
unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node {
683 683
unsafe fn generateTestRunner(
684 684
    ctx: *unsafe mut CompileContext,
685 685
    arena: &mut ast::NodeArena
686 686
) throws (Error) {
687 687
    let entryPkg = try getEntryPackage(ctx);
688 -
    let root = try getRootModule(&*entryPkg, &ctx.graph);
688 +
    let root = try getRootModule(&entryPkg, &ctx.graph);
689 689
690 690
    // Collect all test functions across all modules.
691 691
    let mut tests: [TestDesc; MAX_TESTS] = undefined;
692 692
    let mut testCount: u32 = 0;
693 693
708 708
    io::printError(" test(s)\n");
709 709
710 710
    // Synthesize the `@default` function and append to the root module.
711 711
    let fnDecl = synthTestMainFn(arena, &tests[..testCount]);
712 712
713 -
    injectIntoBlock(root.ast, arena, fnDecl);
713 +
    let updatedRoot = injectIntoBlock(root.ast, arena, fnDecl);
714 +
    try module::setAst(&mut ctx.graph, root.entry.id, updatedRoot) catch {
715 +
        throw error(&["failed to set test runner AST"]);
716 +
    };
714 717
}
715 718
716 719
/// Synthesize the test entry point.
717 720
unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[TestDesc]) -> *ast::Node {
718 721
    // Build array literal: `[testing::test(...), ...]`.
760 763
    return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl {
761 764
        name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs,
762 765
    }));
763 766
}
764 767
765 -
/// Append a declaration to a block node's statement list.
768 +
/// Build a block node with a declaration appended to its statement list.
766 769
unsafe fn injectIntoBlock(
767 -
    blockNode: *mut ast::Node,
770 +
    blockNode: *ast::Node,
768 771
    arena: &mut ast::NodeArena,
769 772
    decl: *ast::Node
770 -
) {
773 +
) -> *ast::Node {
771 774
    let case ast::NodeValue::Block(block) = blockNode.value else {
772 775
        panic "injectIntoBlock: expected Block node";
773 776
    };
774 -
    let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena));
775 -
    set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts, isUnsafe: block.isUnsafe });
777 +
    let allocator = alloc::arenaAllocator(&mut arena.arena);
778 +
    let mut stmts = ast::nodeSlice(arena, block.statements.len + 1);
779 +
    for stmt in block.statements {
780 +
        stmts.append(stmt, allocator);
781 +
    }
782 +
    stmts.append(decl, allocator);
783 +
    return ast::allocNode(arena, blockNode.span, ast::NodeValue::Block(
784 +
        ast::Block { statements: stmts, isUnsafe: block.isUnsafe }
785 +
    ));
776 786
}
777 787
778 788
/// Write a self-contained RV64 image containing text and data sections.
779 789
unsafe fn writeImage(
780 790
    code: *[u32],
786 796
    let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32);
787 797
    let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32);
788 798
789 799
    let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420);
790 800
    if fd < 0 { return false; }
791 -
    let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes)
801 +
    let written = unix::writeAll(fd, headerBytes) and unix::writeAll(fd, codeBytes)
792 802
        and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData);
793 803
    let closed = unix::close(fd) == 0;
794 804
    return written and closed;
795 805
}
796 806
847 857
/// Run the resolver on the parsed modules.
848 858
unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) {
849 859
    let mut mainArena = alloc::new(&mut MAIN_ARENA[..]);
850 860
    let entryPkg = try getEntryPackage(ctx);
851 861
852 -
    pkgLog(&*entryPkg, &["resolving", ".."]);
862 +
    pkgLog(&entryPkg, &["resolving", ".."]);
853 863
854 864
    let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData);
855 865
    let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData));
856 866
    let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount);
857 867
    let storage = resolver::ResolverStorage {
865 875
    // Build the semantic package list consumed by the resolver.
866 876
    let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined;
867 877
    let mut resolverPackageCount: u32 = 0;
868 878
    for i in 0..ctx.packageCount {
869 879
        let pkg = &ctx.packages[i];
870 -
        let root = try getRootModule(&*pkg, &ctx.graph);
880 +
        let root = try getRootModule(pkg, &ctx.graph);
871 881
872 882
        set resolverPkgs[resolverPackageCount] = resolver::Pkg {
873 883
            rootEntry: root.entry,
874 884
            rootAst: root.ast,
875 885
        };
877 887
    }
878 888
879 889
    // Resolve all packages.
880 890
    // TODO: Fix this error printing dance.
881 891
    let diags = try resolver::resolve(&mut res, &ctx.graph, &resolverPkgs[..resolverPackageCount]) catch {
882 -
        let diags = resolver::Diagnostics { errors: res.errors };
892 +
        let diags = resolver::diagnostics(&mut res);
883 893
        resolver::printer::printDiagnostics(&diags, &res);
884 894
        throw Error::Other;
885 895
    };
886 896
    if not resolver::success(&diags) {
887 897
        resolver::printer::printDiagnostics(&diags, &res);
905 915
    pkg: &package::Package,
906 916
    path: *[u8],
907 917
    asmDataLen: &mut u32,
908 918
    arena: &mut alloc::Arena
909 919
) throws (Error) {
910 -
    pkgLog(&*pkg, &["asm:", "parsing", "(", path, ")", ".."]);
920 +
    pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]);
911 921
912 922
    let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else {
913 923
        throw error(&["error reading assembly file"]);
914 924
    };
915 925
    let source = &ASM_SOURCE_BUF[..sourceLen];
966 976
) -> rv64::Program throws (Error) {
967 977
    let entryIdx = ctx.entryPkgIdx else {
968 978
        panic "lowerAndGenerateAllPackages: no entry package";
969 979
    };
970 980
    let entryPkg = &ctx.packages[entryIdx];
971 -
    let startupPath = getEntryStartupPath(&*ctx);
981 +
    let startupPath = getEntryStartupPath(ctx);
972 982
    let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest };
973 983
    let storage = rv64::Storage {
974 984
        dataSyms: &mut CODEGEN_DATA_SYMS[..],
975 985
        dataSymEntries: &mut CODEGEN_DATA_SYM_ENTRIES[..],
976 986
    };
985 995
        rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug },
986 996
        &mut res.arena
987 997
    );
988 998
    let mut codegenCtx = codegen::Context {
989 999
        generator: &mut generator,
990 -
        fnArena: fnArena as *unsafe mut alloc::Arena,
1000 +
        fnArena: (&mut *fnArena) as *unsafe mut alloc::Arena,
991 1001
    };
992 1002
    let mut low = lower::lowerer(
993 1003
        res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options
994 1004
    );
995 1005
    set low.output = lower::FnOutput::Stream(lower::FnSink {
996 1006
        ctx: &mut codegenCtx as *unsafe mut opaque,
997 1007
        emitFn: codegen::emit,
998 1008
    });
999 1009
    let mut asmDataLen: u32 = 0;
1000 1010
    if let path = startupPath {
1001 -
        try assembleAsmModule(&mut generator, &*entryPkg, path, &mut asmDataLen, &mut res.arena);
1011 +
        try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena);
1002 1012
    }
1003 1013
    try lowerAllPackagesInto(ctx, res, &mut low);
1004 -
    let asmData = try assembleAsmInputs(&*ctx, &mut generator, &mut asmDataLen, &mut res.arena);
1014 +
    let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena);
1005 1015
1006 1016
    match generator.entryPatch {
1007 1017
        case rv64::EntryPatch::Reserved(targetName) => {
1008 1018
            if targetName == nil {
1009 1019
                throw error(&["fatal:", "no default function found"]);
1010 1020
            }
1011 1021
        }
1012 1022
        else => {}
1013 1023
    }
1014 1024
    if let path = codegenOptions.logPath {
1015 -
        pkgLog(&*entryPkg, &["generating code", "(", path, ")", ".."]);
1025 +
        pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1016 1026
    }
1017 -
    return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]);
1027 +
    return rv64::finishProgram(&mut generator, low.data, storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]);
1018 1028
}
1019 1029
1020 1030
/// Lower, optionally dump, and optionally generate binary output.
1021 1031
unsafe fn compile(
1022 1032
    ctx: *unsafe mut CompileContext,
1047 1057
    // Generate binary output if path specified.
1048 1058
    let outPath = ctx.outputPath else {
1049 1059
        try lowerAllPackages(ctx, res);
1050 1060
        return;
1051 1061
    };
1052 -
    let startupPath = getEntryStartupPath(&*ctx);
1062 +
    let startupPath = getEntryStartupPath(ctx);
1053 1063
    let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions {
1054 1064
        logPath: outPath,
1055 1065
        debug: ctx.debug,
1056 1066
        entryMode: CodegenEntryMode::None
1057 1067
            if startupPath <> nil
1069 1079
1070 1080
    // Write debug info file if enabled.
1071 1081
    if ctx.debug {
1072 1082
        try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena);
1073 1083
    }
1074 -
    pkgLog(&*entryPkg, &["ok", "(", outPath, ")"]);
1084 +
    pkgLog(&entryPkg, &["ok", "(", outPath, ")"]);
1075 1085
}
1076 1086
1077 1087
@default unsafe fn main(env: *sys::Env) -> i32 {
1078 1088
    let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]);
1079 1089
    let mut ctx = try processCommand(env.args, &mut arena) catch {
compiler/radiance/codegen.rad +4 -4
13 13
    fnArena: *unsafe mut alloc::Arena,
14 14
}
15 15
16 16
/// Emit one lowered function to machine code and reclaim its IL arena.
17 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: *il::Fn, role: lower::FnRole) {
18 +
export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *unsafe il::Fn, role: lower::FnRole) {
19 19
    let ctx = ctxPtr as *unsafe mut Context;
20 20
21 21
    match role {
22 22
        case lower::FnRole::Default => {
23 -
            rv64::recordFunctionAlias(&mut *ctx.generator, super::DEFAULT_ENTRY_SYMBOL);
23 +
            rv64::recordFunctionAlias(ctx.generator, super::DEFAULT_ENTRY_SYMBOL);
24 24
            match ctx.generator.entryPatch {
25 25
                case rv64::EntryPatch::Reserved(_) => {
26 26
                    set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name);
27 27
                }
28 28
                // Startup assembly calls the default function through its
32 32
        }
33 33
        else => {}
34 34
    }
35 35
    let generator = ctx.generator;
36 36
    let arena = ctx.fnArena;
37 -
    rv64::generateFunction(&mut *generator, func, &mut *arena);
38 -
    alloc::reset(&mut *ctx.fnArena);
37 +
    rv64::generateFunction(generator, func, arena);
38 +
    alloc::reset(ctx.fnArena);
39 39
}
lib/std/arch/rv64.rad +13 -11
159 159
export fn imageHeader(codeBytes: u32, roDataBytes: u32, rwDataBytes: u32) -> [u32; 5] {
160 160
    return [IMAGE_MAGIC, IMAGE_VERSION, codeBytes, roDataBytes, rwDataBytes];
161 161
}
162 162
163 163
/// Storage buffers passed from driver for code generation.
164 -
export record Storage: Copy {
164 +
export record Storage {
165 165
    /// Buffer for data symbols.
166 166
    dataSyms: *mut [data::DataSym],
167 167
    /// Hash table entries for data symbol lookup.
168 168
    dataSymEntries: *mut [dict::Entry],
169 169
}
201 201
/// State for incremental RV64 program generation.
202 202
///
203 203
/// The generator owns global codegen state that must survive across function
204 204
/// emission. Function-local scratch stays outside this record so callers can
205 205
/// reclaim it after each function.
206 -
export record Generator: Copy {
206 +
export record Generator {
207 207
    /// Binary emitter and relocation state.
208 208
    e: emit::Emitter,
209 209
    /// Entry jump patching state.
210 210
    entryPatch: EntryPatch,
211 211
}
234 234
}
235 235
236 236
/// Generate code for one IL function.
237 237
export unsafe fn generateFunction(
238 238
    generator: &mut Generator,
239 -
    func: *il::Fn,
239 +
    func: *unsafe il::Fn,
240 240
    arena: &mut alloc::Arena
241 241
) {
242 242
    if func.isExtern {
243 243
        return;
244 244
    }
299 299
}
300 300
301 301
/// Finish RV64 code generation and return the emitted program.
302 302
export unsafe fn finishProgram(
303 303
    generator: &mut Generator,
304 -
    globalData: *[il::Data],
304 +
    globalData: &[il::Data],
305 305
    storage: Storage,
306 306
    roDataPrefix: *[u8],
307 -
    roDataBuf: *mut [u8],
308 -
    rwDataBuf: *mut [u8]
307 +
    roDataBuf: &mut [u8],
308 +
    rwDataBuf: &mut [u8]
309 309
) -> Program {
310 310
    // Build data map after function lowering. Function-local literals can add
311 311
    // global data while functions are lowered, so final layout belongs here.
312 +
    let case Storage { dataSyms: symbolBuf, dataSymEntries } = storage
313 +
        else panic "expected code generation storage";
312 314
    let mut dataSymCount: u32 = 0;
313 315
    let roLayoutSize = data::layoutSectionAtOffset(
314 -
        globalData, storage.dataSyms, &mut dataSymCount, RO_DATA_BASE, roDataPrefix.len, true
316 +
        globalData, symbolBuf, &mut dataSymCount, RO_DATA_BASE, roDataPrefix.len, true
315 317
    );
316 -
    data::layoutSection(globalData, storage.dataSyms, &mut dataSymCount, RW_DATA_BASE, false);
318 +
    data::layoutSection(globalData, symbolBuf, &mut dataSymCount, RW_DATA_BASE, false);
317 319
318 -
    let dataSyms = &storage.dataSyms[..dataSymCount];
319 -
    let dataSymMap = data::buildMap(dataSyms, storage.dataSymEntries);
320 +
    let dataSyms = &symbolBuf[..dataSymCount];
321 +
    let dataSymMap = data::buildMap(dataSyms, dataSymEntries);
320 322
    let codeBase = mem::alignUp(RO_DATA_BASE + roLayoutSize, DWORD_SIZE as u32);
321 323
322 324
    match generator.entryPatch {
323 325
        case EntryPatch::Reserved(targetName) => {
324 326
            let target = targetName else {
347 349
    let rwDataSize = data::emitSection(
348 350
        globalData, &dataSymMap, &generator.e.labels, codeBase, rwDataBuf, false
349 351
    );
350 352
    return Program {
351 353
        code: emit::getCode(&generator.e),
352 -
        funcs: generator.e.funcs,
354 +
        funcs: &generator.e.funcs[..],
353 355
        roDataSize,
354 356
        rwDataSize,
355 357
        debugEntries: emit::getDebugEntries(&generator.e),
356 358
    };
357 359
}
lib/std/arch/rv64/asm.rad +13 -13
450 450
    /// Fixup payload.
451 451
    info: FixupInfo,
452 452
}
453 453
454 454
/// Parser and emission state.
455 -
export record Assembler: Copy {
455 +
export record Assembler {
456 456
    /// Allocation arena for temporary assembler state.
457 457
    arena: *unsafe mut alloc::Arena,
458 458
    /// Assembler lexical scanner.
459 459
    scan: scanner::Scanner,
460 460
    /// Output text buffer.
484 484
    sourceKind: scanner::SourceKind,
485 485
    source: *[u8],
486 486
    textBuf: *mut [u32],
487 487
    dataBuf: *mut [u8],
488 488
    arena: &mut alloc::Arena,
489 -
    pool: *mut strings::Pool,
489 +
    pool: *unsafe mut strings::Pool,
490 490
    dataBase: u32
491 491
) -> Program throws (Error) {
492 492
    let slotCap = source.len + SOURCE_CAP_PADDING;
493 493
    let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE);
494 494
497 497
    let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap);
498 498
    let entries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
499 499
    let constEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
500 500
    let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
501 501
502 +
    let symbolBuf = symbols as *mut [Symbol];
503 +
    let fixupBuf = fixups as *mut [Fixup];
504 +
    let externalBuf = externalFixups as *mut [Fixup];
502 505
    let mut a = Assembler {
503 506
        arena: arena as *unsafe mut alloc::Arena,
504 507
        scan: scanner::scanner(sourceKind, source, pool),
505 -
        text: @sliceOf(textBuf.ptr, 0, textBuf.len),
506 -
        data: @sliceOf(dataBuf.ptr, 0, dataBuf.len),
508 +
        text: &mut textBuf[..0],
509 +
        data: &mut dataBuf[..0],
507 510
        section: Section::Text,
508 -
        symbols: @sliceOf((symbols as *mut [Symbol]).ptr, 0, (symbols as *mut [Symbol]).len),
511 +
        symbols: &mut symbolBuf[..0],
509 512
        symbolMap: dict::init(entries as *mut [dict::Entry]),
510 513
        constMap: dict::init(constEntries as *mut [dict::Entry]),
511 514
        exportMap: dict::init(exportEntries as *mut [dict::Entry]),
512 -
        fixups: @sliceOf((fixups as *mut [Fixup]).ptr, 0, (fixups as *mut [Fixup]).len),
513 -
        externalFixups: @sliceOf((externalFixups as *mut [Fixup]).ptr, 0, (externalFixups as *mut [Fixup]).len),
515 +
        fixups: &mut fixupBuf[..0],
516 +
        externalFixups: &mut externalBuf[..0],
514 517
        dataBase,
515 518
    };
516 519
    // Parse assembly source and emit instructions.
517 520
    try parser::parseProgram(&mut a);
518 521
    // Resolve fixups and finalize program.
519 522
    try emit::finishProgram(&mut a);
520 523
521 -
    return Program {
522 -
        text: a.text,
523 -
        data: a.data,
524 -
        symbols: a.symbols,
525 -
        externalFixups: a.externalFixups,
526 -
    };
524 +
    let case Assembler { text, data, symbols: definedSymbols, externalFixups: pendingFixups, .. } = a
525 +
        else panic "expected assembler state";
526 +
    return Program { text, data, symbols: definedSymbols, externalFixups: pendingFixups };
527 527
}
528 528
529 529
/// Return the next power of two at least as large as `value`.
530 530
fn nextPowerOfTwo(value: u32) -> u32 {
531 531
    let mut n: u32 = MIN_TABLE_CAPACITY;
lib/std/arch/rv64/asm/emit.rad +5 -5
18 18
    a.symbols.append(super::Symbol {
19 19
        name,
20 20
        section: a.section,
21 21
        offset,
22 22
        isExported: dict::get(&a.exportMap, name) <> nil,
23 -
    }, alloc::arenaAllocator(&mut *a.arena));
23 +
    }, alloc::arenaAllocator(a.arena));
24 24
    dict::insert(&mut a.symbolMap, name, idx as i32);
25 25
}
26 26
27 27
/// Append one encoded instruction word to the text section.
28 28
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
29 -
    a.text.append(word, alloc::arenaAllocator(&mut *a.arena));
29 +
    a.text.append(word, alloc::arenaAllocator(a.arena));
30 30
}
31 31
32 32
/// Append `words` no-op instructions to the text section.
33 33
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
34 34
    for _ in 0..words {
36 36
    }
37 37
}
38 38
39 39
/// Append one byte to the data section.
40 40
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
41 -
    a.data.append(byte, alloc::arenaAllocator(&mut *a.arena));
41 +
    a.data.append(byte, alloc::arenaAllocator(a.arena));
42 42
}
43 43
44 44
/// Emit a little-endian integer with `bytes` bytes.
45 45
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
46 46
    for i in 0..bytes {
78 78
    }
79 79
}
80 80
81 81
/// Record a pending symbol fixup.
82 82
unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
83 -
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(&mut *a.arena));
83 +
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
84 84
}
85 85
86 86
/// Record a text fixup that must be resolved after all program text is known.
87 87
unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
88 -
    a.externalFixups.append(fixup, alloc::arenaAllocator(&mut *a.arena));
88 +
    a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
89 89
}
90 90
91 91
/// Record a text-section symbol fixup and reserve its instruction words.
92 92
export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
93 93
    recordFixup(a, symbol, info);
lib/std/arch/rv64/asm/parser.rad +25 -24
37 37
    }
38 38
    return mem::alignUp(value, alignment);
39 39
}
40 40
41 41
/// Advance the parser by one token, preserving the previous token.
42 -
fn advance(a: &mut super::Assembler) {
42 +
unsafe fn advance(a: &mut super::Assembler) {
43 43
    set a.scan.previous = a.scan.current;
44 44
    set a.scan.current = scanner::next(&mut a.scan);
45 45
}
46 46
47 47
/// Consume the current token when it has `kind`.
48 -
fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool {
48 +
unsafe fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool {
49 49
    if a.scan.current.kind == kind {
50 50
        advance(a);
51 51
        return true;
52 52
    }
53 53
    return false;
69 69
        throw failOnToken(tok, "data directive is only valid in the data section");
70 70
    }
71 71
}
72 72
73 73
/// Consume `kind` or throw `message` at the current token.
74 -
fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) {
74 +
unsafe fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) {
75 75
    if not consume(a, kind) {
76 76
        throw fail(a, message);
77 77
    }
78 78
}
79 79
80 80
/// Consume `kind` and return the consumed token.
81 -
fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) {
81 +
unsafe fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) {
82 82
    try expect(a, kind, message);
83 83
    return a.scan.previous;
84 84
}
85 85
86 86
/// Require that the current item has reached its semicolon terminator.
123 123
            try emit::emitDataValue(a, value, width),
124 124
    }
125 125
}
126 126
127 127
/// Parse a possibly scoped name from one or more `::`-separated segments.
128 -
fn parseScopedName(
128 +
unsafe fn parseScopedName(
129 129
    a: &mut super::Assembler,
130 130
    kind: scanner::TokenKind,
131 131
    message: *[u8],
132 132
    trimPrefix: u32
133 133
) -> *[u8] throws (super::Error) {
137 137
138 138
    while consume(a, scanner::TokenKind::ColonColon) {
139 139
        let segment = try expectToken(a, scanner::TokenKind::Ident, "expected identifier after `::`");
140 140
        set end = segment.offset + segment.source.len;
141 141
    }
142 -
    return strings::intern(a.scan.pool, &a.scan.source[start..end]);
142 +
    let source = &a.scan.source[start..end];
143 +
    return strings::intern(a.scan.pool, source);
143 144
}
144 145
145 146
/// Parse a bare symbol name.
146 -
fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
147 +
unsafe fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
147 148
    return try parseScopedName(a, scanner::TokenKind::Ident, "expected symbol name", 0);
148 149
}
149 150
150 151
/// Return `true` when [`tok`] is any label token form.
151 152
fn isLabel(tok: scanner::TokenKind) -> bool {
155 156
/// Parse the contents of a quoted label token, decoding escapes as needed.
156 157
unsafe fn parseQuotedLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
157 158
    let tok = try expectToken(a, scanner::TokenKind::QuotedLabel, "expected label name");
158 159
    let rawStart = super::LABEL_SIGIL_LEN + super::QUOTE_DELIM_LEN;
159 160
    let raw = &tok.source[rawStart..tok.source.len - super::QUOTE_DELIM_LEN];
160 -
    let storage = try alloc::allocSlice(&mut *a.arena, 1, 1, raw.len) catch {
161 +
    let storage = try alloc::allocSlice(a.arena, 1, 1, raw.len) catch {
161 162
        panic "asm: out of memory allocating quoted label";
162 163
    } as *mut [u8];
163 164
    let len = fmt::unescapeString(raw, storage);
164 165
165 166
    return strings::intern(a.scan.pool, &storage[..len]);
172 173
    }
173 174
    return try parseScopedName(a, scanner::TokenKind::Label, "expected label name", super::LABEL_SIGIL_LEN);
174 175
}
175 176
176 177
/// Parse a directive name without its leading `.`.
177 -
fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
178 +
unsafe fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
178 179
    let name = try expectToken(a, scanner::TokenKind::Directive, "expected directive name");
179 180
    return &name.source[super::DIRECTIVE_SIGIL_LEN..];
180 181
}
181 182
182 183
/// Parse one top-level assembler item.
544 545
        }
545 546
    }
546 547
}
547 548
548 549
/// Parse a `.constant` directive.
549 -
fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) {
550 +
unsafe fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) {
550 551
    let name = try parseSymbolName(a);
551 552
    let value = try expectI32Value(a, try parseExpr(a), "constant out of range");
552 553
553 554
    dict::insert(&mut a.constMap, name, value);
554 555
}
667 668
        }
668 669
    }
669 670
}
670 671
671 672
/// Parse and resolve a register operand.
672 -
fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) {
673 +
unsafe fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) {
673 674
    let tok = try expectToken(a, scanner::TokenKind::Register, "expected register");
674 675
    let reg = lookupRegister(&tok.source[1..]) else {
675 676
        throw super::Error::Invalid { offset: tok.offset, message: "unknown register" };
676 677
    };
677 678
    return reg;
678 679
}
679 680
680 681
/// Parse a simple signed immediate or constant value.
681 -
fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
682 +
unsafe fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
682 683
    if consume(a, scanner::TokenKind::Minus) {
683 684
        return -(try parseValuePrimary(a));
684 685
    }
685 686
    return try parseValuePrimary(a);
686 687
}
687 688
688 689
/// Parse the primary form used by simple immediate values.
689 -
fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
690 +
unsafe fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
690 691
    if a.scan.current.kind == scanner::TokenKind::Number {
691 692
        return try parseInteger(a);
692 693
    }
693 694
    if a.scan.current.kind == scanner::TokenKind::Ident {
694 695
        return try parseConstantValue(a);
695 696
    }
696 697
    throw fail(a, "expected number or constant");
697 698
}
698 699
699 700
/// Parse an additive constant expression.
700 -
fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) {
701 +
unsafe fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) {
701 702
    let mut value = try parseExprMul(a);
702 703
703 704
    while a.scan.current.kind == scanner::TokenKind::Plus or a.scan.current.kind == scanner::TokenKind::Minus {
704 705
        let op = a.scan.current.kind;
705 706
        advance(a);
713 714
    }
714 715
    return value;
715 716
}
716 717
717 718
/// Parse multiplicative expression operators.
718 -
fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) {
719 +
unsafe fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) {
719 720
    let mut value = try parseExprUnary(a);
720 721
721 722
    while a.scan.current.kind == scanner::TokenKind::Star or a.scan.current.kind == scanner::TokenKind::Slash {
722 723
        let op = a.scan.current.kind;
723 724
        advance(a);
734 735
    }
735 736
    return value;
736 737
}
737 738
738 739
/// Parse unary expression operators.
739 -
fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) {
740 +
unsafe fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) {
740 741
    if consume(a, scanner::TokenKind::Minus) {
741 742
        return -(try parseExprUnary(a));
742 743
    }
743 744
    if consume(a, scanner::TokenKind::Plus) {
744 745
        return try parseExprUnary(a);
745 746
    }
746 747
    return try parseExprPrimary(a);
747 748
}
748 749
749 750
/// Parse expression atoms.
750 -
fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
751 +
unsafe fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
751 752
    if consume(a, scanner::TokenKind::LParen) {
752 753
        let value = try parseExpr(a);
753 754
        try expect(a, scanner::TokenKind::RParen, "expected `)`");
754 755
        return value;
755 756
    }
761 762
    }
762 763
    throw fail(a, "expected expression");
763 764
}
764 765
765 766
/// Parse and resolve a named assembler constant.
766 -
fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
767 +
unsafe fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
767 768
    let name = try parseSymbolName(a);
768 769
    let value = dict::get(&a.constMap, name) else {
769 770
        throw super::Error::Invalid { offset: a.scan.previous.offset, message: "undefined constant" };
770 771
    };
771 772
    return value as i64;
772 773
}
773 774
774 775
/// Parse and resolve a CSR operand.
775 -
fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) {
776 +
unsafe fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) {
776 777
    let name = try parseSymbolName(a);
777 778
    let csr = lookupCsr(name) else {
778 779
        throw super::Error::Invalid { offset: a.scan.previous.offset, message: "unknown CSR" };
779 780
    };
780 781
    return csr;
781 782
}
782 783
783 784
/// Parse an offset(base) memory operand.
784 -
fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) {
785 +
unsafe fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) {
785 786
    let mut offset: i32 = 0;
786 787
    if a.scan.current.kind <> scanner::TokenKind::LParen {
787 788
        set offset = try expectSmallImmValue(a, try parseValue(a));
788 789
    }
789 790
    try expect(a, scanner::TokenKind::LParen, "expected `(`");
792 793
793 794
    return MemOperand { base, offset };
794 795
}
795 796
796 797
/// Parse an immediate value that fits in a signed 12-bit field.
797 -
fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
798 +
unsafe fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
798 799
    return try expectSmallImmValue(a, try parseValue(a));
799 800
}
800 801
801 802
/// Parse and validate a branch immediate.
802 -
fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
803 +
unsafe fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
803 804
    let value = try expectI32Value(a, try parseValue(a), "branch immediate out of range");
804 805
    if not encode::isBranchImm(value) {
805 806
        throw fail(a, "branch immediate out of range");
806 807
    }
807 808
    return value;
808 809
}
809 810
810 811
/// Parse and validate a jump immediate.
811 -
fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
812 +
unsafe fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
812 813
    let value = try expectI32Value(a, try parseValue(a), "jump immediate out of range");
813 814
    if not encode::isJumpImm(value) {
814 815
        throw fail(a, "jump immediate out of range");
815 816
    }
816 817
    return value;
817 818
}
818 819
819 820
/// Parse an integer token as an i64.
820 -
fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) {
821 +
unsafe fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) {
821 822
    let tok = try expectToken(a, scanner::TokenKind::Number, "expected number");
822 823
    let value = parseIntegerText(tok.source) else {
823 824
        throw failOnToken(tok, "invalid integer literal");
824 825
    };
825 826
    return value;
lib/std/arch/rv64/asm/scanner.rad +12 -10
61 61
    cursor: u32,
62 62
    /// Current token observed by the parser.
63 63
    current: Token,
64 64
    /// Previously consumed token observed by the parser.
65 65
    previous: Token,
66 -
    /// Intern pool for identifier-shaped token text.
67 -
    pool: *mut strings::Pool,
66 +
    /// Intern pool for identifier-shaped token text. It must remain valid while scanning.
67 +
    pool: *unsafe mut strings::Pool,
68 68
}
69 69
70 70
/// Individual token with kind, source text, and byte offset.
71 71
export record Token: Copy {
72 72
    /// Token kind.
76 76
    /// Byte offset of `source` in the input buffer.
77 77
    offset: u32,
78 78
}
79 79
80 80
/// Create a new assembler scanner.
81 -
export fn scanner(sourceKind: SourceKind, source: *[u8], pool: *mut strings::Pool) -> Scanner {
81 +
export fn scanner(sourceKind: SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> Scanner {
82 82
    let invalidToken = invalid(0, "");
83 83
    return Scanner {
84 84
        sourceKind,
85 85
        source,
86 86
        token: 0,
149 149
            else => return,
150 150
        }
151 151
    }
152 152
}
153 153
154 -
/// Return the next assembler token.
155 -
export fn next(s: &mut Scanner) -> Token {
154 +
/// Return the next assembler token. The retained pool must be valid and writable.
155 +
export unsafe fn next(s: &mut Scanner) -> Token {
156 156
    skipWhitespace(s);
157 157
    set s.token = s.cursor;
158 158
159 159
    if isEof(s) {
160 160
        return tok(s, TokenKind::Eof);
270 270
    }
271 271
    return invalid(s.token, "unterminated character");
272 272
}
273 273
274 274
/// Scan an identifier-shaped token of the given kind.
275 -
fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token {
275 +
unsafe fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token {
276 276
    scanIdentifierBody(s);
277 +
    let source = &s.source[s.token..s.cursor];
277 278
278 279
    return Token {
279 280
        kind,
280 -
        source: strings::intern(s.pool, &s.source[s.token..s.cursor]),
281 +
        source: strings::intern(s.pool, source),
281 282
        offset: s.token,
282 283
    };
283 284
}
284 285
285 286
/// Scan a sigil-prefixed identifier-shaped token.
286 -
fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token {
287 +
unsafe fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token {
287 288
    let ch = current(s) else {
288 289
        return invalid(s.token, message);
289 290
    };
290 291
    if not char::isAlpha(ch) and ch <> '_' {
291 292
        return invalid(s.token, message);
292 293
    }
293 294
    scanIdentifierBody(s);
295 +
    let source = &s.source[s.token..s.cursor];
294 296
295 297
    return Token {
296 298
        kind,
297 -
        source: strings::intern(s.pool, &s.source[s.token..s.cursor]),
299 +
        source: strings::intern(s.pool, source),
298 300
        offset: s.token,
299 301
    };
300 302
}
301 303
302 304
/// Scan an assembler label token, accepting either `@name` or `@"quoted"` syntax.
303 -
fn scanLabelToken(s: &mut Scanner) -> Token {
305 +
unsafe fn scanLabelToken(s: &mut Scanner) -> Token {
304 306
    let ch = current(s) else {
305 307
        return invalid(s.token, "expected label after `@`");
306 308
    };
307 309
    if ch == '"' {
308 310
        advance(s);
lib/std/arch/rv64/asm/scanner/tests.rad +7 -7
11 11
        return super::scanner(super::SourceKind::String, source, &mut TEST_STRING_POOL);
12 12
    }
13 13
}
14 14
15 15
/// Scanner recognizes assembler-specific sigils and scoped names.
16 -
@test fn testScanRegisterDirectiveAndLabelTokens() throws (testing::TestError) {
16 +
@test unsafe fn testScanRegisterDirectiveAndLabelTokens() throws (testing::TestError) {
17 17
    let mut s = testScanner(
18 18
        ".text %sp @entry name::tail 42"
19 19
    );
20 20
    let directive = super::next(&mut s);
21 21
    try testing::expect(directive.kind == super::TokenKind::Directive);
35 35
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Number);
36 36
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
37 37
}
38 38
39 39
/// Keyword-shaped text remains plain assembler identifiers.
40 -
@test fn testScanKeywordShapedAsmNamesRemainAsmTokens() throws (testing::TestError) {
40 +
@test unsafe fn testScanKeywordShapedAsmNamesRemainAsmTokens() throws (testing::TestError) {
41 41
    let mut s = testScanner(
42 42
        "and or not align addi .text @label"
43 43
    );
44 44
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
45 45
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
50 50
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Label);
51 51
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
52 52
}
53 53
54 54
/// Quoted labels can spell symbol names that are not identifier-shaped.
55 -
@test fn testScanQuotedLabelToken() throws (testing::TestError) {
55 +
@test unsafe fn testScanQuotedLabelToken() throws (testing::TestError) {
56 56
    let mut s = testScanner(
57 57
        "@\"foo.bar.baz\""
58 58
    );
59 59
    let label = super::next(&mut s);
60 60
    try testing::expect(label.kind == super::TokenKind::QuotedLabel);
61 61
    try testing::expect(mem::eq(label.source, "@\"foo.bar.baz\""));
62 62
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
63 63
}
64 64
65 65
/// Sigil-prefixed tokens require the name to start immediately after the sigil.
66 -
@test fn testScanSigilsRequireAdjacency() throws (testing::TestError) {
66 +
@test unsafe fn testScanSigilsRequireAdjacency() throws (testing::TestError) {
67 67
    let mut regScan = testScanner("% a0");
68 68
    try testing::expect(super::next(&mut regScan).kind == super::TokenKind::Invalid);
69 69
70 70
    let mut labelScan = testScanner("@ entry");
71 71
    try testing::expect(super::next(&mut labelScan).kind == super::TokenKind::Invalid);
73 73
    let mut directiveScan = testScanner(". text");
74 74
    try testing::expect(super::next(&mut directiveScan).kind == super::TokenKind::Invalid);
75 75
}
76 76
77 77
/// Scanner reaches EOF after trailing whitespace and comments.
78 -
@test fn testScanProgramEndingWithNewline() throws (testing::TestError) {
78 +
@test unsafe fn testScanProgramEndingWithNewline() throws (testing::TestError) {
79 79
    let mut s = testScanner(
80 80
        ".text;\n@start\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n// comment\nbeq %a0 %zero @done;\n@done\nret;\n"
81 81
    );
82 82
    loop {
83 83
        let tok = super::next(&mut s);
87 87
        }
88 88
    }
89 89
}
90 90
91 91
/// Signed numbers scan only the numeric formats supported by the assembler scanner.
92 -
@test fn testScanSignedHexAndUnsupportedNumericForms() throws (testing::TestError) {
92 +
@test unsafe fn testScanSignedHexAndUnsupportedNumericForms() throws (testing::TestError) {
93 93
    let mut s = testScanner(
94 94
        "+0x2a -0b10 45.5"
95 95
    );
96 96
    let mut tok = super::next(&mut s);
97 97
    try testing::expect(tok.kind == super::TokenKind::Number);
117 117
    try testing::expect(tok.kind == super::TokenKind::Number);
118 118
    try testing::expect(mem::eq(tok.source, "5"));
119 119
}
120 120
121 121
/// Unterminated string and character literals report invalid tokens.
122 -
@test fn testScanUnterminatedDelimitedLiterals() throws (testing::TestError) {
122 +
@test unsafe fn testScanUnterminatedDelimitedLiterals() throws (testing::TestError) {
123 123
    let mut stringScan = testScanner("\"unterminated");
124 124
    let stringTok = super::next(&mut stringScan);
125 125
    try testing::expect(stringTok.kind == super::TokenKind::Invalid);
126 126
    try testing::expect(mem::eq(stringTok.source, "unterminated string"));
127 127
lib/std/arch/rv64/emit.rad +12 -7
95 95
    /// Offset from SP.
96 96
    offset: i32,
97 97
}
98 98
99 99
/// Emission context. Tracks state during code generation.
100 -
export record Emitter: Copy {
100 +
export record Emitter {
101 101
    /// Allocator for growing append-backed emitter lists.
102 102
    allocator: alloc::Allocator,
103 103
    /// Emitted instructions storage.
104 104
    code: *mut [u32],
105 105
    /// Current number of emitted instructions.
196 196
    if debug {
197 197
        set debugEntries = try alloc::allocSlice(
198 198
            arena, @sizeOf(types::DebugEntry), @alignOf(types::DebugEntry), MAX_DEBUG_ENTRIES
199 199
        ) as *mut [types::DebugEntry];
200 200
    }
201 +
    let pendingBranchesBuf = pendingBranches as *mut [PendingBranch];
202 +
    let pendingCallsBuf = pendingCalls as *mut [PendingCall];
203 +
    let pendingJumpsBuf = pendingJumps as *mut [PendingJump];
204 +
    let pendingAddrLoadsBuf = pendingAddrLoads as *mut [PendingAddrLoad];
205 +
    let funcsBuf = funcs as *mut [types::FuncAddr];
201 206
    return Emitter {
202 207
        allocator: alloc::arenaAllocator(arena),
203 208
        code: code as *mut [u32],
204 209
        codeLen: 0,
205 -
        pendingBranches: @sliceOf((pendingBranches as *mut [PendingBranch]).ptr, 0, MAX_PENDING),
206 -
        pendingCalls: @sliceOf((pendingCalls as *mut [PendingCall]).ptr, 0, MAX_PENDING),
207 -
        pendingJumps: @sliceOf((pendingJumps as *mut [PendingJump]).ptr, 0, MAX_PENDING),
208 -
        pendingAddrLoads: @sliceOf((pendingAddrLoads as *mut [PendingAddrLoad]).ptr, 0, MAX_PENDING),
210 +
        pendingBranches: &mut pendingBranchesBuf[..0],
211 +
        pendingCalls: &mut pendingCallsBuf[..0],
212 +
        pendingJumps: &mut pendingJumpsBuf[..0],
213 +
        pendingAddrLoads: &mut pendingAddrLoadsBuf[..0],
209 214
        labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]),
210 -
        funcs: @sliceOf((funcs as *mut [types::FuncAddr]).ptr, 0, MAX_FUNCS),
215 +
        funcs: &mut funcsBuf[..0],
211 216
        debugEntries,
212 217
        debugEntriesLen: 0,
213 218
    };
214 219
}
215 220
375 380
                assert encode::isJumpImm(offset), "patchLocalBranches: jump offset too large";
376 381
                patch(e, p.index, encode::jal(super::ZERO, offset));
377 382
            },
378 383
        }
379 384
    }
380 -
    set e.pendingBranches = @sliceOf(e.pendingBranches.ptr, 0, e.pendingBranches.cap);
385 +
    set e.pendingBranches = &mut e.pendingBranches[..0];
381 386
}
382 387
383 388
/// Encode a conditional branch instruction.
384 389
fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 {
385 390
    match op {
lib/std/arch/rv64/isel.rad +129 -129
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 150
unsafe fn getSrcReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
151 151
    if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
152 -
        emit::emitLd(&mut *s.e, scratch, spillBase(s), spillOffset(s, slot));
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
165 165
        },
166 166
        case il::Val::Imm(imm) => {
167 167
            if imm == 0 {
168 168
                return super::ZERO;
169 169
            }
170 -
            emit::loadImm(&mut *s.e, scratch, imm);
170 +
            emit::loadImm(s.e, scratch, imm);
171 171
            return scratch;
172 172
        },
173 173
        case il::Val::DataSym(name) => {
174 -
            emit::recordDataAddrLoad(&mut *s.e, name, scratch);
174 +
            emit::recordDataAddrLoad(s.e, name, scratch);
175 175
            return scratch;
176 176
        },
177 177
        case il::Val::FnAddr(name) => {
178 -
            emit::recordAddrLoad(&mut *s.e, name, scratch);
178 +
            emit::recordAddrLoad(s.e, name, scratch);
179 179
            return scratch;
180 180
        },
181 181
        case il::Val::Undef => {
182 182
            return scratch;
183 183
        }
193 193
}
194 194
195 195
/// Emit a move instruction if source and destination differ.
196 196
unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) {
197 197
    if *rd <> *rs {
198 -
        emit::emit(&mut *s.e, encode::mv(rd, rs));
198 +
        emit::emit(s.e, encode::mv(rd, rs));
199 199
    }
200 200
}
201 201
202 202
/// Emit zero-extension from a sub-word type to the full register width.
203 203
fn emitZext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
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);
249 249
    if not isExtendedImm(divisor, typ, signed) {
250 -
        emitCmpExt(&mut *s.e, rs2, rs2, typ, signed);
250 +
        emitCmpExt(s.e, rs2, rs2, typ, signed);
251 251
    }
252 252
    let mut knownNonZero = false;
253 253
    if let case il::Val::Imm(imm) = divisor {
254 254
        set knownNonZero = imm <> 0;
255 255
    }
256 256
    if not knownNonZero {
257 -
        emit::emit(&mut *s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2));
258 -
        emit::emit(&mut *s.e, encode::ebreak());
257 +
        emit::emit(s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2));
258 +
        emit::emit(s.e, encode::ebreak());
259 259
    }
260 260
    return rs2;
261 261
}
262 262
263 263
////////////////////////
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 -
fn computeReserveInfo(func: *il::Fn) -> ReserveInfo {
277 +
unsafe fn computeReserveInfo(func: *unsafe 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];
299 299
300 300
/// Select instructions for a function.
301 301
export unsafe fn selectFn(
302 302
    e: &mut emit::Emitter,
303 303
    ralloc: &regalloc::AllocResult,
304 -
    func: *il::Fn
304 +
    func: *unsafe il::Fn
305 305
) {
306 306
    // Reset block offsets for this function.
307 307
    labels::resetBlocks(&mut e.labels);
308 308
    // Pre-scan for constant-sized reserves to promote to fixed frame slots.
309 309
    let reserveInfo = computeReserveInfo(func);
324 324
        reserveOffset: 0, pendingSpill: nil,
325 325
        nextSynthBlock: func.blocks.len + 1,
326 326
        isDynamic: frame.isDynamic,
327 327
    };
328 328
    // Record function name for printing.
329 -
    emit::recordFunc(&mut *s.e, func.name);
329 +
    emit::recordFunc(s.e, func.name);
330 330
    // Record function code offset for call patching.
331 -
    emit::recordFuncOffset(&mut *s.e, func.name);
331 +
    emit::recordFuncOffset(s.e, func.name);
332 332
    // Emit prologue.
333 -
    emit::emitPrologue(&mut *s.e, &frame);
333 +
    emit::emitPrologue(s.e, &frame);
334 334
335 335
    // Move function params from arg registers to assigned registers.
336 336
    // Cross-call params may have been assigned to callee-saved registers
337 337
    // instead of their natural arg registers. Spilled params are stored
338 338
    // directly to their spill slots.
341 341
            let param = funcParam.value;
342 342
            let argReg = super::ARG_REGS[i];
343 343
344 344
            if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
345 345
                // Spilled parameter: store arg register to spill slot.
346 -
                emit::emitSd(&mut *s.e, argReg, spillBase(&s), spillOffset(&s, slot));
346 +
                emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot));
347 347
            } else if let assigned = ralloc.assignments[param.n] {
348 348
                emitMv(&mut s, assigned, argReg);
349 349
            }
350 350
        }
351 351
    }
353 353
    // Emit each block.
354 354
    for i in 0..func.blocks.len {
355 355
        selectBlock(&mut s, i, &func.blocks[i], &frame, func);
356 356
    }
357 357
    // Emit epilogue.
358 -
    emit::emitEpilogue(&mut *s.e, &frame);
358 +
    emit::emitEpilogue(s.e, &frame);
359 359
    // Patch local branches now that all blocks are emitted.
360 -
    emit::patchLocalBranches(&mut *s.e);
360 +
    emit::patchLocalBranches(s.e);
361 361
}
362 362
363 363
/// Select instructions for a block.
364 -
unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *il::Block, frame: &emit::Frame, func: *il::Fn) {
364 +
unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *unsafe il::Block, frame: &emit::Frame, func: *unsafe il::Fn) {
365 365
    // Record block address for branch patching.
366 -
    emit::recordBlock(&mut *s.e, blockIdx);
366 +
    emit::recordBlock(s.e, blockIdx);
367 367
368 368
    // Block parameters are handled at jump sites (in `Jmp`/`Br`).
369 369
    // By the time we enter the block, the arguments have already been
370 370
    // moved to the parameter registers by the predecessor's terminator.
371 371
372 372
    // Process each instruction, auto-committing any pending spill after each.
373 373
    let hasLocs = block.locs.len > 0;
374 374
    for instr, i in block.instrs {
375 375
        // Record debug location before emitting machine instructions.
376 376
        if hasLocs {
377 -
            emit::recordSrcLoc(&mut *s.e, block.locs[i]);
377 +
            emit::recordSrcLoc(s.e, block.locs[i]);
378 378
        }
379 379
        set s.pendingSpill = nil;
380 380
        selectInstr(s, blockIdx, instr, frame, func);
381 381
382 382
        // Flush the pending spill store, if any.
383 383
        if let p = s.pendingSpill {
384 384
            if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) {
385 -
                emit::emitSd(&mut *s.e, p.rd, spillBase(s), spillOffset(s, slot));
385 +
                emit::emitSd(s.e, p.rd, spillBase(s), spillOffset(s, slot));
386 386
            }
387 387
            set s.pendingSpill = nil;
388 388
        }
389 389
    }
390 390
}
391 391
392 392
/// Select instructions for a single IL instruction.
393 -
unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *il::Fn) {
393 +
unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *unsafe il::Fn) {
394 394
    match instr {
395 395
        case il::Instr::BinOp { op, typ, dst, a, b } => {
396 396
            let rd = getDstReg(s, dst, super::SCRATCH1);
397 397
            let rs1 = resolveVal(s, super::SCRATCH1, a);
398 398
            selectAluBinOp(s, op, typ, rd, rs1, b);
403 403
            selectAluUnOp(s, op, typ, rd, rs);
404 404
        },
405 405
        case il::Instr::Load { typ, dst, src, offset } => {
406 406
            let rd = getDstReg(s, dst, super::SCRATCH1);
407 407
            let base = getSrcReg(s, src, super::SCRATCH2);
408 -
            emit::emitLoad(&mut *s.e, rd, base, offset, typ);
408 +
            emit::emitLoad(s.e, rd, base, offset, typ);
409 409
        },
410 410
        case il::Instr::Sload { typ, dst, src, offset } => {
411 411
            let rd = getDstReg(s, dst, super::SCRATCH1);
412 412
            let base = getSrcReg(s, src, super::SCRATCH2);
413 -
            emit::emitSload(&mut *s.e, rd, base, offset, typ);
413 +
            emit::emitSload(s.e, rd, base, offset, typ);
414 414
        },
415 415
        case il::Instr::Store { typ, src, dst, offset } => {
416 416
            let base = getSrcReg(s, dst, super::SCRATCH2);
417 417
            let rs = resolveVal(s, super::SCRATCH1, src);
418 -
            emit::emitStore(&mut *s.e, rs, base, offset, typ);
418 +
            emit::emitStore(s.e, rs, base, offset, typ);
419 419
        },
420 420
        case il::Instr::Copy { dst, val } => {
421 421
            let rd = getDstReg(s, dst, super::SCRATCH1);
422 422
            let rs = resolveVal(s, super::SCRATCH1, val);
423 423
            emitMv(s, rd, rs);
430 430
                    let aligned: i32 = mem::alignUpI32(s.reserveOffset, alignment as i32);
431 431
                    let base = spillBase(s);
432 432
                    let offset = s.ralloc.spill.frameSize + aligned
433 433
                        - (s.frameSize if s.isDynamic else 0);
434 434
435 -
                    emit::emitAddImm(&mut *s.e, rd, base, offset);
435 +
                    emit::emitAddImm(s.e, rd, base, offset);
436 436
                    set s.reserveOffset = aligned + (sz as i32);
437 437
                },
438 438
                case il::Val::Reg(r) => {
439 439
                    // Dynamic-sized reserve: runtime SP adjustment.
440 440
                    let rd = getDstReg(s, dst, super::SCRATCH1);
441 441
                    let rs = getSrcReg(s, r, super::SCRATCH2);
442 442
443 -
                    emit::emit(&mut *s.e, encode::sub(super::SP, super::SP, rs));
443 +
                    emit::emit(s.e, encode::sub(super::SP, super::SP, rs));
444 444
445 445
                    if alignment > 1 {
446 446
                        let mask = 0 - alignment as i32;
447 447
                        assert encode::isSmallImm(mask);
448 448
449 -
                        emit::emit(&mut *s.e, encode::andi(super::SP, super::SP, mask));
449 +
                        emit::emit(s.e, encode::andi(super::SP, super::SP, mask));
450 450
                    }
451 -
                    emit::emit(&mut *s.e, encode::mv(rd, super::SP));
451 +
                    emit::emit(s.e, encode::mv(rd, super::SP));
452 452
                },
453 453
                else =>
454 454
                    panic "selectInstr: invalid reserve operand",
455 455
            }
456 456
        },
476 476
                    panic "selectInstr: blit dst not spilled";
477 477
                };
478 478
                let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else {
479 479
                    panic "selectInstr: blit src not spilled";
480 480
                };
481 -
                emit::emitLd(&mut *s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot));
481 +
                emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot));
482 482
                set srcReload = spillOffset(s, srcSlot);
483 483
            } else {
484 484
                set rdst = getSrcReg(s, dst, super::SCRATCH2);
485 485
                set rsrc = getSrcReg(s, src, super::SCRATCH2);
486 486
            }
493 493
            let canLoop = not bothSpilled
494 494
                and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2
495 495
                and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2;
496 496
497 497
            if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD {
498 -
                emit::emitAddImm(&mut *s.e, super::SCRATCH1, rsrc, dwordBytes);
498 +
                emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes);
499 499
500 500
                let loopStart = s.e.codeLen;
501 501
502 -
                emit::emitLd(&mut *s.e, super::SCRATCH2, rsrc, 0);
503 -
                emit::emitSd(&mut *s.e, super::SCRATCH2, rdst, 0);
504 -
                emit::emit(&mut *s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE));
502 +
                emit::emitLd(s.e, super::SCRATCH2, rsrc, 0);
503 +
                emit::emitSd(s.e, super::SCRATCH2, rdst, 0);
504 +
                emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE));
505 505
506 506
                if *rdst <> *rsrc {
507 -
                    emit::emit(&mut *s.e, encode::addi(rdst, rdst, super::DWORD_SIZE));
507 +
                    emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE));
508 508
                }
509 509
                let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE;
510 510
511 -
                emit::emit(&mut *s.e, encode::bne(rsrc, super::SCRATCH1, brOff));
511 +
                emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff));
512 512
                set remaining -= dwordBytes;
513 513
            }
514 514
515 515
            // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time.
516 516
            // Before each load/store pair, check whether the offset is
517 517
            // about to exceed the 12-bit signed immediate range. When
518 518
            // it does, advance the base registers by the accumulated
519 519
            // offset and reset to zero.
520 520
            while remaining >= super::DWORD_SIZE {
521 521
                if offset > super::MAX_IMM - super::DWORD_SIZE {
522 -
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
522 +
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
523 523
                    if *rdst <> *rsrc {
524 -
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
524 +
                        emit::emitAddImm(s.e, rdst, rdst, offset);
525 525
                    }
526 526
                    set offset = 0;
527 527
                }
528 528
                if let off = srcReload {
529 -
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
530 -
                    emit::emitLd(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
529 +
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
530 +
                    emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset);
531 531
                } else {
532 -
                    emit::emitLd(&mut *s.e, super::SCRATCH1, rsrc, offset);
532 +
                    emit::emitLd(s.e, super::SCRATCH1, rsrc, offset);
533 533
                }
534 -
                emit::emitSd(&mut *s.e, super::SCRATCH1, rdst, offset);
534 +
                emit::emitSd(s.e, super::SCRATCH1, rdst, offset);
535 535
                set offset += super::DWORD_SIZE;
536 536
                set remaining -= super::DWORD_SIZE;
537 537
            }
538 538
            if remaining >= super::WORD_SIZE {
539 539
                if offset > super::MAX_IMM - super::WORD_SIZE {
540 -
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
540 +
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
541 541
                    if *rdst <> *rsrc {
542 -
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
542 +
                        emit::emitAddImm(s.e, rdst, rdst, offset);
543 543
                    }
544 544
                    set offset = 0;
545 545
                }
546 546
                if let off = srcReload {
547 -
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
548 -
                    emit::emitLw(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
547 +
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
548 +
                    emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset);
549 549
                } else {
550 -
                    emit::emitLw(&mut *s.e, super::SCRATCH1, rsrc, offset);
550 +
                    emit::emitLw(s.e, super::SCRATCH1, rsrc, offset);
551 551
                }
552 -
                emit::emitSw(&mut *s.e, super::SCRATCH1, rdst, offset);
552 +
                emit::emitSw(s.e, super::SCRATCH1, rdst, offset);
553 553
                set offset += super::WORD_SIZE;
554 554
                set remaining -= super::WORD_SIZE;
555 555
            }
556 556
            while remaining > 0 {
557 557
                if offset > super::MAX_IMM - 1 {
558 -
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
558 +
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
559 559
                    if *rdst <> *rsrc {
560 -
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
560 +
                        emit::emitAddImm(s.e, rdst, rdst, offset);
561 561
                    }
562 562
                    set offset = 0;
563 563
                }
564 564
                if let off = srcReload {
565 -
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
566 -
                    emit::emitLb(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
565 +
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
566 +
                    emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset);
567 567
                } else {
568 -
                    emit::emitLb(&mut *s.e, super::SCRATCH1, rsrc, offset);
568 +
                    emit::emitLb(s.e, super::SCRATCH1, rsrc, offset);
569 569
                }
570 -
                emit::emitSb(&mut *s.e, super::SCRATCH1, rdst, offset);
570 +
                emit::emitSb(s.e, super::SCRATCH1, rdst, offset);
571 571
                set offset += 1;
572 572
                set remaining -= 1;
573 573
            }
574 574
            // Restore base registers if they were advanced (never happens
575 575
            // in the both-spilled case since size <= MAX_IMM).
576 576
            if not bothSpilled {
577 577
                let advanced = staticSize as i32 - offset;
578 578
                if advanced <> 0 {
579 -
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, 0 - advanced);
579 +
                    emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced);
580 580
                    if *rdst <> *rsrc {
581 -
                        emit::emitAddImm(&mut *s.e, rdst, rdst, 0 - advanced);
581 +
                        emit::emitAddImm(s.e, rdst, rdst, 0 - advanced);
582 582
                    }
583 583
                }
584 584
            }
585 585
        },
586 586
        case il::Instr::Zext { typ, dst, val } => {
587 587
            let rd = getDstReg(s, dst, super::SCRATCH1);
588 588
            let rs = resolveVal(s, super::SCRATCH1, val);
589 -
            emitZext(&mut *s.e, rd, rs, typ);
589 +
            emitZext(s.e, rd, rs, typ);
590 590
        },
591 591
        case il::Instr::Sext { typ, dst, val } => {
592 592
            let rd = getDstReg(s, dst, super::SCRATCH1);
593 593
            let rs = resolveVal(s, super::SCRATCH1, val);
594 -
            emitSext(&mut *s.e, rd, rs, typ);
594 +
            emitSext(s.e, rd, rs, typ);
595 595
        },
596 596
        case il::Instr::Ret { val } => {
597 597
            if let v = val {
598 598
                let rs = resolveVal(s, super::SCRATCH1, v);
599 599
                emitMv(s, super::A0, rs);
601 601
            // Skip the jump to epilogue if this RET is in the last block,
602 602
            // since the epilogue immediately follows.
603 603
            if frame.totalSize <> 0 and blockIdx + 1 == frame.epilogueBlock {
604 604
                // Epilogue is the next block; fallthrough is sufficient.
605 605
            } else {
606 -
                emit::emitReturn(&mut *s.e, frame);
606 +
                emit::emitReturn(s.e, frame);
607 607
            }
608 608
        },
609 609
        case il::Instr::Jmp { target, args } => {
610 610
            // Move arguments to target block's parameter registers.
611 611
            emitBlockArgs(s, func, target, args);
612 612
            // Skip branch if target is the next block (fallthrough).
613 613
            if target <> blockIdx + 1 {
614 -
                emit::recordBranch(&mut *s.e, target, emit::BranchKind::Jump);
614 +
                emit::recordBranch(s.e, target, emit::BranchKind::Jump);
615 615
            }
616 616
        },
617 617
        case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => {
618 618
            // Use zero register directly for immediate `0` operands.
619 619
            let aIsZero = isZeroImm(a);
636 636
            if let case il::CmpOp::Slt = op {
637 637
                set signed = true;
638 638
            }
639 639
            let useSext = cmpUsesSext(typ, signed);
640 640
            if not aIsZero and not isExtendedImm(a, typ, useSext) {
641 -
                emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
641 +
                emitCmpExt(s.e, rs1, rs1, typ, useSext);
642 642
            }
643 643
            if not bIsZero and not isExtendedImm(b, typ, useSext) {
644 -
                emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
644 +
                emitCmpExt(s.e, rs2, rs2, typ, useSext);
645 645
            }
646 646
            // Block-argument moves must only execute on the taken path.
647 647
            // When `thenArgs` is non-empty, invert the branch so that the
648 648
            // then-moves land on the fall-through (taken) side.
649 649
            //
652 652
            // conditional branch to skip to the *other* target and letting
653 653
            // execution fall through.
654 654
            if thenArgs.len > 0 and elseArgs.len > 0 {
655 655
                panic "selectInstr: both `then` and `else` have block arguments";
656 656
            } else if thenArgs.len > 0 {
657 -
                emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
657 +
                emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
658 658
                emitBlockArgs(s, func, thenTarget, thenArgs);
659 659
                // Skip trailing jump if then is the next block (fallthrough).
660 660
                if thenTarget <> blockIdx + 1 {
661 -
                    emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Jump);
661 +
                    emit::recordBranch(s.e, thenTarget, emit::BranchKind::Jump);
662 662
                }
663 663
            } else if thenTarget == blockIdx + 1 and elseArgs.len == 0 {
664 664
                // Then is the next block and no else args: invert the
665 665
                // condition to branch to else and fall through to then.
666 -
                emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
666 +
                emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
667 667
            } else {
668 -
                emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 });
668 +
                emit::recordBranch(s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 });
669 669
                emitBlockArgs(s, func, elseTarget, elseArgs);
670 670
                // Skip trailing jump if else is the next block (fallthrough).
671 671
                if elseTarget <> blockIdx + 1 {
672 -
                    emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::Jump);
672 +
                    emit::recordBranch(s.e, elseTarget, emit::BranchKind::Jump);
673 673
                }
674 674
            }
675 675
        },
676 676
        case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => {
677 677
            let rs1 = resolveVal(s, super::SCRATCH1, val);
678 678
            // When a case has block args, invert the branch to skip past
679 679
            // the arg moves.
680 680
            for c in cases {
681 -
                emit::loadImm(&mut *s.e, super::SCRATCH2, c.value);
681 +
                emit::loadImm(s.e, super::SCRATCH2, c.value);
682 682
683 683
                if c.args.len > 0 {
684 684
                    let skip = s.nextSynthBlock;
685 685
                    set s.nextSynthBlock = skip + 1;
686 686
687 -
                    emit::recordBranch(&mut *s.e, skip, emit::BranchKind::InvertedCond {
687 +
                    emit::recordBranch(s.e, skip, emit::BranchKind::InvertedCond {
688 688
                        op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2,
689 689
                    });
690 690
                    emitBlockArgs(s, func, c.target, c.args);
691 -
                    emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Jump);
692 -
                    emit::recordBlock(&mut *s.e, skip);
691 +
                    emit::recordBranch(s.e, c.target, emit::BranchKind::Jump);
692 +
                    emit::recordBlock(s.e, skip);
693 693
                } else {
694 -
                    emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Cond {
694 +
                    emit::recordBranch(s.e, c.target, emit::BranchKind::Cond {
695 695
                        op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2,
696 696
                    });
697 697
                }
698 698
            }
699 699
            // Fall through to default.
700 700
            emitBlockArgs(s, func, defaultTarget, defaultArgs);
701 -
            emit::recordBranch(&mut *s.e, defaultTarget, emit::BranchKind::Jump);
701 +
            emit::recordBranch(s.e, defaultTarget, emit::BranchKind::Jump);
702 702
        },
703 703
        case il::Instr::Unreachable => {
704 -
            emit::emit(&mut *s.e, encode::ebreak());
704 +
            emit::emit(s.e, encode::ebreak());
705 705
        },
706 706
        case il::Instr::Call { retTy, dst, func, args } => {
707 707
            // For indirect calls, save target to scratch register before arg
708 708
            // setup can clobber it.
709 709
            if let case il::Val::Reg(r) = func {
715 715
            emitParallelMoves(s, &super::ARG_REGS[..], args);
716 716
717 717
            // Emit call.
718 718
            match func {
719 719
                case il::Val::FnAddr(name) => {
720 -
                    emit::recordCall(&mut *s.e, name);
720 +
                    emit::recordCall(s.e, name);
721 721
                },
722 722
                case il::Val::Reg(_) => {
723 -
                    emit::emit(&mut *s.e, encode::jalr(super::RA, super::SCRATCH2, 0));
723 +
                    emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0));
724 724
                },
725 725
                else => {
726 726
                    panic "selectInstr: invalid call target";
727 727
                }
728 728
            }
738 738
            // support constant-evaluating struct/union values in them.
739 739
            let ecallDsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3];
740 740
            let ecallArgs: [il::Val; 5] = [num, a0, a1, a2, a3];
741 741
742 742
            emitParallelMoves(s, &ecallDsts[..], &ecallArgs[..]);
743 -
            emit::emit(&mut *s.e, encode::ecall());
743 +
            emit::emit(s.e, encode::ecall());
744 744
745 745
            // Result in A0.
746 746
            let ecallRd = getDstReg(s, dst, super::SCRATCH1);
747 747
            emitMv(s, ecallRd, super::A0);
748 748
        },
749 749
        case il::Instr::Ebreak => {
750 -
            emit::emit(&mut *s.e, encode::ebreak());
750 +
            emit::emit(s.e, encode::ebreak());
751 751
        },
752 752
        case il::Instr::MemoryFence => {
753 -
            emit::emit(&mut *s.e, encode::fence());
753 +
            emit::emit(s.e, encode::fence());
754 754
        },
755 755
    }
756 756
}
757 757
758 758
/// Choose the cheapest canonical representation that preserves the comparison.
839 839
        case il::BinOp::Add => {
840 840
            if typ == il::Type::W32 {
841 841
                // Inline W32 ADD with immediate optimization.
842 842
                if let case il::Val::Imm(imm) = b {
843 843
                    if encode::isSmallImm64(imm) {
844 -
                        emit::emit(&mut *s.e, encode::addiw(rd, rs1, imm as i32));
844 +
                        emit::emit(s.e, encode::addiw(rd, rs1, imm as i32));
845 845
                        return;
846 846
                    }
847 847
                }
848 848
                let rs2 = resolveVal(s, super::SCRATCH2, b);
849 -
                emit::emit(&mut *s.e, encode::addw(rd, rs1, rs2));
849 +
                emit::emit(s.e, encode::addw(rd, rs1, rs2));
850 850
            } else {
851 851
                selectBinOp(s, rd, rs1, b, BinOp::Add, super::SCRATCH2);
852 852
            }
853 853
        }
854 854
        case il::BinOp::Sub => {
855 855
            // Optimize subtraction by small immediate: use ADDI with negated value.
856 856
            if let case il::Val::Imm(imm) = b {
857 857
                let neg = -imm;
858 858
                if neg >= super::MIN_IMM as i64 and neg <= super::MAX_IMM as i64 {
859 -
                    emit::emit(&mut *s.e,
859 +
                    emit::emit(s.e,
860 860
                        encode::addiw(rd, rs1, neg as i32)
861 861
                            if typ == il::Type::W32 else
862 862
                        encode::addi(rd, rs1, neg as i32));
863 863
                    return;
864 864
                }
865 865
            }
866 866
            let rs2 = resolveVal(s, super::SCRATCH2, b);
867 867
868 -
            emit::emit(&mut *s.e,
868 +
            emit::emit(s.e,
869 869
                encode::subw(rd, rs1, rs2)
870 870
                    if typ == il::Type::W32 else
871 871
                encode::sub(rd, rs1, rs2));
872 872
        }
873 873
        case il::BinOp::Mul => {
874 874
            // Strength-reduce multiplication by known constants.
875 875
            if let case il::Val::Imm(imm) = b {
876 876
                if imm == 0 {
877 -
                    emit::emit(&mut *s.e, encode::mv(rd, super::ZERO));
877 +
                    emit::emit(s.e, encode::mv(rd, super::ZERO));
878 878
                    return;
879 879
                } else if imm == 1 {
880 880
                    emitMv(s, rd, rs1);
881 881
                    return;
882 882
                } else if imm == 2 {
883 -
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 1));
883 +
                    emit::emit(s.e, encode::slli(rd, rs1, 1));
884 884
                    return;
885 885
                } else if imm == 4 {
886 -
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 2));
886 +
                    emit::emit(s.e, encode::slli(rd, rs1, 2));
887 887
                    return;
888 888
                } else if imm == 8 {
889 -
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 3));
889 +
                    emit::emit(s.e, encode::slli(rd, rs1, 3));
890 890
                    return;
891 891
                }
892 892
            }
893 893
            let rs2 = resolveVal(s, super::SCRATCH2, b);
894 -
            emit::emit(&mut *s.e,
894 +
            emit::emit(s.e,
895 895
                encode::mulw(rd, rs1, rs2)
896 896
                    if typ == il::Type::W32 else
897 897
                encode::mul(rd, rs1, rs2));
898 898
        }
899 899
        case il::BinOp::Sdiv => {
900 900
            let rs2 = resolveAndTrapIfZero(s, b, typ, true);
901 -
            emit::emit(&mut *s.e,
901 +
            emit::emit(s.e,
902 902
                encode::divw(rd, rs1, rs2)
903 903
                    if typ == il::Type::W32 else
904 904
                encode::div(rd, rs1, rs2));
905 905
        }
906 906
        case il::BinOp::Udiv => {
907 907
            let rs2 = resolveAndTrapIfZero(s, b, typ, false);
908 -
            emit::emit(&mut *s.e,
908 +
            emit::emit(s.e,
909 909
                encode::divuw(rd, rs1, rs2)
910 910
                    if typ == il::Type::W32 else
911 911
                encode::divu(rd, rs1, rs2));
912 912
        }
913 913
        case il::BinOp::Srem => {
914 914
            let rs2 = resolveAndTrapIfZero(s, b, typ, true);
915 -
            emit::emit(&mut *s.e,
915 +
            emit::emit(s.e,
916 916
                encode::remw(rd, rs1, rs2)
917 917
                    if typ == il::Type::W32 else
918 918
                encode::rem(rd, rs1, rs2));
919 919
        }
920 920
        case il::BinOp::Urem => {
921 921
            let rs2 = resolveAndTrapIfZero(s, b, typ, false);
922 -
            emit::emit(&mut *s.e,
922 +
            emit::emit(s.e,
923 923
                encode::remuw(rd, rs1, rs2)
924 924
                    if typ == il::Type::W32 else
925 925
                encode::remu(rd, rs1, rs2));
926 926
        }
927 927
        case il::BinOp::And =>
937 937
        case il::BinOp::Ushr =>
938 938
            selectShift(s, rd, rs1, b, ShiftOp::Srl, typ, super::SCRATCH2),
939 939
        case il::BinOp::Eq, il::BinOp::Ne => {
940 940
            let rs2 = resolveVal(s, super::SCRATCH2, b);
941 941
            let useSext = cmpUsesSext(typ, false);
942 -
            emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
942 +
            emitCmpExt(s.e, rs1, rs1, typ, useSext);
943 943
            if not isExtendedImm(b, typ, useSext) {
944 -
                emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
944 +
                emitCmpExt(s.e, rs2, rs2, typ, useSext);
945 945
            }
946 -
            emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2));
946 +
            emit::emit(s.e, encode::xor(rd, rs1, rs2));
947 947
            if let case il::BinOp::Eq = op {
948 -
                emit::emit(&mut *s.e, encode::sltiu(rd, rd, 1));
948 +
                emit::emit(s.e, encode::sltiu(rd, rd, 1));
949 949
            } else {
950 -
                emit::emit(&mut *s.e, encode::sltu(rd, super::ZERO, rd));
950 +
                emit::emit(s.e, encode::sltu(rd, super::ZERO, rd));
951 951
            }
952 952
        }
953 953
        case il::BinOp::Slt =>
954 954
            selectCmp(s, typ, rd, rs1, b, CmpOp::Slt, false, super::SCRATCH2),
955 955
        case il::BinOp::Ult =>
964 964
/// Select a unary ALU operation.
965 965
unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) {
966 966
    match op {
967 967
        case il::UnOp::Neg => {
968 968
            if typ == il::Type::W32 {
969 -
                emit::emit(&mut *s.e, encode::subw(rd, super::ZERO, rs));
969 +
                emit::emit(s.e, encode::subw(rd, super::ZERO, rs));
970 970
            } else {
971 -
                emit::emit(&mut *s.e, encode::neg(rd, rs));
971 +
                emit::emit(s.e, encode::neg(rd, rs));
972 972
            }
973 973
        }
974 974
        case il::UnOp::Not =>
975 -
            emit::emit(&mut *s.e, encode::not_(rd, rs)),
975 +
            emit::emit(s.e, encode::not_(rd, rs)),
976 976
    }
977 977
}
978 978
979 979
/// Select binary operation with immediate optimization.
980 980
unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) {
981 981
    // Try immediate optimization first.
982 982
    if let case il::Val::Imm(imm) = b {
983 983
        if encode::isSmallImm64(imm) {
984 984
            let simm = imm as i32;
985 985
            match op {
986 -
                case BinOp::Add => emit::emit(&mut *s.e, encode::addi(rd, rs1, simm)),
987 -
                case BinOp::And => emit::emit(&mut *s.e, encode::andi(rd, rs1, simm)),
988 -
                case BinOp::Or  => emit::emit(&mut *s.e, encode::ori(rd, rs1, simm)),
989 -
                case BinOp::Xor => emit::emit(&mut *s.e, encode::xori(rd, rs1, simm)),
986 +
                case BinOp::Add => emit::emit(s.e, encode::addi(rd, rs1, simm)),
987 +
                case BinOp::And => emit::emit(s.e, encode::andi(rd, rs1, simm)),
988 +
                case BinOp::Or  => emit::emit(s.e, encode::ori(rd, rs1, simm)),
989 +
                case BinOp::Xor => emit::emit(s.e, encode::xori(rd, rs1, simm)),
990 990
            }
991 991
            return;
992 992
        }
993 993
    }
994 994
    // Fallback: load into register.
995 995
    let rs2 = resolveVal(s, scratch, b);
996 996
    match op {
997 -
        case BinOp::Add => emit::emit(&mut *s.e, encode::add(rd, rs1, rs2)),
998 -
        case BinOp::And => emit::emit(&mut *s.e, encode::and_(rd, rs1, rs2)),
999 -
        case BinOp::Or  => emit::emit(&mut *s.e, encode::or_(rd, rs1, rs2)),
1000 -
        case BinOp::Xor => emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2)),
997 +
        case BinOp::Add => emit::emit(s.e, encode::add(rd, rs1, rs2)),
998 +
        case BinOp::And => emit::emit(s.e, encode::and_(rd, rs1, rs2)),
999 +
        case BinOp::Or  => emit::emit(s.e, encode::or_(rd, rs1, rs2)),
1000 +
        case BinOp::Xor => emit::emit(s.e, encode::xor(rd, rs1, rs2)),
1001 1001
    }
1002 1002
}
1003 1003
1004 1004
/// Select shift operation with immediate optimization.
1005 1005
/// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits
1013 1013
        // Otherwise fall back to register shifts, which naturally mask the count.
1014 1014
        if shamt >= 0 and ((isW32 and shamt < 32) or (not isW32 and shamt < 64)) {
1015 1015
            let sa = shamt as i32;
1016 1016
            if isW32 {
1017 1017
                match op {
1018 -
                    case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slliw(rd, rs1, sa)),
1019 -
                    case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srliw(rd, rs1, sa)),
1020 -
                    case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraiw(rd, rs1, sa)),
1018 +
                    case ShiftOp::Sll => emit::emit(s.e, encode::slliw(rd, rs1, sa)),
1019 +
                    case ShiftOp::Srl => emit::emit(s.e, encode::srliw(rd, rs1, sa)),
1020 +
                    case ShiftOp::Sra => emit::emit(s.e, encode::sraiw(rd, rs1, sa)),
1021 1021
                }
1022 1022
            } else {
1023 1023
                match op {
1024 -
                    case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slli(rd, rs1, sa)),
1025 -
                    case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srli(rd, rs1, sa)),
1026 -
                    case ShiftOp::Sra => emit::emit(&mut *s.e, encode::srai(rd, rs1, sa)),
1024 +
                    case ShiftOp::Sll => emit::emit(s.e, encode::slli(rd, rs1, sa)),
1025 +
                    case ShiftOp::Srl => emit::emit(s.e, encode::srli(rd, rs1, sa)),
1026 +
                    case ShiftOp::Sra => emit::emit(s.e, encode::srai(rd, rs1, sa)),
1027 1027
                }
1028 1028
            }
1029 1029
            return;
1030 1030
        }
1031 1031
    }
1032 1032
    // Fallback: load into register.
1033 1033
    let rs2 = resolveVal(s, scratch, b);
1034 1034
    if isW32 {
1035 1035
        match op {
1036 -
            case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sllw(rd, rs1, rs2)),
1037 -
            case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srlw(rd, rs1, rs2)),
1038 -
            case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraw(rd, rs1, rs2)),
1036 +
            case ShiftOp::Sll => emit::emit(s.e, encode::sllw(rd, rs1, rs2)),
1037 +
            case ShiftOp::Srl => emit::emit(s.e, encode::srlw(rd, rs1, rs2)),
1038 +
            case ShiftOp::Sra => emit::emit(s.e, encode::sraw(rd, rs1, rs2)),
1039 1039
        }
1040 1040
    } else {
1041 1041
        match op {
1042 -
            case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sll(rd, rs1, rs2)),
1043 -
            case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srl(rd, rs1, rs2)),
1044 -
            case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sra(rd, rs1, rs2)),
1042 +
            case ShiftOp::Sll => emit::emit(s.e, encode::sll(rd, rs1, rs2)),
1043 +
            case ShiftOp::Srl => emit::emit(s.e, encode::srl(rd, rs1, rs2)),
1044 +
            case ShiftOp::Sra => emit::emit(s.e, encode::sra(rd, rs1, rs2)),
1045 1045
        }
1046 1046
    }
1047 1047
}
1048 1048
1049 1049
/// Resolve parallel moves from IL values to physical destination registers.
1166 1166
/// Emit moves from block arguments to target block's parameter registers.
1167 1167
///
1168 1168
/// Handles spilled destinations directly, then delegates to [`emitParallelMoves`]
1169 1169
/// for the remaining register-to-register parallel move resolution. Edges that
1170 1170
/// would overwrite an unconsumed spill source are unsupported.
1171 -
unsafe fn emitBlockArgs(s: &mut Selector, func: *il::Fn, target: u32, args: &[il::Val]) {
1171 +
unsafe fn emitBlockArgs(s: &mut Selector, func: *unsafe il::Fn, target: u32, args: &[il::Val]) {
1172 1172
    if args.len == 0 {
1173 1173
        return;
1174 1174
    }
1175 1175
    let block = &func.blocks[target];
1176 1176
    assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch";
1222 1222
        if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, param) {
1223 1223
            if let case il::Val::Undef = arg {
1224 1224
                // Undefined values don't need any move.
1225 1225
            } else {
1226 1226
                let rs = resolveVal(s, super::SCRATCH1, arg);
1227 -
                emit::emitSd(&mut *s.e, rs, spillBase(s), spillOffset(s, slot));
1227 +
                emit::emitSd(s.e, rs, spillBase(s), spillOffset(s, slot));
1228 1228
            }
1229 1229
        } else {
1230 1230
            set dsts[i] = getReg(s, param);
1231 1231
        }
1232 1232
    }
1247 1247
    let mut signed = false;
1248 1248
    if let case CmpOp::Slt = op {
1249 1249
        set signed = true;
1250 1250
    }
1251 1251
    let useSext = cmpUsesSext(typ, signed);
1252 -
    emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
1252 +
    emitCmpExt(s.e, rs1, rs1, typ, useSext);
1253 1253
1254 1254
    // Canonicalizing the immediate can expose an immediate instruction even
1255 1255
    // when the IL value used a different representation for the same width.
1256 1256
    let mut rhs = b;
1257 1257
    if let case il::Val::Imm(imm) = b {
1258 1258
        let canonical = canonicalCmpImm(imm, typ, useSext);
1259 1259
        set rhs = il::Val::Imm(canonical);
1260 1260
        if encode::isSmallImm64(canonical) {
1261 1261
            let simm = canonical as i32;
1262 1262
            match op {
1263 -
                case CmpOp::Slt => emit::emit(&mut *s.e, encode::slti(rd, rs1, simm)),
1264 -
                case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltiu(rd, rs1, simm)),
1263 +
                case CmpOp::Slt => emit::emit(s.e, encode::slti(rd, rs1, simm)),
1264 +
                case CmpOp::Ult => emit::emit(s.e, encode::sltiu(rd, rs1, simm)),
1265 1265
            }
1266 1266
            if invert {
1267 -
                emit::emit(&mut *s.e, encode::xori(rd, rd, 1));
1267 +
                emit::emit(s.e, encode::xori(rd, rd, 1));
1268 1268
            }
1269 1269
            return;
1270 1270
        }
1271 1271
    }
1272 1272
1273 1273
    let rs2 = resolveVal(s, scratch, rhs);
1274 1274
    if not isExtendedImm(rhs, typ, useSext) {
1275 -
        emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
1275 +
        emitCmpExt(s.e, rs2, rs2, typ, useSext);
1276 1276
    }
1277 1277
    match op {
1278 -
        case CmpOp::Slt => emit::emit(&mut *s.e, encode::slt(rd, rs1, rs2)),
1279 -
        case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltu(rd, rs1, rs2)),
1278 +
        case CmpOp::Slt => emit::emit(s.e, encode::slt(rd, rs1, rs2)),
1279 +
        case CmpOp::Ult => emit::emit(s.e, encode::sltu(rd, rs1, rs2)),
1280 1280
    }
1281 1281
    if invert {
1282 -
        emit::emit(&mut *s.e, encode::xori(rd, rd, 1));
1282 +
        emit::emit(s.e, encode::xori(rd, rd, 1));
1283 1283
    }
1284 1284
}
lib/std/arch/rv64/tests.rad +1 -1
21 21
@test unsafe fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) {
22 22
    let mut arena = alloc::new(&mut ASSEMBLY_ARENA_STORAGE[..]);
23 23
    let symbols = try alloc::allocSlice(&mut arena, @sizeOf(asm::Symbol), @alignOf(asm::Symbol), 2) catch {
24 24
        throw testing::TestError::Failed;
25 25
    };
26 -
    let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2, 2);
26 +
    let symbolSlice = symbols as *mut [asm::Symbol];
27 27
    set symbolSlice[0] = asm::Symbol {
28 28
        name: "local",
29 29
        section: asm::Section::Text,
30 30
        offset: 0,
31 31
        isExported: false,
lib/std/collections/dict.rad +1 -1
12 12
    /// Associated value.
13 13
    value: i32,
14 14
}
15 15
16 16
/// Open-addressed hash map with caller-provided storage.
17 -
export record Dict: Copy {
17 +
export record Dict {
18 18
    /// Hash table entries.
19 19
    entries: *mut [Entry],
20 20
    /// Number of occupied entries.
21 21
    count: u32,
22 22
}
lib/std/lang/alloc.rad +19 -2
16 16
17 17
/// Bump allocator backed by a byte slice.
18 18
///
19 19
/// Allocations are made by advancing an offset pointer. Individual allocations
20 20
/// cannot be freed; instead, the entire arena is reset at once via [`reset`].
21 -
export record Arena: Copy {
21 +
export record Arena {
22 22
    /// Backing storage.
23 23
    data: *mut [u8],
24 24
    /// Current allocation offset in bytes.
25 25
    offset: u32,
26 26
}
128 128
}
129 129
130 130
/// Arena allocation function conforming to the `Allocator` interface.
131 131
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
132 132
    let arena = ctx as *unsafe mut Arena;
133 -
    return try! alloc(&mut *arena, size, alignment);
133 +
    return try! alloc(arena, size, alignment);
134 +
}
135 +
136 +
/// Allocate raw storage that remains valid until the arena is reset.
137 +
export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) {
138 +
    let owner = try alloc(arena, size, alignment);
139 +
    let byte = owner as *mut u8;
140 +
    let raw: *unsafe mut u8 = &mut *byte;
141 +
    return raw as *unsafe mut opaque;
142 +
}
143 +
144 +
/// Allocate a raw slice that remains valid until the arena is reset.
145 +
export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) {
146 +
    if count == 0 {
147 +
        return &mut [];
148 +
    }
149 +
    let raw = try allocRaw(arena, size * count, alignment);
150 +
    return @sliceOf(raw, count);
134 151
}
lib/std/lang/ast.rad +27 -25
10 10
export constant MAX_TRAIT_METHODS: u32 = 8;
11 11
12 12
/// Arena for all parser allocations.
13 13
///
14 14
/// Uses a bump allocator for both AST nodes and node pointer arrays.
15 -
export record NodeArena: Copy {
15 +
export record NodeArena {
16 16
    /// Bump allocator for all allocations.
17 17
    arena: alloc::Arena,
18 18
    /// Next node ID to assign. Incremented on each node allocation.
19 19
    nextId: u32,
20 20
}
32 32
    if capacity == 0 {
33 33
        return &mut [];
34 34
    }
35 35
    let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity);
36 36
37 -
    return @sliceOf(ptr.ptr as *mut *Node, 0, capacity);
37 +
    let nodes = ptr as *mut [*Node];
38 +
    return &mut nodes[..0];
38 39
}
39 40
40 41
/// Attribute bit set applied to declarations or fields.
41 42
export union Attribute: Copy {
42 43
    /// Public visibility attribute.
53 54
    Unsafe = 0b100000,
54 55
}
55 56
56 57
/// Ordered collection of attribute nodes applied to a declaration.
57 58
export record Attributes: Copy {
58 -
    list: *mut [*Node],
59 +
    /// Attribute nodes in declaration order.
60 +
    list: *[*Node],
59 61
}
60 62
61 63
/// Check if an attributes list contains an attribute.
62 64
export fn attributesContains(self: &Attributes, attr: Attribute) -> bool {
63 65
    for node in self.list {
201 203
    /// Nominal type, points to identifier node.
202 204
    Nominal(*Node),
203 205
    /// Inline record type for union variant payloads.
204 206
    Record {
205 207
        /// Field declaration nodes.
206 -
        fields: *mut [*Node],
208 +
        fields: *[*Node],
207 209
        /// Whether this record has labeled fields.
208 210
        labeled: bool,
209 211
    },
210 212
    /// Anonymous function type.
211 213
    Fn {
227 229
}
228 230
229 231
/// Function signature.
230 232
export record FnSig: Copy {
231 233
    /// Parameter type nodes in declaration order.
232 -
    params: *mut [*Node],
234 +
    params: *[*Node],
233 235
    /// Optional return type node.
234 236
    returnType: ?*Node,
235 237
    /// Throwable type nodes declared in the signature.
236 -
    throwList: *mut [*Node],
238 +
    throwList: *[*Node],
237 239
}
238 240
239 241
/// Address-of expression metadata.
240 242
export record AddressOf: Copy {
241 243
    /// Target expression being referenced.
245 247
}
246 248
247 249
/// Compound statement block with optional dedicated scope.
248 250
export record Block: Copy {
249 251
    /// Statements that belong to this block.
250 -
    statements: *mut [*Node],
252 +
    statements: *[*Node],
251 253
    /// Whether this block permits unsafe operations.
252 254
    isUnsafe: bool,
253 255
}
254 256
255 257
/// Function call expression.
256 258
export record Call: Copy {
257 259
    /// Callee expression.
258 260
    callee: *Node,
259 261
    /// Argument expressions in source order.
260 -
    args: *mut [*Node],
262 +
    args: *[*Node],
261 263
}
262 264
263 265
/// Single argument to a function or record literal, optionally labeled.
264 266
export record Arg: Copy {
265 267
    /// Optional label applied to the argument.
299 301
/// Try expression metadata.
300 302
export record Try: Copy {
301 303
    /// Expression evaluated with implicit error propagation.
302 304
    expr: *Node,
303 305
    /// Catch clauses. Empty for propagation (`try`), `try!`, or `try?`.
304 -
    catches: *mut [*Node],
306 +
    catches: *[*Node],
305 307
    /// Whether the try should panic instead of returning an error.
306 308
    shouldPanic: bool,
307 309
    /// Whether the try should return an optional instead of propagating error.
308 310
    returnsOptional: bool,
309 311
}
361 363
}
362 364
363 365
/// Prong arm.
364 366
export union ProngArm: Copy {
365 367
    /// Case arm with pattern list.
366 -
    Case(*mut [*Node]),
368 +
    Case(*[*Node]),
367 369
    /// Binding arm with single identifier or placeholder.
368 370
    Binding(*Node),
369 371
    /// Else arm.
370 372
    Else,
371 373
}
405 407
/// `match` statement metadata.
406 408
export record Match: Copy {
407 409
    /// Expression whose value controls the match.
408 410
    subject: *Node,
409 411
    /// Prong nodes evaluated in order.
410 -
    prongs: *mut [*Node],
412 +
    prongs: *[*Node],
411 413
}
412 414
413 415
/// `match` prong metadata.
414 416
export record MatchProng: Copy {
415 417
    /// Prong arm.
470 472
export record RecordLit: Copy {
471 473
    /// Type name associated with the literal.
472 474
    /// If `nil`, it's an anonymous record literal.
473 475
    typeName: ?*Node,
474 476
    /// Field initializer nodes.
475 -
    fields: *mut [*Node],
477 +
    fields: *[*Node],
476 478
    /// When true, remaining fields are discarded (`{ x, .. }`).
477 479
    ignoreRest: bool,
478 480
}
479 481
480 482
/// Record declaration.
481 483
export record RecordDecl: Copy {
482 484
    /// Identifier naming the record.
483 485
    name: *Node,
484 486
    /// Field declaration nodes.
485 -
    fields: *mut [*Node],
487 +
    fields: *[*Node],
486 488
    /// Optional attribute list applied to the record.
487 489
    attrs: ?Attributes,
488 490
    /// Trait derivations attached to the record.
489 -
    derives: *mut [*Node],
491 +
    derives: *[*Node],
490 492
    /// Whether this record has labeled fields.
491 493
    labeled: bool,
492 494
}
493 495
494 496
/// Union declarations.
495 497
export record UnionDecl: Copy {
496 498
    /// Identifier naming the union.
497 499
    name: *Node,
498 500
    /// Variant nodes making up the union.
499 -
    variants: *mut [*Node],
501 +
    variants: *[*Node],
500 502
    /// Optional attribute list applied to the union.
501 503
    attrs: ?Attributes,
502 504
    /// Trait derivations attached to the union.
503 -
    derives: *mut [*Node],
505 +
    derives: *[*Node],
504 506
}
505 507
506 508
/// Union variant declaration.
507 509
export record UnionDeclVariant: Copy {
508 510
    /// Identifier naming the variant.
614 616
    /// Numeric literal such as `42` or `0xFF`.
615 617
    Number(fmt::IntLiteral),
616 618
    /// Range expression such as `0..10` or `..`.
617 619
    Range(Range),
618 620
    /// Array literal expression.
619 -
    ArrayLit(*mut [*Node]),
621 +
    ArrayLit(*[*Node]),
620 622
    /// Array repeat literal expression.
621 623
    ArrayRepeatLit(ArrayRepeatLit),
622 624
    /// Array subscript expression.
623 625
    Subscript {
624 626
        /// Array or slice.
633 635
    /// Builtin function call (e.g. `@sizeOf(T)`).
634 636
    BuiltinCall {
635 637
        /// Builtin function kind.
636 638
        kind: Builtin,
637 639
        /// Argument list.
638 -
        args: *mut [*Node],
640 +
        args: *[*Node],
639 641
    },
640 642
    /// Block expression or statement body.
641 643
    Block(Block),
642 644
    /// Call expression, eg. `f(x)`.
643 645
    Call(Call),
755 757
    /// Trait declaration.
756 758
    TraitDecl {
757 759
        /// Trait name identifier.
758 760
        name: *Node,
759 761
        /// Supertrait name nodes.
760 -
        supertraits: *mut [*Node],
762 +
        supertraits: *[*Node],
761 763
        /// Method signature nodes ([`TraitMethodSig`]).
762 -
        methods: *mut [*Node],
764 +
        methods: *[*Node],
763 765
        /// Optional attributes.
764 766
        attrs: ?Attributes,
765 767
    },
766 768
    /// Method signature inside a trait declaration.
767 769
    TraitMethodSig {
779 781
        /// Trait name identifier.
780 782
        traitName: *Node,
781 783
        /// Target type identifier.
782 784
        targetType: *Node,
783 785
        /// Method definition nodes ([`MethodDecl`]).
784 -
        methods: *mut [*Node],
786 +
        methods: *[*Node],
785 787
    },
786 788
    /// Method definition with a receiver.
787 789
    /// Used both inside `instance` blocks and as standalone methods.
788 790
    MethodDecl {
789 791
        /// Method name identifier.
851 853
    fnBody: *Node
852 854
}
853 855
854 856
/// Synthesize a module with a function in it with the given name and statements.
855 857
export unsafe fn synthFnModule(
856 -
    arena: &mut NodeArena, name: *[u8], bodyStmts: *mut [*Node]
858 +
    arena: &mut NodeArena, name: *[u8], bodyStmts: *[*Node]
857 859
) -> SynthFnMod {
858 860
    let a = alloc::arenaAllocator(&mut arena.arena);
859 861
    let fnName = synthNode(arena, NodeValue::Ident(name));
860 -
    let params: *mut [*Node] = &mut [];
861 -
    let throwList: *mut [*Node] = &mut [];
862 +
    let params: *[*Node] = &[];
863 +
    let throwList: *[*Node] = &[];
862 864
    let fnSig = FnSig { params, returnType: nil, throwList };
863 -
    let fnBody = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false }));
865 +
    let fnBody: *Node = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false }));
864 866
    let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl {
865 867
        name: fnName, sig: fnSig, body: fnBody, attrs: nil,
866 868
    }));
867 869
    let mut rootStmts: *mut [*Node] = &mut [];
868 870
    rootStmts.append(fnDecl, a);
lib/std/lang/ast/printer.rad +21 -21
104 104
        }
105 105
        case super::TypeSig::Optional { valueType } =>
106 106
            return sexpr::list(a, "?", &[toExpr(a, valueType)]),
107 107
        case super::TypeSig::Nominal(name) => return toExpr(a, name),
108 108
        case super::TypeSig::Record { fields, .. } =>
109 -
            return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])),
109 +
            return sexpr::list(a, "record", nodeListToExprs(a, fields)),
110 110
        case super::TypeSig::Fn { sig, isUnsafe } => {
111 111
            let mut ret = sexpr::sym("void");
112 112
            if let rt = sig.returnType {
113 113
                set ret = toExpr(a, rt);
114 114
            }
115 115
            let head = "unsafe-fn" if isUnsafe else "fn";
116 -
            return sexpr::list(a, head, &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]);
116 +
            return sexpr::list(a, head, &[sexpr::list(a, "params", nodeListToExprs(a, sig.params)), ret]);
117 117
        }
118 118
        case super::TypeSig::TraitObject { class, traitName, mutable } => {
119 119
            let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj");
120 120
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable
121 121
                else sexpr::list(a, head, &[toExpr(a, traitName)]);
122 122
        }
123 123
    }
124 124
}
125 125
126 126
/// Convert a node slice to a slice of expressions.
127 -
unsafe fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
127 +
unsafe fn nodeListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] {
128 128
    if nodes.len == 0 {
129 129
        return &[];
130 130
    }
131 131
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
132 132
    for node, i in nodes {
137 137
138 138
/// Convert optional attributes to an attribute list expression.
139 139
unsafe fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
140 140
    let mut exprs: *[sexpr::Expr] = &[];
141 141
    if let list = attrs {
142 -
        set exprs = nodeListToExprs(a, &list.list[..]);
142 +
        set exprs = nodeListToExprs(a, list.list);
143 143
    }
144 144
    return sexpr::list(a, "attrs", exprs);
145 145
}
146 146
147 147
/// Convert an optional node to an expression, or return placeholder.
167 167
    }
168 168
    return sexpr::Expr::Null;
169 169
}
170 170
171 171
/// Convert a list of match prongs to expressions.
172 -
unsafe fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
172 +
unsafe fn prongListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] {
173 173
    if nodes.len == 0 {
174 174
        return &[];
175 175
    }
176 176
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
177 177
    for prong, i in nodes {
190 190
/// Convert a match prong to an S-expression.
191 191
unsafe fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
192 192
    match p.arm {
193 193
        case super::ProngArm::Case(patterns) => {
194 194
            return sexpr::block(a, "case", &[
195 -
                sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])),
195 +
                sexpr::list(a, "patterns", nodeListToExprs(a, patterns)),
196 196
                guardExpr(a, p.guard)
197 197
            ], &[toExpr(a, p.body)]);
198 198
        }
199 199
        case super::ProngArm::Else => {
200 200
            return sexpr::block(a, "else", &[guardExpr(a, p.guard)], &[toExpr(a, p.body)]);
217 217
) -> sexpr::Expr {
218 218
    return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]);
219 219
}
220 220
221 221
/// Convert a list of record fields to expressions.
222 -
unsafe fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
222 +
unsafe fn fieldListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] {
223 223
    if nodes.len == 0 {
224 224
        return &[];
225 225
    }
226 226
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
227 227
    for node, i in nodes {
241 241
unsafe fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
242 242
    return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]);
243 243
}
244 244
245 245
/// Convert a list of union variants to expressions.
246 -
unsafe fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
246 +
unsafe fn variantListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] {
247 247
    if nodes.len == 0 {
248 248
        return &[];
249 249
    }
250 250
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
251 251
    for node, i in nodes {
291 291
            set buf[0] = toExpr(a, c.callee);
292 292
            for arg, i in c.args { set buf[i + 1] = toExpr(a, arg); }
293 293
            return sexpr::Expr::List { head: "call", tail: buf, multiline: false };
294 294
        }
295 295
        case super::NodeValue::BuiltinCall { kind, args } =>
296 -
            return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args[..])),
296 +
            return sexpr::list(a, builtinName(kind), nodeListToExprs(a, args)),
297 297
        case super::NodeValue::Subscript { container, index } =>
298 298
            return sexpr::list(a, "[]", &[toExpr(a, container), toExpr(a, index)]),
299 299
        case super::NodeValue::FieldAccess(acc) =>
300 300
            return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
301 301
        case super::NodeValue::ScopeAccess(acc) =>
306 306
        case super::NodeValue::Deref(target) =>
307 307
            return sexpr::list(a, "deref", &[toExpr(a, target)]),
308 308
        case super::NodeValue::As(cast) =>
309 309
            return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]),
310 310
        case super::NodeValue::ArrayLit(elems) =>
311 -
            return sexpr::list(a, "array", nodeListToExprs(a, &elems[..])),
311 +
            return sexpr::list(a, "array", nodeListToExprs(a, elems)),
312 312
        case super::NodeValue::ArrayRepeatLit(rep) =>
313 313
            return sexpr::list(a, "array-repeat", &[toExpr(a, rep.item), toExpr(a, rep.count)]),
314 314
        case super::NodeValue::RecordLit(lit) => {
315 315
            let mut total: u32 = lit.fields.len as u32;
316 316
            if let _ = lit.typeName {
343 343
        }
344 344
        case super::NodeValue::Try(t) => {
345 345
            let mut head = "try";
346 346
            if t.shouldPanic { set head = "try!"; }
347 347
            if t.catches.len > 0 {
348 -
                let catches = nodeListToExprs(a, &t.catches[..]);
348 +
                let catches = nodeListToExprs(a, t.catches);
349 349
                return sexpr::list(a, head, &[toExpr(a, t.expr), sexpr::block(a, "catches", &[], catches)]);
350 350
            }
351 351
            return sexpr::list(a, head, &[toExpr(a, t.expr)]);
352 352
        }
353 353
        case super::NodeValue::CatchClause(clause) => {
365 365
            set children[len] = toExpr(a, clause.body);
366 366
            set len += 1;
367 367
            return sexpr::list(a, head, &children[..len]);
368 368
        }
369 369
        case super::NodeValue::Block(blk) => {
370 -
            let children = nodeListToExprs(a, &blk.statements[..]);
370 +
            let children = nodeListToExprs(a, blk.statements);
371 371
            let name = "unsafe" if blk.isUnsafe else "block";
372 372
            return sexpr::block(a, name, &[], children);
373 373
        }
374 374
        case super::NodeValue::Let(decl) => {
375 375
            let mut head = "let";
441 441
                toExpr(a, f.iterable)
442 442
            ], &[toExpr(a, f.body), toExprOrNull(a, f.elseBranch)]),
443 443
        case super::NodeValue::Loop { body } =>
444 444
            return sexpr::block(a, "loop", &[], &[toExpr(a, body)]),
445 445
        case super::NodeValue::Match(m) => {
446 -
            let children = prongListToExprs(a, &m.prongs[..]);
446 +
            let children = prongListToExprs(a, m.prongs);
447 447
            return sexpr::block(a, "match", &[toExpr(a, m.subject)], children);
448 448
        }
449 449
        case super::NodeValue::MatchProng(p) => {
450 450
            return prongToExpr(a, p);
451 451
        }
452 452
        case super::NodeValue::FnDecl(f) => {
453 -
            let params = sexpr::list(a, "params", nodeListToExprs(a, &f.sig.params[..]));
453 +
            let params = sexpr::list(a, "params", nodeListToExprs(a, f.sig.params));
454 454
            let ret = toExprOrNull(a, f.sig.returnType);
455 455
            if let body = f.body {
456 456
                return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]);
457 457
            }
458 458
            return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]);
459 459
        }
460 460
        case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]),
461 461
        case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]),
462 462
        case super::NodeValue::RecordDecl(r) => {
463 -
            let children = fieldListToExprs(a, &r.fields[..]);
463 +
            let children = fieldListToExprs(a, r.fields);
464 464
            return sexpr::block(a, "record", &[toExpr(a, r.name)], children);
465 465
        }
466 466
        case super::NodeValue::RecordField { field, type, value } => {
467 467
            return fieldToExpr(a, field, type, value);
468 468
        }
469 469
        case super::NodeValue::UnionDecl(u_) => {
470 -
            let children = variantListToExprs(a, &u_.variants[..]);
470 +
            let children = variantListToExprs(a, u_.variants);
471 471
            return sexpr::block(a, "union", &[toExpr(a, u_.name)], children);
472 472
        }
473 473
        case super::NodeValue::UnionDeclVariant(v) => {
474 474
            return variantToExpr(a, v.name, v.type);
475 475
        }
476 476
        case super::NodeValue::ExprStmt(e) => return toExpr(a, e),
477 477
        case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => {
478 -
            let children = nodeListToExprs(a, &methods[..]);
479 -
            let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits[..]));
478 +
            let children = nodeListToExprs(a, methods);
479 +
            let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, supertraits));
480 480
            return sexpr::block(a, "trait", &[toExpr(a, name), supers], children);
481 481
        }
482 482
        case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => {
483 -
            let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..]));
483 +
            let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params));
484 484
            let ret = toExprOrNull(a, sig.returnType);
485 485
            let attributes = attributesToExpr(a, attrs);
486 486
            return sexpr::list(
487 487
                a,
488 488
                "methodSig",
489 489
                &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret],
490 490
            );
491 491
        }
492 492
        case super::NodeValue::InstanceDecl { traitName, targetType, methods } => {
493 -
            let children = nodeListToExprs(a, &methods[..]);
493 +
            let children = nodeListToExprs(a, methods);
494 494
            return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children);
495 495
        }
496 496
        case super::NodeValue::MethodDecl {
497 497
            name, receiverName, receiverType, sig, body, attrs,
498 498
        } => {
499 -
            let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..]));
499 +
            let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params));
500 500
            let ret = toExprOrNull(a, sig.returnType);
501 501
            let attributes = attributesToExpr(a, attrs);
502 502
            return sexpr::block(
503 503
                a,
504 504
                "method",
lib/std/lang/gen/bitset.rad +3 -2
28 28
}
29 29
30 30
/// Create a new bitset backed by the given zero-initialized storage.
31 31
/// The storage must outlive the bitset and its iterators.
32 32
export unsafe fn new(bits: &mut [u32]) -> Bitset {
33 -
    return Bitset { bits: bits as *unsafe mut [u32], len: bits.len * 32 };
33 +
    let len = bits.len * 32;
34 +
    return Bitset { bits: bits as *unsafe mut [u32], len };
34 35
}
35 36
36 37
/// Create a new bitset backed by the given storage, zeroing it first.
37 38
/// The storage must outlive the bitset and its iterators.
38 39
export unsafe fn init(bits: &mut [u32]) -> Bitset {
43 44
}
44 45
45 46
/// Create a bitset from arena allocation.
46 47
export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) {
47 48
    let numWords = wordsFor(len);
48 -
    let bits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *mut [u32];
49 +
    let bits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *unsafe mut [u32];
49 50
50 51
    return init(bits);
51 52
}
52 53
53 54
/// Set bit `n` in the bitset.
lib/std/lang/gen/data.rad +12 -12
22 22
    /// Absolute address, including data base address.
23 23
    addr: u32,
24 24
}
25 25
26 26
/// Hash-indexed data symbol map.
27 -
export record DataSymMap: Copy {
27 +
export record DataSymMap {
28 28
    /// Underlying hash table.
29 29
    dict: dict::Dict,
30 30
    /// Fallback linear array for edge cases.
31 31
    syms: *[DataSym],
32 32
}
34 34
/// Lay out data symbols for a single section.
35 35
/// Data with sidecar image bytes is placed first, then zero-initialized data,
36 36
/// so that only meaningful bytes need to be written to the output file.
37 37
/// Returns the updated offset past all placed symbols.
38 38
export fn layoutSection(
39 -
    items: *[il::Data],
40 -
    syms: *mut [DataSym],
39 +
    items: &[il::Data],
40 +
    syms: &mut [DataSym],
41 41
    count: &mut u32,
42 42
    base: u32,
43 43
    readOnly: bool
44 44
) -> u32 {
45 45
    return layoutSectionAtOffset(items, syms, count, base, 0, readOnly);
46 46
}
47 47
48 48
/// Lay out data symbols for a single section starting at [`startOffset`].
49 49
export fn layoutSectionAtOffset(
50 -
    items: *[il::Data],
51 -
    syms: *mut [DataSym],
50 +
    items: &[il::Data],
51 +
    syms: &mut [DataSym],
52 52
    count: &mut u32,
53 53
    base: u32,
54 54
    startOffset: u32,
55 55
    readOnly: bool
56 56
) -> u32 {
57 57
    let mut offset: u32 = startOffset;
58 58
59 59
    // Data requiring sidecar image bytes first.
60 60
    for i in 0..items.len {
61 -
        let data = &items[i];
61 +
        let data = items[i];
62 62
        if data.readOnly == readOnly and not data.isZeroInit {
63 63
            set offset = mem::alignUp(offset, data.alignment);
64 64
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
65 65
            set *count += 1;
66 66
            set offset += data.size;
67 67
        }
68 68
    }
69 69
    // Zero-initialized data after.
70 70
    for i in 0..items.len {
71 -
        let data = &items[i];
71 +
        let data = items[i];
72 72
        if data.readOnly == readOnly and data.isZeroInit {
73 73
            set offset = mem::alignUp(offset, data.alignment);
74 74
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
75 75
            set *count += 1;
76 76
            set offset += data.size;
81 81
82 82
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
83 83
/// Iterates data requiring sidecar image bytes, serializing each data item.
84 84
/// Returns the total number of bytes written.
85 85
export unsafe fn emitSection(
86 -
    items: *[il::Data],
86 +
    items: &[il::Data],
87 87
    dataSymMap: &DataSymMap,
88 88
    fnLabels: &labels::Labels,
89 89
    codeBase: u32,
90 -
    buf: *mut [u8],
90 +
    buf: &mut [u8],
91 91
    readOnly: bool
92 92
) -> u32 {
93 93
    return emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
94 94
}
95 95
96 96
/// Emit data bytes for a single section starting at `startOffset`.
97 97
export unsafe fn emitSectionAtOffset(
98 -
    items: *[il::Data],
98 +
    items: &[il::Data],
99 99
    dataSymMap: &DataSymMap,
100 100
    fnLabels: &labels::Labels,
101 101
    codeBase: u32,
102 -
    buf: *mut [u8],
102 +
    buf: &mut [u8],
103 103
    readOnly: bool,
104 104
    startOffset: u32
105 105
) -> u32 {
106 106
    let mut offset: u32 = startOffset;
107 107
108 108
    for i in 0..items.len {
109 -
        let data = &items[i];
109 +
        let data = items[i];
110 110
        if data.readOnly == readOnly and not data.isZeroInit {
111 111
            set offset = mem::alignUp(offset, data.alignment);
112 112
            assert offset + data.size <= buf.len, "emitSectionAtOffset: buffer overflow";
113 113
            for j in 0..data.values.len {
114 114
                let v = &data.values[j];
lib/std/lang/gen/labels.rad +1 -1
10 10
export constant MAX_FUNCS: u32 = 8192;
11 11
/// Size of the function hash table. Must be a power of two.
12 12
export constant FUNC_TABLE_SIZE: u32 = MAX_FUNCS * 2;
13 13
14 14
/// Label tracking for code emission.
15 -
export record Labels: Copy {
15 +
export record Labels {
16 16
    /// Block offsets indexed by block index.
17 17
    /// Per-function, reset each function.
18 18
    blockOffsets: *mut [i32],
19 19
    /// Number of blocks recorded in current function.
20 20
    blockCount: u32,
lib/std/lang/gen/regalloc.rad +2 -2
35 35
}
36 36
37 37
/// Complete register allocation result.
38 38
export record AllocResult: Copy {
39 39
    /// SSA register to physical register mapping.
40 -
    assignments: *[?super::Reg],
40 +
    assignments: *unsafe [?super::Reg],
41 41
    /// Spill slot information.
42 42
    spill: spill::SpillInfo,
43 43
    /// Bitmask of used callee-saved registers.
44 44
    usedCalleeSaved: u32,
45 45
}
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 51
export unsafe fn allocate(
52 -
    func: *il::Fn,
52 +
    func: *unsafe il::Fn,
53 53
    config: &TargetConfig,
54 54
    arena: &mut alloc::Arena
55 55
) -> AllocResult throws (alloc::AllocError) {
56 56
    // Phase 1: Liveness analysis.
57 57
    let live = try liveness::analyze(func, arena);
lib/std/lang/gen/regalloc/assign.rad +18 -17
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: Copy {
21 +
export record RegMap {
22 22
    /// SSA (virtual) registers that have mappings.
23 23
    virtRegs: *mut [u32],
24 24
    /// Physical register for each virtual register.
25 25
    physRegs: *mut [gen::Reg],
26 26
    /// Number of active mappings.
28 28
}
29 29
30 30
/// Register assignment result, per function.
31 31
export record AssignInfo: Copy {
32 32
    /// SSA register -> physical register mapping.
33 -
    assignments: *mut [?gen::Reg],
33 +
    assignments: *unsafe mut [?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 39
record InstrCtx: Copy {
40 40
    current: *unsafe mut RegMap,
41 41
    usedRegs: *unsafe mut bitset::Bitset,
42 42
    /// Last operand-use index for each register used in the current block.
43 -
    lastUse: *[u32],
43 +
    lastUse: *unsafe [u32],
44 44
    live: *unsafe liveness::LiveInfo,
45 45
    blockIdx: u32,
46 46
    instrIdx: u32,
47 47
    allocatable: *[gen::Reg],
48 48
    calleeSaved: *[gen::Reg],
49 -
    assignments: *mut [?gen::Reg],
49 +
    assignments: *unsafe mut [?gen::Reg],
50 50
    spillInfo: *unsafe spill::SpillInfo,
51 51
}
52 52
53 53
/// Context for recording the last operand-use index in a block.
54 54
record LastUseCtx: Copy {
55 55
    /// Per-register indices, shared by all blocks in the function.
56 -
    lastUse: *mut [u32],
56 +
    lastUse: *unsafe mut [u32],
57 57
    /// Index of the instruction whose operands are being recorded.
58 58
    index: u32,
59 59
}
60 60
61 61
/// Compute register assignment.
62 62
export unsafe fn assign(
63 -
    func: *il::Fn,
63 +
    func: *unsafe il::Fn,
64 64
    live: &liveness::LiveInfo,
65 65
    spillInfo: &spill::SpillInfo,
66 66
    config: &super::TargetConfig,
67 67
    arena: &mut alloc::Arena
68 68
) -> AssignInfo throws (alloc::AllocError) {
76 76
            usedCalleeSaved: 0,
77 77
        };
78 78
    }
79 79
80 80
    // Allocate output structures.
81 -
    let assignments = try alloc::allocSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *mut [?gen::Reg];
81 +
    let assignments = try alloc::allocRawSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *unsafe mut [?gen::Reg];
82 82
    for i in 0..maxReg {
83 83
        set assignments[i] = nil;
84 84
    }
85 85
    // Reuse one last-use table for all blocks in the function.
86 -
    let lastUse = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *mut [u32];
86 +
    let lastUse = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *unsafe mut [u32];
87 87
    // Pre-assign function parameters to argument registers.
88 88
    // Cross-call params are NOT pre-assigned here; they will be allocated
89 89
    // to callee-saved registers by the normal path, and isel emits moves
90 90
    // from the arg register to the assigned register at function entry.
91 91
    for param, i in func.params {
94 94
                set assignments[param.value.n] = config.argRegs[i];
95 95
            }
96 96
        }
97 97
    }
98 98
    // Allocate used registers bitset (32 physical registers per 32-bit word).
99 -
    let usedRegsBits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *mut [u32];
99 +
    let usedRegsBits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *unsafe mut [u32];
100 100
    let mut usedRegs = bitset::init(usedRegsBits);
101 101
102 102
    // Current register mapping.
103 103
    let mut current = try createRegMap(arena);
104 104
271 271
    panic "rallocReg: no free register, spilling fault";
272 272
}
273 273
274 274
/// Record the current index; forward traversal leaves the last operand use.
275 275
unsafe fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) {
276 -
    recordLastUse(reg, ctxPtr as &mut LastUseCtx);
276 +
    let index = (ctxPtr as &mut LastUseCtx).index;
277 +
    recordLastUse(reg, &mut (ctxPtr as &mut LastUseCtx).lastUse[..], index);
277 278
}
278 279
279 280
/// Record the last instruction that uses the register.
280 -
fn recordLastUse(reg: il::Reg, ctx: &mut LastUseCtx) {
281 -
    set ctx.lastUse[reg.n] = ctx.index;
281 +
fn recordLastUse(reg: il::Reg, lastUse: &mut [u32], index: u32) {
282 +
    set lastUse[reg.n] = index;
282 283
}
283 284
284 285
/// Free operands with no later block use or live-out use, then allocate missing uses.
285 286
unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) {
286 287
    processInstrReg(reg, ctxPtr as &mut InstrCtx);
287 288
}
288 289
289 290
/// Release expired registers and assign a register for this operand.
290 291
unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) {
291 292
    if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) {
292 -
        if let phys = rmapRemove(&mut *ctx.current, reg.n) {
293 -
            bitset::clear(&mut *ctx.usedRegs, *phys as u32);
293 +
        if let phys = rmapRemove(ctx.current, reg.n) {
294 +
            bitset::clear(ctx.usedRegs, *phys as u32);
294 295
        }
295 296
    }
296 297
    assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds";
297 -
    if spill::isSpilled(&*ctx.spillInfo, reg) {
298 +
    if spill::isSpilled(ctx.spillInfo, reg) {
298 299
        return; // Spilled values don't get physical registers.
299 300
    }
300 301
    if ctx.assignments[reg.n] == nil {
301 302
        let current = ctx.current;
302 303
        let usedRegs = ctx.usedRegs;
303 304
        set ctx.assignments[reg.n] = rallocReg(
304 -
            &mut *current, &mut *usedRegs, reg.n, ctx.allocatable,
305 -
            ctx.calleeSaved, &*ctx.spillInfo
305 +
            current, usedRegs, reg.n, ctx.allocatable,
306 +
            ctx.calleeSaved, ctx.spillInfo
306 307
        );
307 308
    }
308 309
}
lib/std/lang/gen/regalloc/liveness.rad +19 -19
33 33
export constant MAX_SSA_REGS: u32 = 8192;
34 34
35 35
/// Liveness information for a function.
36 36
export record LiveInfo: Copy {
37 37
    /// Per-block live-in sets (indexed by block index).
38 -
    liveIn: *mut [bitset::Bitset],
38 +
    liveIn: *unsafe mut [bitset::Bitset],
39 39
    /// Per-block live-out sets (indexed by block index).
40 -
    liveOut: *mut [bitset::Bitset],
40 +
    liveOut: *unsafe mut [bitset::Bitset],
41 41
    /// Per-block defs sets (registers defined in block).
42 -
    defs: *mut [bitset::Bitset],
42 +
    defs: *unsafe mut [bitset::Bitset],
43 43
    /// Per-block uses sets (registers used before defined in block).
44 -
    uses: *mut [bitset::Bitset],
44 +
    uses: *unsafe mut [bitset::Bitset],
45 45
    /// Number of blocks.
46 46
    blockCount: u32,
47 47
    /// Maximum register number used.
48 48
    maxReg: u32,
49 49
}
50 50
51 51
/// Context for collecting defs and uses during block analysis.
52 52
record DefsUses: Copy {
53 -
    defs: *bitset::Bitset,
54 -
    uses: *mut bitset::Bitset,
53 +
    defs: *unsafe bitset::Bitset,
54 +
    uses: *unsafe mut bitset::Bitset,
55 55
}
56 56
57 57
/// Context for searching for a specific register in an instruction.
58 58
record FindCtx: Copy {
59 59
    target: u32,
60 60
    found: bool,
61 61
}
62 62
63 63
/// Compute liveness by growing live sets until no live-in set changes.
64 -
export unsafe fn analyze(func: *il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) {
64 +
export unsafe fn analyze(func: *unsafe il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) {
65 65
    let blockCount = func.blocks.len;
66 66
    if blockCount == 0 {
67 67
        return LiveInfo {
68 68
            liveIn: &mut [],
69 69
            liveOut: &mut [],
91 91
            }
92 92
        }
93 93
    }
94 94
    assert maxReg <= MAX_SSA_REGS, "analyze: maximum SSA registers exceeded";
95 95
    // Allocate per-block bitsets.
96 -
    let liveIn = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
97 -
    let liveOut = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
98 -
    let defs = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
99 -
    let uses = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
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 100
101 101
    for b in 0..blockCount {
102 102
        set liveIn[b] = try bitset::allocate(arena, maxReg);
103 103
        set liveOut[b] = try bitset::allocate(arena, maxReg);
104 104
        set defs[b] = try bitset::allocate(arena, maxReg);
133 133
}
134 134
135 135
/// Compute `liveIn = uses | (liveOut - defs)` and update `dst`.
136 136
/// Returns `true` if `dst` changed. Combined loop avoids multiple passes.
137 137
unsafe fn computeAndUpdateLiveIn(
138 -
    dst: *mut bitset::Bitset,
139 -
    liveOut: *bitset::Bitset,
140 -
    defs: *bitset::Bitset,
141 -
    uses: *bitset::Bitset
138 +
    dst: *unsafe mut bitset::Bitset,
139 +
    liveOut: *unsafe bitset::Bitset,
140 +
    defs: *unsafe bitset::Bitset,
141 +
    uses: *unsafe bitset::Bitset
142 142
) -> bool {
143 143
    let numWords = dst.bits.len;
144 144
    let mut changed = false;
145 145
    for i in 0..numWords {
146 146
        let newWord = uses.bits[i] | (liveOut.bits[i] & ~defs.bits[i]);
151 151
    }
152 152
    return changed;
153 153
}
154 154
155 155
/// Compute local defs and uses for a single block.
156 -
unsafe fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) {
156 +
unsafe fn computeLocalDefsUses(block: *unsafe il::Block, defs: *unsafe mut bitset::Bitset, uses: *unsafe mut bitset::Bitset) {
157 157
    for p in block.params {
158 158
        bitset::put(defs, p.value.n);
159 159
    }
160 160
    for i in 0..block.instrs.len {
161 161
        let instr = block.instrs[i];
197 197
    }
198 198
    return current;
199 199
}
200 200
201 201
/// Add successor live-in sets to the block's live-out set.
202 -
unsafe fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
202 +
unsafe fn addSuccessorLiveIn(func: *unsafe il::Fn, block: *unsafe il::Block, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) {
203 203
    if block.instrs.len == 0 {
204 204
        return;
205 205
    }
206 206
    let term = block.instrs[block.instrs.len - 1];
207 207
221 221
        else => {},
222 222
    }
223 223
}
224 224
225 225
/// Union a target block's live-in set into the block's live-out set.
226 -
unsafe fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
226 +
unsafe fn unionBlockLiveIn(target: u32, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) {
227 227
    bitset::union_(liveOut, &liveIn[target]);
228 228
}
229 229
230 230
/// Check if a register has any use after this instruction.
231 -
export unsafe fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
231 +
export unsafe fn hasLaterUse(info: *unsafe LiveInfo, func: *unsafe il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
232 232
    let block = &func.blocks[blockIdx];
233 233
234 234
    if bitset::contains(&info.liveOut[blockIdx], reg.n) {
235 235
        return true;
236 236
    }
lib/std/lang/gen/regalloc/spill.rad +13 -12
49 49
}
50 50
51 51
/// Spill decision for a function.
52 52
export record SpillInfo: Copy {
53 53
    /// SSA register mapped to stack slot offset. `-1` means not spilled.
54 -
    slots: *mut [i32],
54 +
    slots: *[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 58
    calleeClass: bitset::Bitset,
59 59
    /// Maximum SSA register number.
72 72
    cost: u32,
73 73
}
74 74
75 75
/// Context for counting register uses.
76 76
record CountCtx: Copy {
77 -
    costs: *mut [SpillCost],
77 +
    costs: *unsafe mut [SpillCost],
78 78
    weight: u32,
79 79
}
80 80
81 81
/// Analyze a function and determine which values need spill slots.
82 82
export unsafe fn analyze(
83 -
    func: *il::Fn,
83 +
    func: *unsafe il::Fn,
84 84
    live: &liveness::LiveInfo,
85 85
    numRegs: u32,
86 86
    numCalleeSaved: u32,
87 87
    slotSize: u32,
88 88
    arena: &mut alloc::Arena
101 101
    let slots = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), maxReg) as *mut [i32];
102 102
    for i in 0..maxReg {
103 103
        set slots[i] = -1;
104 104
    }
105 105
    // Allocate cost array.
106 -
    let costs = try alloc::allocSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *mut [SpillCost];
106 +
    let costs = try alloc::allocRawSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *unsafe mut [SpillCost];
107 107
    for i in 0..maxReg {
108 108
        set costs[i] = SpillCost { defs: 0, uses: 0 };
109 109
    }
110 110
    // Phase 1: Calculate spill costs.
111 111
    fillCosts(func, costs);
176 176
    }
177 177
    return SpillInfo { slots, frameSize, calleeClass, maxReg };
178 178
}
179 179
180 180
/// Calculate spill costs for all registers, weighted by loop depth.
181 -
unsafe fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) {
181 +
unsafe fn fillCosts(func: *unsafe il::Fn, costs: *unsafe mut [SpillCost]) {
182 182
    for b in 0..func.blocks.len {
183 183
        let block = &func.blocks[b];
184 184
185 185
        // Exponential weight for loop depth, capped to avoid overflow.
186 186
        let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth;
233 233
        bitset::clear(source, c.entries[i].reg);
234 234
    }
235 235
}
236 236
237 237
/// Collect all values from a bitset into a candidates buffer with their costs.
238 -
unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *[SpillCost]) -> Candidates {
238 +
unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *unsafe [SpillCost]) -> Candidates {
239 239
    let mut c = Candidates { entries: undefined, n: 0 };
240 240
    let mut it = bitset::iter(bs);
241 241
    while let reg = bitset::iterNext(&mut it) {
242 242
        assert c.n < MAX_CANDIDATES, "collectCandidates: too many live values";
243 243
        if reg < costs.len {
250 250
251 251
/// Limit register pressure by marking low-cost values as spilled.
252 252
unsafe fn limitPressure(
253 253
    live: &mut bitset::Bitset,
254 254
    spilled: &mut bitset::Bitset,
255 -
    costs: *[SpillCost],
255 +
    costs: *unsafe [SpillCost],
256 256
    numRegs: u32
257 257
) {
258 258
    let liveCount = bitset::count(live);
259 259
    if liveCount <= numRegs {
260 260
        return;
277 277
/// register. If the count exceeds `numCalleeSaved`, spill the cheapest
278 278
/// crossing values.
279 279
unsafe fn limitCrossCallPressure(
280 280
    live: &mut bitset::Bitset,
281 281
    spilled: &mut bitset::Bitset,
282 -
    costs: *[SpillCost],
282 +
    costs: *unsafe [SpillCost],
283 283
    calleeClass: &mut bitset::Bitset,
284 284
    numCalleeSaved: u32,
285 285
    callDst: ?il::Reg
286 286
) {
287 287
    // Collect crossing candidates: live values excluding the call destination.
311 311
    }
312 312
}
313 313
314 314
/// Callback for [`il::forEachReg`]: increments use count for register.
315 315
unsafe fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) {
316 -
    countRegUse(reg, ctxPtr as &mut CountCtx);
316 +
    let weight = (ctxPtr as &mut CountCtx).weight;
317 +
    countRegUse(reg, &mut (ctxPtr as &mut CountCtx).costs[..], weight);
317 318
}
318 319
319 320
/// Add the block weight to the register use count.
320 -
fn countRegUse(reg: il::Reg, ctx: &mut CountCtx) {
321 -
    assert reg.n < ctx.costs.len, "countRegUseCallback: register out of bounds";
322 -
    set ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight;
321 +
fn countRegUse(reg: il::Reg, costs: &mut [SpillCost], weight: u32) {
322 +
    assert reg.n < costs.len, "countRegUseCallback: register out of bounds";
323 +
    set costs[reg.n].uses += weight;
323 324
}
324 325
325 326
/// Callback for [`il::forEachReg`]: adds register to live set.
326 327
unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) {
327 328
    bitset::put(ctx as &mut bitset::Bitset, reg.n);
lib/std/lang/il.rad +14 -14
80 80
81 81
/// Separator for qualified symbol names.
82 82
export constant PATH_SEPARATOR: *[u8] = "::";
83 83
84 84
/// Format a qualified symbol name: `pkg::mod::path::name`.
85 -
export unsafe fn formatQualifiedName(arena: &mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] {
85 +
export unsafe fn formatQualifiedName(arena: &mut alloc::Arena, path: &[*[u8]], name: *[u8]) -> *[u8] {
86 86
    let mut totalLen: u32 = name.len;
87 87
    for segment in path {
88 88
        set totalLen += segment.len + PATH_SEPARATOR.len;
89 89
    }
90 90
    let buf = try! alloc::allocSlice(arena, 1, 1, totalLen) as *mut [u8];
179 179
    /// The constant value to match against.
180 180
    value: i64,
181 181
    /// The target block index.
182 182
    target: u32,
183 183
    /// Arguments to pass to the target block.
184 -
    args: *mut [Val],
184 +
    args: *unsafe mut [Val],
185 185
}
186 186
187 187
/// IL instruction.
188 188
/// SSA registers are represented as `Reg`, values as `Val`.
189 189
export union Instr: Copy {
265 265
        /// Holds return value, for non-void function.
266 266
        dst: ?Reg,
267 267
        /// Function value, either a symbol or an address in a register.
268 268
        func: Val,
269 269
        /// Function arguments.
270 -
        args: *[Val]
270 +
        args: *unsafe [Val]
271 271
    },
272 272
273 273
    /////////////////
274 274
    // Terminators //
275 275
    /////////////////
276 276
277 277
    /// Return from function: `ret <val>;` or `ret;`
278 278
    Ret { val: ?Val },
279 279
    /// Unconditional jump: `jmp @block(<arg>...);`
280 -
    Jmp { target: u32, args: *mut [Val] },
280 +
    Jmp { target: u32, args: *unsafe mut [Val] },
281 281
    /// Compare-and-branch: `br.<op> <type> <a> <b> @then @else;`
282 -
    Br { op: CmpOp, typ: Type, a: Val, b: Val, thenTarget: u32, thenArgs: *mut [Val], elseTarget: u32, elseArgs: *mut [Val] },
282 +
    Br { op: CmpOp, typ: Type, a: Val, b: Val, thenTarget: u32, thenArgs: *unsafe mut [Val], elseTarget: u32, elseArgs: *unsafe mut [Val] },
283 283
    /// Multi-way branch: `switch <val> (<n> @block) ... @default;`
284 -
    Switch { val: Val, defaultTarget: u32, defaultArgs: *mut [Val], cases: *mut [SwitchCase] },
284 +
    Switch { val: Val, defaultTarget: u32, defaultArgs: *unsafe mut [Val], cases: *unsafe mut [SwitchCase] },
285 285
    /// Unreachable code marker: `unreachable;`
286 286
    /// Indicates control flow cannot reach this point to allow for optimizations.
287 287
    Unreachable,
288 288
289 289
    /////////////////
311 311
/// to another sequence.
312 312
export record Block: Copy {
313 313
    /// Block label.
314 314
    label: *[u8],
315 315
    /// Block parameters.
316 -
    params: *[Param],
316 +
    params: *unsafe [Param],
317 317
    /// Instructions in the block. The last instruction must be a terminator.
318 -
    instrs: *mut [Instr],
318 +
    instrs: *unsafe mut [Instr],
319 319
    /// Source locations for debugging and error reporting.
320 320
    /// One entry per instruction when debug info is enabled, empty otherwise.
321 -
    locs: *[SrcLoc],
321 +
    locs: *unsafe [SrcLoc],
322 322
    /// Predecessor block indices. Used for control flow analysis.
323 -
    preds: *[u32],
323 +
    preds: *unsafe [u32],
324 324
    /// Loop nesting depth.
325 325
    /// Used for spill cost weighting in register allocation.
326 326
    loopDepth: u32,
327 327
}
328 328
329 329
/// An IL function.
330 330
export record Fn: Copy {
331 331
    /// Qualified function name (e.g. `$mod$path$func`).
332 332
    name: *[u8],
333 333
    /// Function parameters.
334 -
    params: *[Param],
334 +
    params: *unsafe [Param],
335 335
    /// Return type.
336 336
    returnType: Type,
337 337
    /// Whether the function is extern (no body).
338 338
    isExtern: bool,
339 339
    /// Whether the function is a leaf (contains no call or ecall instructions).
340 340
    isLeaf: bool,
341 341
    /// Basic blocks. Empty for extern functions.
342 -
    blocks: *[Block],
342 +
    blocks: *unsafe [Block],
343 343
}
344 344
345 345
/////////////
346 346
// Program //
347 347
/////////////
387 387
    values: *[DataValue],
388 388
}
389 389
390 390
/// An IL program (compilation unit).
391 391
export record Program: Copy {
392 -
    /// Global data.
392 +
    /// Immutable global data.
393 393
    data: *[Data],
394 394
    /// Functions.
395 -
    fns: *[*Fn],
395 +
    fns: *unsafe [*unsafe Fn],
396 396
}
397 397
398 398
///////////////////////
399 399
// Utility Functions //
400 400
///////////////////////
lib/std/lang/il/printer.rad +7 -6
144 144
    set buffer[start] = '%';
145 145
    write(out, &buffer[start..]);
146 146
}
147 147
148 148
/// Write a comma-separated argument list in parentheses.
149 -
unsafe fn writeArgs(out: &mut sexpr::Output, args: *[super::Val]) {
149 +
unsafe fn writeArgs(out: &mut sexpr::Output, args: *unsafe [super::Val]) {
150 150
    write(out, "(");
151 151
    for arg, i in args {
152 152
        if i > 0 {
153 153
            write(out, ", ");
154 154
        }
163 163
    write(out, " ");
164 164
    writeReg(out, param.value);
165 165
}
166 166
167 167
/// Write a comma-separated parameter list.
168 -
unsafe fn writeParams(out: &mut sexpr::Output, params: *[super::Param]) {
168 +
unsafe fn writeParams(out: &mut sexpr::Output, params: *unsafe [super::Param]) {
169 169
    for param, i in params {
170 170
        if i > 0 {
171 171
            write(out, ", ");
172 172
        }
173 173
        writeParam(out, param);
177 177
//////////////////////////
178 178
// Instruction printing //
179 179
//////////////////////////
180 180
181 181
/// Write an instruction.
182 -
unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) {
182 +
unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) {
183 183
    match inst {
184 184
        // Memory operations.
185 185
        case super::Instr::Reserve { dst, size, alignment } => {
186 186
            write(out, "reserve ");
187 187
            writeReg(out, dst);
387 387
////////////////////
388 388
// Block printing //
389 389
////////////////////
390 390
391 391
/// Write a basic block.
392 -
unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *[super::Block], block: *super::Block) {
392 +
unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *unsafe [super::Block], block: *unsafe super::Block) {
393 393
    // Block label.
394 394
    write(out, "  @");
395 395
    write(out, block.label);
396 396
397 397
    // Block parameters.
413 413
///////////////////////
414 414
// Function printing //
415 415
///////////////////////
416 416
417 417
/// Write a function.
418 -
unsafe fn writeFn(out: &mut sexpr::Output, f: *super::Fn) {
418 +
unsafe fn writeFn(out: &mut sexpr::Output, f: *unsafe super::Fn) {
419 419
    // Function signature.
420 420
    if f.isExtern {
421 421
        write(out, "extern ");
422 422
    }
423 423
    write(out, "fn ");
528 528
export unsafe fn printProgramToBuffer(
529 529
    program: &super::Program,
530 530
    buf: *mut [u8]
531 531
) -> *[u8] {
532 532
    let mut pos: u32 = 0;
533 -
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
533 +
    let raw: *unsafe mut [u8] = &mut buf[..];
534 +
    let mut out = sexpr::Output::Buffer { buf: raw, pos: &mut pos };
534 535
    printProgram(&mut out, program);
535 536
536 537
    return &buf[..pos];
537 538
}
lib/std/lang/lower.rad +241 -233
267 267
/// Function sink used by lowerers that consume functions as they are produced.
268 268
export record FnSink: Copy {
269 269
    /// Opaque context. It must remain valid for every sink callback.
270 270
    ctx: *unsafe mut opaque,
271 271
    /// Callback invoked for each lowered function.
272 -
    emitFn: unsafe fn(*unsafe mut opaque, *il::Fn, FnRole),
272 +
    emitFn: unsafe fn(*unsafe mut opaque, *unsafe il::Fn, FnRole),
273 273
}
274 274
275 275
/// Destination for functions produced by the lowerer.
276 276
export union FnOutput: Copy {
277 277
    /// Store lowered functions in the provided slice.
278 -
    Accumulate(*mut [*il::Fn]),
278 +
    Accumulate(*unsafe mut [*unsafe il::Fn]),
279 279
    /// Send lowered functions to an external consumer immediately.
280 280
    Stream(FnSink),
281 281
}
282 282
283 283
/// Module-level lowering context. Shared across all function lowerings.
284 284
/// Holds global state like the data section (strings, constants) and provides
285 285
/// access to the resolver for type queries.
286 -
export record Lowerer: Copy {
286 +
export record Lowerer {
287 287
    /// Arena for persistent lowering state, including data and symbol names.
288 288
    arena: *unsafe mut alloc::Arena,
289 289
    /// Arena for allocations owned by the function currently being lowered.
290 290
    fnArena: *unsafe mut alloc::Arena,
291 291
    /// Allocator backed by the arena.
314 314
    options: LowerOptions,
315 315
}
316 316
317 317
/// Entry mapping a function symbol to its qualified name.
318 318
record FnSymEntry: Copy {
319 -
    sym: *resolver::Symbol,
319 +
    sym: *unsafe resolver::Symbol,
320 320
    qualName: *[u8],
321 321
}
322 322
323 323
/// Entry in the global error tag table.
324 324
record ErrTagEntry: Copy {
327 327
    /// The globally unique tag assigned to this error type (non-zero).
328 328
    tag: u32,
329 329
}
330 330
331 331
/// Compute the maximum size of any error type in a throw list.
332 -
fn maxErrSize(throwList: *[*resolver::Type]) -> u32 {
332 +
unsafe fn maxErrSize(throwList: *[*resolver::Type]) -> u32 {
333 333
    let mut maxSize: u32 = 0;
334 334
    for ty in throwList {
335 335
        let size = resolver::getTypeLayout(*ty).size;
336 336
        if size > maxSize {
337 337
            set maxSize = size;
341 341
}
342 342
343 343
/// Get or assign a globally unique error tag for the given error type.
344 344
/// Tag `0` is reserved for success; error tags start at `1`.
345 345
fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 {
346 -
    for entry in self.errTags {
346 +
    for entry in &self.errTags[..] {
347 347
        if entry.ty == errType {
348 348
            return entry.tag;
349 349
        }
350 350
    }
351 351
    let tag = self.errTagCounter;
355 355
356 356
    return tag;
357 357
}
358 358
359 359
/// Emit one function to the active output.
360 -
unsafe fn emitFunction(self: &mut Lowerer, func: *il::Fn, role: FnRole) {
360 +
unsafe fn emitFunction(self: &mut Lowerer, func: *unsafe il::Fn, role: FnRole) {
361 361
    match self.output {
362 362
        case FnOutput::Accumulate(accumulated) => {
363 363
            let mut fns = accumulated;
364 364
            fns.append(func, self.allocator);
365 365
            set self.output = FnOutput::Accumulate(fns);
377 377
    }
378 378
    return FnRole::Normal;
379 379
}
380 380
381 381
/// Builder for accumulating data values during constant lowering.
382 -
record DataValueBuilder: Copy {
382 +
record DataValueBuilder {
383 +
    /// Allocator used to grow the value storage.
383 384
    allocator: alloc::Allocator,
385 +
    /// Owned values under construction.
384 386
    values: *mut [il::DataValue],
385 387
    /// Whether all pushed values can be represented by zero-filled memory.
386 388
    zeroInit: bool,
387 389
}
388 390
389 391
/// Result of lowering constant data.
390 392
record ConstDataResult: Copy {
393 +
    /// Immutable initializer values.
391 394
    values: *[il::DataValue],
395 +
    /// Whether the values can be represented by zero-filled memory.
392 396
    zeroInit: bool,
393 397
}
394 398
395 399
/// Create a new builder.
396 400
fn dataBuilder(allocator: alloc::Allocator) -> DataValueBuilder {
424 428
        set b.zeroInit = false;
425 429
    }
426 430
}
427 431
428 432
/// Return the accumulated values.
429 -
fn dataBuilderFinish(b: &DataValueBuilder) -> ConstDataResult {
430 -
    return ConstDataResult {
431 -
        values: &b.values[..],
432 -
        zeroInit: b.zeroInit,
433 -
    };
433 +
fn dataBuilderFinish(b: DataValueBuilder) -> ConstDataResult {
434 +
    let case DataValueBuilder { values, zeroInit, .. } = b
435 +
        else panic "expected data builder";
436 +
    return ConstDataResult { values, zeroInit };
434 437
}
435 438
436 439
///////////////////////////
437 440
// SSA Variable Tracking //
438 441
///////////////////////////
505 508
record BlockData: Copy {
506 509
    /// Block label for debugging and IL printing.
507 510
    label: *[u8],
508 511
    /// Block parameters for merging values at control flow joins. These
509 512
    /// receive values from predecessor edges when control flow merges.
510 -
    params: *mut [il::Param],
513 +
    params: *unsafe mut [il::Param],
511 514
    /// Variable ids in parameter order. Before sealing, these are the variables
512 515
    /// whose predecessor arguments must be resolved.
513 -
    paramVars: *mut [u32],
516 +
    paramVars: *unsafe mut [u32],
514 517
    /// Instructions accumulated so far. The last instruction should eventually
515 518
    /// be a terminator.
516 -
    instrs: *mut [il::Instr],
519 +
    instrs: *unsafe mut [il::Instr],
517 520
    /// Debug source locations, one per instruction. Only populated when
518 521
    /// debug info is enabled.
519 -
    locs: *mut [il::SrcLoc],
522 +
    locs: *unsafe mut [il::SrcLoc],
520 523
    /// Predecessor block ids. Used for SSA construction to propagate values
521 524
    /// from predecessors when a variable is used before being defined locally.
522 -
    preds: *mut [u32],
525 +
    preds: *unsafe mut [u32],
523 526
    /// The current SSA value of each variable in this block. Indexed by variable
524 527
    /// id. A `nil` means the variable wasn't assigned in this block. Updated by
525 528
    /// [`defVar`], queried by [`useVarInBlock`].
526 -
    vars: *mut [?il::Val],
529 +
    vars: *unsafe mut [?il::Val],
527 530
    /// Sealing state. Once sealed, all predecessors are known and we can resolve
528 531
    /// variable uses that need to pull values from predecessors.
529 532
    sealState: Sealed,
530 533
    /// Loop nesting depth when this block was created.
531 534
    loopDepth: u32,
621 624
    /// Union type: tag compared against variant indices.
622 625
    Union(resolver::UnionType),
623 626
}
624 627
625 628
/// Determine the kind of a match subject from its type.
626 -
fn matchSubjectKind(type: resolver::Type) -> MatchSubjectKind {
629 +
unsafe fn matchSubjectKind(type: resolver::Type) -> MatchSubjectKind {
627 630
    if resolver::isOptionalPointer(type) {
628 631
        return MatchSubjectKind::OptionalPtr;
629 632
    }
630 633
    if resolver::isOptionalAggregate(type) {
631 634
        return MatchSubjectKind::OptionalAggregate;
677 680
    low: *unsafe mut Lowerer,
678 681
    /// Allocator for IL allocations.
679 682
    allocator: alloc::Allocator,
680 683
    /// Type signature of the function being lowered.
681 684
    fnType: *resolver::FnType,
685 +
    /// Number of SSA variable slots required for each block.
686 +
    localCount: u32,
682 687
    /// Function name, used as prefix for generated data symbols.
683 688
    fnName: *[u8],
684 689
685 690
    // ~ SSA variable tracking ~ //
686 691
687 692
    /// Metadata (name, type, mutability) for each variable. Indexed by variable
688 693
    /// id. Doesn't change after declaration. For the SSA value of a variable in
689 694
    /// a specific block, see [`BlockData::vars`].
690 -
    vars: *mut [VarData],
695 +
    vars: *unsafe mut [VarData],
691 696
    /// Parameter-to-variable bindings, initialized in the entry block.
692 -
    params: *mut [FnParamBinding],
697 +
    params: *unsafe mut [FnParamBinding],
693 698
694 699
    // ~ Basic block management ~ //
695 700
696 701
    /// Block storage array, indexed by block id.
697 -
    blockData: *mut [BlockData],
702 +
    blockData: *unsafe mut [BlockData],
698 703
    /// The entry block for this function.
699 704
    entryBlock: ?BlockId,
700 705
    /// The block currently receiving new instructions.
701 706
    currentBlock: ?BlockId,
702 707
703 708
    // ~ Loop management ~ //
704 709
705 710
    /// Stack of loop contexts for break/continue resolution.
706 -
    loopStack: *mut [LoopCtx],
711 +
    loopStack: *unsafe mut [LoopCtx],
707 712
    /// Current nesting depth (index into loopStack).
708 713
    loopDepth: u32,
709 714
710 715
    // ~ Counters ~ //
711 716
747 752
    root: *ast::Node,
748 753
    pkgName: *[u8],
749 754
    arena: &mut alloc::Arena
750 755
) -> il::Program throws (LowerError) {
751 756
    let mut low = Lowerer {
752 -
        arena: arena as *unsafe mut alloc::Arena,
753 -
        fnArena: arena as *unsafe mut alloc::Arena,
757 +
        arena: (&mut *arena) as *unsafe mut alloc::Arena,
758 +
        fnArena: (&mut *arena) as *unsafe mut alloc::Arena,
754 759
        allocator: alloc::arenaAllocator(arena),
755 760
        resolver: res as *unsafe resolver::Resolver,
756 761
        moduleGraph: nil,
757 762
        pkgName,
758 763
        currentMod: nil,
763 768
        errTagCounter: 1,
764 769
        options: LowerOptions { debug: false, buildTest: false },
765 770
    };
766 771
    try lowerDecls(&mut low, root, true);
767 772
768 -
    return finalize(&low);
773 +
    return finalize(low);
769 774
}
770 775
771 776
/////////////////////////////////
772 777
// Multi-Module Lowering API   //
773 778
/////////////////////////////////
781 786
    arena: *unsafe mut alloc::Arena,
782 787
    fnArena: *unsafe mut alloc::Arena,
783 788
    options: LowerOptions
784 789
) -> Lowerer {
785 790
    return Lowerer {
786 -
        arena: arena as *unsafe mut alloc::Arena,
787 -
        fnArena: fnArena as *unsafe mut alloc::Arena,
788 -
        allocator: alloc::arenaAllocator(&mut *arena),
789 -
        resolver: res as *unsafe resolver::Resolver,
791 +
        arena,
792 +
        fnArena,
793 +
        allocator: alloc::arenaAllocator(arena),
794 +
        resolver: res,
790 795
        moduleGraph: graph as *unsafe module::ModuleGraph,
791 796
        pkgName,
792 797
        currentMod: nil,
793 798
        data: &mut [],
794 799
        output: FnOutput::Accumulate(&mut []),
843 848
            else => {},
844 849
        }
845 850
    }
846 851
}
847 852
848 -
/// Finalize lowering and return the unified IL program.
849 -
export fn finalize(low: &Lowerer) -> il::Program {
850 -
    match low.output {
853 +
/// 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
856 +
        else panic "expected lowerer";
857 +
    match output {
851 858
        case FnOutput::Accumulate(accumulated) => {
852 859
            return il::Program {
853 -
                data: &low.data[..],
854 -
                fns: &accumulated[..],
860 +
                data,
861 +
                fns: accumulated,
855 862
            };
856 863
        }
857 864
        case FnOutput::Stream(_) => {
858 865
            panic "finalize: cannot finalize streaming lowerer";
859 866
        }
875 882
        set id = self.currentMod;
876 883
    }
877 884
    let actualId = id else {
878 885
        return &[];
879 886
    };
880 -
    let entry = module::get(&*graph, actualId) else {
887 +
    let entry = module::get(graph, actualId) else {
881 888
        return &[];
882 889
    };
883 890
    return module::moduleQualifiedPath(entry);
884 891
}
885 892
888 895
unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] {
889 896
    let path = getModulePath(self, modId);
890 897
    if path.len == 0 {
891 898
        return name;
892 899
    }
893 -
    return il::formatQualifiedName(&mut *self.arena, path, name);
900 +
    return il::formatQualifiedName(self.arena, path, name);
894 901
}
895 902
896 903
/// Register a function symbol with its qualified name.
897 904
/// Called when lowering function declarations, so cross-package calls can find
898 905
/// the function by name.
899 -
fn registerFnSym(self: &mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) {
906 +
fn registerFnSym(self: &mut Lowerer, sym: *unsafe resolver::Symbol, qualName: *[u8]) {
900 907
    self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
901 908
}
902 909
903 910
/// Look up a function's qualified name by its symbol.
904 911
/// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered).
905 912
// TODO: This is kind of dubious as an optimization, if it depends on the order
906 913
// in which modules are lowered.
907 914
// TODO: Use a hash table here?
908 -
fn lookupFnSym(self: &Lowerer, sym: *resolver::Symbol) -> ?*[u8] {
909 -
    for entry in self.fnSyms {
915 +
unsafe fn lookupFnSym(self: &Lowerer, sym: *unsafe resolver::Symbol) -> ?*[u8] {
916 +
    for entry in &self.fnSyms[..] {
910 917
        if entry.sym == sym {
911 918
            return entry.qualName;
912 919
        }
913 920
    }
914 921
    return nil;
928 935
    self: &mut Lowerer,
929 936
    node: *ast::Node,
930 937
    fnType: *resolver::FnType,
931 938
    qualName: *[u8]
932 939
) -> FnLowerer {
933 -
    let loopStack = try! alloc::allocSlice(&mut *self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx];
940 +
    let loopStack = try! alloc::allocRawSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *unsafe mut [LoopCtx];
934 941
935 942
    let mut fnLow = FnLowerer {
936 -
        low: self as *unsafe mut Lowerer,
937 -
        allocator: alloc::arenaAllocator(&mut *self.fnArena),
943 +
        low: (&mut *self) as *unsafe mut Lowerer,
944 +
        allocator: alloc::arenaAllocator(self.fnArena),
938 945
        fnType: fnType,
946 +
        localCount: resolver::nodeData(self.resolver, node).localCount,
939 947
        fnName: qualName,
940 948
        vars: &mut [],
941 949
        params: &mut [],
942 950
        blockData: &mut [],
943 951
        entryBlock: nil,
968 976
/// This sets up the per-function lowering state, processes parameters,
969 977
/// then lowers the function body into a CFG of basic blocks.
970 978
///
971 979
/// For throwing functions, the return type is a result aggregate
972 980
/// rather than the declared return type.
973 -
unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) {
981 +
unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*unsafe il::Fn throws (LowerError) {
974 982
    if not shouldLowerFn(&decl, self.options.buildTest) {
975 983
        return nil;
976 984
    }
977 985
    let case ast::NodeValue::Ident(name) = decl.name.value else {
978 986
        throw LowerError::ExpectedIdentifier;
979 987
    };
980 -
    let data = resolver::nodeData(&*self.resolver, node);
988 +
    let data = resolver::nodeData(self.resolver, node);
981 989
    let case resolver::Type::Fn(fnType) = data.ty else {
982 990
        throw LowerError::ExpectedFunction;
983 991
    };
984 992
    let isExtern = checkAttr(decl.attrs, ast::Attribute::Extern);
985 993
997 1005
    // as the first argument; the callee writes the return value into it.
998 1006
    if requiresReturnParam(fnType) and not isExtern {
999 1007
        set fnLow.returnReg = nextReg(&mut fnLow);
1000 1008
    }
1001 1009
    let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil);
1002 -
    let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1010 +
    let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1003 1011
1004 1012
    set *func = il::Fn {
1005 1013
        name: qualName,
1006 1014
        params: lowParams,
1007 1015
        returnType: undefined,
1031 1039
1032 1040
/// Build a qualified name of the form "Type::method".
1033 1041
unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] {
1034 1042
    let sepLen: u32 = 2; // "::"
1035 1043
    let totalLen = typeName.len + sepLen + methodName.len;
1036 -
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1044 +
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1037 1045
    let mut pos: u32 = 0;
1038 1046
1039 1047
    set pos += try! mem::copy(&mut buf[pos..], typeName);
1040 1048
    set pos += try! mem::copy(&mut buf[pos..], "::");
1041 1049
    set pos += try! mem::copy(&mut buf[pos..], methodName);
1047 1055
/// Build a v-table data name of the form "vtable::Type::Trait".
1048 1056
unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] {
1049 1057
    let prefix = "vtable::";
1050 1058
    let sepLen: u32 = 2; // "::"
1051 1059
    let totalLen = prefix.len + typeName.len + sepLen + traitName.len;
1052 -
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1060 +
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1053 1061
    let mut pos: u32 = 0;
1054 1062
1055 1063
    set pos += try! mem::copy(&mut buf[pos..], prefix);
1056 1064
    set pos += try! mem::copy(&mut buf[pos..], typeName);
1057 1065
    set pos += try! mem::copy(&mut buf[pos..], "::");
1071 1079
unsafe fn lowerInstanceDecl(
1072 1080
    self: &mut Lowerer,
1073 1081
    node: *ast::Node,
1074 1082
    traitNameNode: *ast::Node,
1075 1083
    targetTypeNode: *ast::Node,
1076 -
    methods: *mut [*ast::Node]
1084 +
    methods: *[*ast::Node]
1077 1085
) throws (LowerError) {
1078 1086
    // Look up the trait and type from the resolver.
1079 -
    let traitSym = resolver::nodeData(&*self.resolver, traitNameNode).sym
1087 +
    let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym
1080 1088
        else throw LowerError::MissingSymbol(traitNameNode);
1081 1089
    let case resolver::SymbolData::Trait(traitInfo) = traitSym.data
1082 1090
        else throw LowerError::MissingMetadata;
1083 -
    let typeSym = resolver::nodeData(&*self.resolver, targetTypeNode).sym
1091 +
    let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym
1084 1092
        else throw LowerError::MissingSymbol(targetTypeNode);
1085 1093
1086 1094
    let tName = traitSym.name;
1087 1095
    let typeName = typeSym.name;
1088 1096
1122 1130
    }
1123 1131
1124 1132
    // Create v-table in data section, used for dynamic dispatch.
1125 1133
    let vName = vtableName(self, nil, typeName, tName);
1126 1134
    let values = try! alloc::allocSlice(
1127 -
        &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32
1135 +
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32
1128 1136
    ) as *mut [il::DataValue];
1129 1137
1130 1138
    for i in 0..traitInfo.methods.len {
1131 1139
        set values[i] = il::DataValue {
1132 1140
            item: il::DataItem::Fn(methodNames[i]),
1150 1158
    node: *ast::Node,
1151 1159
    qualName: *[u8],
1152 1160
    receiverName: *ast::Node,
1153 1161
    sig: ast::FnSig,
1154 1162
    body: *ast::Node,
1155 -
) -> ?*il::Fn throws (LowerError) {
1156 -
    let data = resolver::nodeData(&*self.resolver, node);
1163 +
) -> ?*unsafe il::Fn throws (LowerError) {
1164 +
    let data = resolver::nodeData(self.resolver, node);
1157 1165
    let case resolver::Type::Fn(fnType) = data.ty else {
1158 1166
        throw LowerError::ExpectedFunction;
1159 1167
    };
1160 1168
    let sym = data.sym else throw LowerError::MissingSymbol(node);
1161 1169
    registerFnSym(self, sym, qualName);
1163 1171
    let mut fnLow = fnLowerer(self, node, fnType, qualName);
1164 1172
    if requiresReturnParam(fnType) {
1165 1173
        set fnLow.returnReg = nextReg(&mut fnLow);
1166 1174
    }
1167 1175
    let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1168 -
    let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1176 +
    let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn;
1169 1177
1170 1178
    set *func = il::Fn {
1171 1179
        name: qualName,
1172 1180
        params: lowParams,
1173 1181
        returnType: ilType(self, *fnType.returnType),
1191 1199
    node: *ast::Node,
1192 1200
    name: *ast::Node,
1193 1201
    receiverName: *ast::Node,
1194 1202
    sig: ast::FnSig,
1195 1203
    body: *ast::Node,
1196 -
) -> ?*il::Fn throws (LowerError) {
1197 -
    let sym = resolver::nodeData(&*self.resolver, node).sym
1204 +
) -> ?*unsafe il::Fn throws (LowerError) {
1205 +
    let sym = resolver::nodeData(self.resolver, node).sym
1198 1206
        else throw LowerError::MissingSymbol(node);
1199 1207
    let case ast::NodeValue::Ident(mName) = name.value
1200 1208
        else throw LowerError::ExpectedIdentifier;
1201 -
    let me = resolver::findMethodBySymbol(&*self.resolver, sym)
1209 +
    let me = resolver::findMethodBySymbol(self.resolver, sym)
1202 1210
        else throw LowerError::MissingMetadata;
1203 1211
    let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName);
1204 1212
1205 1213
    return try lowerMethod(self, node, qualName, receiverName, sig, body);
1206 1214
}
1225 1233
/// This ensures unique labels like `@then0`, `@then1`, etc.
1226 1234
unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) {
1227 1235
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1228 1236
    let start = fmt::formatU32(suffix, &mut digits[..]);
1229 1237
    let totalLen = base.len + digits.len - start;
1230 -
    let buf = try! alloc::allocSlice(&mut *self.low.fnArena, 1, 1, totalLen) as *mut [u8];
1238 +
    let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8];
1231 1239
1232 1240
    try! mem::copy(&mut buf[..base.len], base);
1233 1241
    try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]);
1234 1242
1235 1243
    return &buf[..totalLen];
1274 1282
    }
1275 1283
    return il::Val::Imm(constToScalar(val));
1276 1284
}
1277 1285
1278 1286
/// Convert a resolver constant value to an IL data initializer item.
1279 -
fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem {
1287 +
unsafe fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem {
1280 1288
    if let case resolver::ConstValue::String(s) = val {
1281 1289
        return il::DataItem::Str(s);
1282 1290
    }
1283 1291
    // Bool and char are byte-sized; integer uses the declared type.
1284 1292
    let mut irTyp = il::Type::W8;
1295 1303
    node: *ast::Node,
1296 1304
    ty: resolver::Type,
1297 1305
    dataPrefix: *[u8],
1298 1306
    b: &mut DataValueBuilder
1299 1307
) throws (LowerError) {
1300 -
    let val = resolver::constValueEntry(&*self.resolver, node) else {
1301 -
        if let idx = voidVariantIndex(&*self.resolver, node) {
1308 +
    let val = resolver::constValueEntry(self.resolver, node) else {
1309 +
        if let idx = voidVariantIndex(self.resolver, node) {
1302 1310
            dataBuilderPush(b, il::DataValue {
1303 1311
                item: il::DataItem::Val { typ: il::Type::W8, val: idx },
1304 1312
                count: 1
1305 1313
            });
1306 1314
            return;
1326 1334
    self: &mut Lowerer,
1327 1335
    node: *ast::Node,
1328 1336
    value: *ast::Node,
1329 1337
    readOnly: bool
1330 1338
) throws (LowerError) {
1331 -
    let data = resolver::nodeData(&*self.resolver, node);
1339 +
    let data = resolver::nodeData(self.resolver, node);
1332 1340
    let sym = data.sym else {
1333 1341
        throw LowerError::MissingSymbol(node);
1334 1342
    };
1335 1343
    if data.ty == resolver::Type::Unknown {
1336 1344
        throw LowerError::MissingType(node);
1337 1345
    }
1338 1346
    let layout = resolver::getTypeLayout(data.ty);
1339 1347
    let qualName = qualifyName(self, nil, sym.name);
1340 1348
    let mut b = dataBuilder(self.allocator);
1341 1349
    try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b);
1342 -
    let result = dataBuilderFinish(&b);
1350 +
    let result = dataBuilderFinish(b);
1343 1351
1344 1352
    self.data.append(il::Data {
1345 1353
        name: qualName,
1346 1354
        size: layout.size,
1347 1355
        alignment: layout.alignment,
1381 1389
    dataPrefix: *[u8],
1382 1390
    b: &mut DataValueBuilder
1383 1391
) throws (LowerError) {
1384 1392
    let case resolver::Type::Slice { mutable, .. } = ty
1385 1393
        else throw LowerError::ExpectedSliceOrArray;
1386 -
    let targetTy = resolver::typeFor(&*self.resolver, addr.target)
1394 +
    let targetTy = resolver::typeFor(self.resolver, addr.target)
1387 1395
        else throw LowerError::MissingType(addr.target);
1388 1396
    let case resolver::Type::Array(arrInfo) = targetTy
1389 1397
        else throw LowerError::ExpectedArray;
1390 1398
1391 1399
    let mut nested = dataBuilder(self.allocator);
1392 1400
    let layout = resolver::getTypeLayout(targetTy);
1393 1401
    try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested);
1394 1402
1395 -
    let backing = dataBuilderFinish(&nested);
1403 +
    let backing = dataBuilderFinish(nested);
1396 1404
    let readOnly = not mutable;
1397 1405
    let mut dataName: *[u8] = undefined;
1398 1406
    if readOnly {
1399 1407
        if let found = findConstData(self, backing.values, layout.alignment) {
1400 1408
            set dataName = found;
1416 1424
    dataPrefix: *[u8],
1417 1425
    b: &mut DataValueBuilder
1418 1426
) throws (LowerError) {
1419 1427
    // Function pointer references in constant data.
1420 1428
    if let case resolver::Type::Fn(_) = ty {
1421 -
        let sym = resolver::nodeData(&*self.resolver, node).sym
1429 +
        let sym = resolver::nodeData(self.resolver, node).sym
1422 1430
            else throw LowerError::MissingSymbol(node);
1423 -
        let modId = resolver::moduleIdForSymbol(&*self.resolver, sym);
1431 +
        let modId = resolver::moduleIdForSymbol(self.resolver, sym);
1424 1432
        let qualName = qualifyName(self, modId, sym.name);
1425 1433
        dataBuilderPush(b, il::DataValue {
1426 1434
            item: il::DataItem::Fn(qualName), count: 1,
1427 1435
        });
1428 1436
        return;
1429 1437
    }
1430 1438
    // In constant data, a void variant of a mixed union still occupies the
1431 1439
    // full tagged-union slot. Emitting only the tag corrupts following fields.
1432 -
    if let sym = resolver::nodeData(&*self.resolver, node).sym {
1440 +
    if let sym = resolver::nodeData(self.resolver, node).sym {
1433 1441
        if let case resolver::SymbolData::Variant { type: resolver::Type::Void, .. } = sym.data {
1434 1442
            if let case resolver::Type::Nominal(resolver::NominalType::Union(_)) = ty {
1435 1443
                try lowerConstUnionVariantInto(self, node, sym, ty, &mut [], dataPrefix, b);
1436 1444
                return;
1437 1445
            }
1450 1458
        case ast::NodeValue::ArrayRepeatLit(repeat) =>
1451 1459
            try lowerConstArrayRepeatInto(self, repeat, ty, dataPrefix, b),
1452 1460
        case ast::NodeValue::RecordLit(recLit) =>
1453 1461
            try lowerConstRecordLitInto(self, node, recLit, ty, dataPrefix, b),
1454 1462
        case ast::NodeValue::Call(call) => {
1455 -
            let calleeSym = resolver::nodeData(&*self.resolver, call.callee).sym
1463 +
            let calleeSym = resolver::nodeData(self.resolver, call.callee).sym
1456 1464
                else throw LowerError::MissingSymbol(call.callee);
1457 1465
            match calleeSym.data {
1458 1466
                case resolver::SymbolData::Variant { .. } =>
1459 1467
                    try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b),
1460 1468
                case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => {
1466 1474
        case ast::NodeValue::AddressOf(addr) => {
1467 1475
            try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b);
1468 1476
        }
1469 1477
        case ast::NodeValue::Ident(_) => {
1470 1478
            // Identifier referencing a constant.
1471 -
            let sym = resolver::nodeData(&*self.resolver, node).sym
1479 +
            let sym = resolver::nodeData(self.resolver, node).sym
1472 1480
                else throw LowerError::MissingSymbol(node);
1473 1481
            let case ast::NodeValue::ConstDecl(decl) = sym.node.value
1474 1482
                else throw LowerError::MissingConst(node);
1475 1483
1476 1484
            try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b);
1477 1485
        },
1478 1486
        case ast::NodeValue::ScopeAccess(_) => {
1479 -
            let sym = resolver::nodeData(&*self.resolver, node).sym
1487 +
            let sym = resolver::nodeData(self.resolver, node).sym
1480 1488
                else throw LowerError::MissingSymbol(node);
1481 1489
            if let case ast::NodeValue::ConstDecl(decl) = sym.node.value {
1482 1490
                try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b);
1483 1491
            } else {
1484 1492
                try lowerConstScalarDataInto(self, node, ty, dataPrefix, b);
1511 1519
1512 1520
/// Flatten a constant array literal `[a, b, c]` into a builder.
1513 1521
/// Each element payload fills its type size; no extra slot padding is needed.
1514 1522
unsafe fn lowerConstArrayLitInto(
1515 1523
    self: &mut Lowerer,
1516 -
    elems: *mut [*ast::Node],
1524 +
    elems: *[*ast::Node],
1517 1525
    ty: resolver::Type,
1518 1526
    dataPrefix: *[u8],
1519 1527
    b: &mut DataValueBuilder
1520 1528
) throws (LowerError) {
1521 1529
    let case resolver::Type::Array(arrInfo) = ty
1546 1554
        let elemLayout = resolver::getTypeLayout(elemTy);
1547 1555
        dataBuilderPush(b, il::DataValue {
1548 1556
            item: il::DataItem::Undef,
1549 1557
            count: elemLayout.size * length
1550 1558
        });
1551 -
    } else if let val = resolver::constValueEntry(&*self.resolver, repeat.item) {
1559 +
    } else if let val = resolver::constValueEntry(self.resolver, repeat.item) {
1552 1560
        if let case resolver::ConstValue::String(_) = val {
1553 1561
            // A string used as a slice is represented by a three-word slice
1554 1562
            // header, not by the bytes of the string itself.
1555 1563
            for _ in 0..length {
1556 1564
                try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b);
1584 1592
        }
1585 1593
        case resolver::Type::Nominal(resolver::NominalType::Union(_)) => {
1586 1594
            let typeName = recLit.typeName else {
1587 1595
                throw LowerError::ExpectedVariant;
1588 1596
            };
1589 -
            let sym = resolver::nodeData(&*self.resolver, typeName).sym else {
1597 +
            let sym = resolver::nodeData(self.resolver, typeName).sym else {
1590 1598
                throw LowerError::MissingSymbol(typeName);
1591 1599
            };
1592 1600
            try lowerConstUnionVariantInto(self, node, sym, ty, recLit.fields, dataPrefix, b);
1593 1601
        }
1594 1602
        else => throw LowerError::ExpectedRecord,
1596 1604
}
1597 1605
1598 1606
/// Build data values for record constants.
1599 1607
unsafe fn lowerConstRecordCtorInto(
1600 1608
    self: &mut Lowerer,
1601 -
    args: *mut [*ast::Node],
1609 +
    args: *[*ast::Node],
1602 1610
    recInfo: resolver::RecordType,
1603 1611
    dataPrefix: *[u8],
1604 1612
    b: &mut DataValueBuilder
1605 1613
) throws (LowerError) {
1606 1614
    let layout = recInfo.layout;
1623 1631
1624 1632
/// Build data values for a constant union variant value from payload fields/args.
1625 1633
unsafe fn lowerConstUnionVariantInto(
1626 1634
    self: &mut Lowerer,
1627 1635
    node: *ast::Node,
1628 -
    variantSym: *mut resolver::Symbol,
1636 +
    variantSym: *unsafe mut resolver::Symbol,
1629 1637
    ty: resolver::Type,
1630 -
    payloadArgs: *mut [*ast::Node],
1638 +
    payloadArgs: *[*ast::Node],
1631 1639
    dataPrefix: *[u8],
1632 1640
    b: &mut DataValueBuilder
1633 1641
) throws (LowerError) {
1634 1642
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data
1635 1643
        else throw LowerError::UnexpectedNodeValue(node);
1681 1689
}
1682 1690
1683 1691
/// Find an existing string data entry with matching content.
1684 1692
// TODO: Optimize with hash table or remove?
1685 1693
fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] {
1686 -
    for d in self.data {
1694 +
    for d in &self.data[..] {
1687 1695
        if d.values.len == 1 {
1688 1696
            if let case il::DataItem::Str(existing) = d.values[0].item {
1689 1697
                if mem::eq(existing, s) {
1690 1698
                    return d.name;
1691 1699
                }
1708 1716
        set totalLen += segment.len;
1709 1717
    }
1710 1718
    if segments.len > 1 {
1711 1719
        set totalLen += segments.len - 1;
1712 1720
    }
1713 -
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1721 +
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1714 1722
    let mut pos: u32 = 0;
1715 1723
1716 1724
    for segment, i in segments {
1717 1725
        set pos += try! mem::copy(&mut buf[pos..], segment);
1718 1726
        if i + 1 <> segments.len {
1732 1740
    count: u32,
1733 1741
    namespace: *[u8]
1734 1742
) -> *[u8] throws (LowerError) {
1735 1743
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1736 1744
    let start = fmt::formatU32(count, &mut digits[..]);
1737 -
    let suffix = try! alloc::allocSlice(&mut *self.arena, 1, 1, digits.len - start) as *mut [u8];
1745 +
    let suffix = try! alloc::allocSlice(self.arena, 1, 1, digits.len - start) as *mut [u8];
1738 1746
    try! mem::copy(suffix, &digits[start..]);
1739 1747
    let segments = [prefix, namespace, suffix];
1740 1748
1741 1749
    return try buildSegmentedName(self, &segments[..]);
1742 1750
}
1772 1780
) -> *[u8] throws (LowerError) {
1773 1781
    if let existing = findStringData(self, s) {
1774 1782
        return existing;
1775 1783
    }
1776 1784
    let values = try! alloc::allocSlice(
1777 -
        &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1
1785 +
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1
1778 1786
    ) as *mut [il::DataValue];
1779 1787
1780 1788
    set values[0] = il::DataValue {
1781 1789
        item: il::DataItem::Str(s),
1782 1790
        count: 1
1838 1846
}
1839 1847
1840 1848
/// Find an existing read-only slice data entry with matching values.
1841 1849
// TODO: Optimize with hash table or remove?
1842 1850
fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1843 -
    for d in self.data {
1851 +
    for d in &self.data[..] {
1844 1852
        if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) {
1845 1853
            return d.name;
1846 1854
        }
1847 1855
    }
1848 1856
    return nil;
1876 1884
    let elemLayout = resolver::getTypeLayout(*elemTy);
1877 1885
    let size = elemLayout.size * length;
1878 1886
    let mut dataName: *[u8] = undefined;
1879 1887
    let mut found: ?*[u8] = nil;
1880 1888
    if readOnly {
1881 -
        set found = findConstData(&mut *self.low, values, alignment);
1889 +
        set found = findConstData(self.low, values, alignment);
1882 1890
    }
1883 1891
    if let name = found {
1884 1892
        set dataName = name;
1885 1893
    } else {
1886 1894
        set dataName = try nextDataName(self);
1906 1914
/// Generate a unique data name for inline literals, eg. `fnName$literal$N`.
1907 1915
unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) {
1908 1916
    let counter = self.dataCounter;
1909 1917
    set self.dataCounter += 1;
1910 1918
    let fnName = self.fnName;
1911 -
    return try nextDeclDataName(&mut *self.low, fnName, counter, "literal");
1919 +
    return try nextDeclDataName(self.low, fnName, counter, "literal");
1912 1920
}
1913 1921
1914 1922
/// Assign a unique function-local data symbol name.
1915 1923
unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
1916 -
    let sym = resolver::nodeData(&*self.low.resolver, node).sym
1924 +
    let sym = resolver::nodeData(self.low.resolver, node).sym
1917 1925
        else throw LowerError::MissingSymbol(node);
1918 1926
1919 1927
    let prefix = self.fnName;
1920 1928
    let segments = [prefix, "nominal", sym.name];
1921 -
    let name = try buildSegmentedName(&mut *self.low, &segments[..]);
1929 +
    let name = try buildSegmentedName(self.low, &segments[..]);
1922 1930
1923 1931
    set sym.name = name;
1924 1932
}
1925 1933
1926 1934
/// Get the next available SSA register.
1930 1938
    return reg;
1931 1939
}
1932 1940
1933 1941
/// Look up the resolved type of an AST node, or throw `MissingType`.
1934 1942
unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
1935 -
    let ty = resolver::typeFor(&*self.low.resolver, node)
1943 +
    let ty = resolver::typeFor(self.low.resolver, node)
1936 1944
        else throw LowerError::MissingType(node);
1937 1945
    return ty;
1938 1946
}
1939 1947
1940 1948
/// Look up the symbol for an AST node, or throw `MissingSymbol`.
1941 -
unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) {
1942 -
    let sym = resolver::nodeData(&*self.low.resolver, node).sym
1949 +
unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) {
1950 +
    let sym = resolver::nodeData(self.low.resolver, node).sym
1943 1951
        else throw LowerError::MissingSymbol(node);
1944 1952
    return sym;
1945 1953
}
1946 1954
1947 1955
/// Remove the last block parameter and its associated variable.
1961 1969
/// Rewrite cached SSA values for a variable across all blocks, and also
1962 1970
/// rewrite any terminator arguments that reference the provisional register.
1963 1971
/// The latter is necessary because recursive SSA resolution may have already
1964 1972
/// patched terminator arguments with the provisional value before it was
1965 1973
/// found to be trivial.
1966 -
fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) {
1974 +
unsafe fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) {
1967 1975
    for i in 0..self.blockData.len {
1968 1976
        let blk = getBlockMut(self, BlockId(i));
1969 1977
        if blk.vars[*v] == from {
1970 1978
            set blk.vars[*v] = to;
1971 1979
        }
1989 1997
        }
1990 1998
    }
1991 1999
}
1992 2000
1993 2001
/// Replace all occurrences of `from` with `to` in an args slice.
1994 -
fn rewriteValInSlice(args: *mut [il::Val], from: il::Val, to: il::Val) {
2002 +
unsafe fn rewriteValInSlice(args: *unsafe mut [il::Val], from: il::Val, to: il::Val) {
1995 2003
    for i in 0..args.len {
1996 2004
        if args[i] == from {
1997 2005
            set args[i] = to;
1998 2006
        }
1999 2007
    }
2021 2029
/// Returns a [`BlockId`] that can be used for jumps and branches. The block must
2022 2030
/// be switched to via [`switchToBlock`] before instructions can be emitted.
2023 2031
unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) {
2024 2032
    let label = try nextLabel(self, labelBase);
2025 2033
    let id = BlockId(self.blockData.len);
2026 -
    let varCount = self.fnType.localCount;
2027 -
    let vars = try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val];
2034 +
    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];
2028 2036
2029 2037
    for i in 0..varCount {
2030 2038
        set vars[i] = nil;
2031 2039
    }
2032 2040
    self.blockData.append(BlockData {
2088 2096
    try sealBlock(self, block);
2089 2097
    switchToBlock(self, block);
2090 2098
}
2091 2099
2092 2100
/// Get block data by block id.
2093 -
fn getBlock(self: &FnLowerer, block: BlockId) -> *BlockData {
2101 +
unsafe fn getBlock(self: &FnLowerer, block: BlockId) -> *unsafe BlockData {
2094 2102
    return &self.blockData[*block];
2095 2103
}
2096 2104
2097 2105
/// Get mutable block data by block id.
2098 -
fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *mut BlockData {
2106 +
unsafe fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *unsafe mut BlockData {
2099 2107
    return &mut self.blockData[*block];
2100 2108
}
2101 2109
2102 2110
/// Get the current block being built.
2103 2111
fn currentBlock(self: &FnLowerer) -> BlockId {
2262 2270
            let operandTy = scalarComparisonType(leftTy, rightTy);
2263 2271
            let unsigned = isUnsignedType(operandTy);
2264 2272
            if let op = cmpOpFrom(binop.op, unsigned) {
2265 2273
                let a = try lowerExpr(self, binop.left);
2266 2274
                let b = try lowerExpr(self, binop.right);
2267 -
                let typ = ilType(&mut *self.low, operandTy);
2275 +
                let typ = ilType(self.low, operandTy);
2268 2276
2269 2277
                // Swap operands if needed.
2270 2278
                match binop.op {
2271 2279
                    case ast::BinaryOp::Gt => // `a > b` = `b < a`
2272 2280
                        try emitBrCmp(self, op, typ, b, a, thenBlock, elseBlock),
2348 2356
2349 2357
/// Emit a load instruction for a scalar value at `src` plus `offset`.
2350 2358
/// For reading values that may be aggregates, use `emitRead` instead.
2351 2359
unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2352 2360
    let dst = nextReg(self);
2353 -
    let ilTyp = ilType(&mut *self.low, typ);
2361 +
    let ilTyp = ilType(self.low, typ);
2354 2362
2355 2363
    if isSignedType(typ) {
2356 2364
        emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset });
2357 2365
    } else {
2358 2366
        emit(self, il::Instr::Load { typ: ilTyp, dst, src, offset });
2369 2377
    }
2370 2378
    return emitLoad(self, src, offset, typ);
2371 2379
}
2372 2380
2373 2381
/// Emit a copy instruction that loads a data symbol's address into a register.
2374 -
unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2382 +
unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg {
2375 2383
    let dst = nextReg(self);
2376 -
    let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym);
2377 -
    let qualName = qualifyName(&mut *self.low, modId, sym.name);
2384 +
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2385 +
    let qualName = qualifyName(self.low, modId, sym.name);
2378 2386
2379 2387
    emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) });
2380 2388
2381 2389
    return dst;
2382 2390
}
2383 2391
2384 2392
/// Emit a copy instruction that loads a function's address into a register.
2385 -
unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2393 +
unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg {
2386 2394
    let dst = nextReg(self);
2387 -
    let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym);
2388 -
    let qualName = qualifyName(&mut *self.low, modId, sym.name);
2395 +
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2396 +
    let qualName = qualifyName(self.low, modId, sym.name);
2389 2397
2390 2398
    emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) });
2391 2399
2392 2400
    return dst;
2393 2401
}
2443 2451
        }
2444 2452
        case MatchSubjectKind::Union(unionInfo) => {
2445 2453
            assert not isNil;
2446 2454
2447 2455
            let case resolver::NodeExtra::UnionVariant { tag: variantTag, .. } =
2448 -
                resolver::nodeData(&*self.low.resolver, pattern).extra
2456 +
                resolver::nodeData(self.low.resolver, pattern).extra
2449 2457
            else {
2450 2458
                throw LowerError::ExpectedVariant;
2451 2459
            };
2452 2460
            // Void unions are passed by value (the tag itself).
2453 2461
            // Non-void unions are passed by reference (need to load tag).
2545 2553
    }
2546 2554
    try sealBlock(self, target);
2547 2555
}
2548 2556
2549 2557
/// Check if the current block already has a terminator instruction.
2550 -
fn blockHasTerminator(self: &FnLowerer) -> bool {
2558 +
unsafe fn blockHasTerminator(self: &FnLowerer) -> bool {
2551 2559
    let blk = getBlock(self, currentBlock(self));
2552 2560
    if blk.instrs.len == 0 {
2553 2561
        return false;
2554 2562
    }
2555 2563
    match blk.instrs[blk.instrs.len - 1] {
2593 2601
// Control Flow Edge Management //
2594 2602
//////////////////////////////////
2595 2603
2596 2604
/// Add a predecessor edge from `pred` to `target`.
2597 2605
/// Must be called before the target block is sealed. Duplicates are ignored.
2598 -
fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) {
2606 +
unsafe fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) {
2599 2607
    let blk = getBlockMut(self, target);
2600 2608
    assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block";
2601 2609
    let preds = &mut blk.preds;
2602 2610
    for i in 0..preds.len {
2603 2611
        if preds[i] == *pred { // Avoid duplicate predecessor entries.
2606 2614
    }
2607 2615
    preds.append(*pred, self.allocator);
2608 2616
}
2609 2617
2610 2618
/// Finalize all blocks and return the block array.
2611 -
unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *[il::Block] throws (LowerError) {
2619 +
unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *unsafe [il::Block] throws (LowerError) {
2612 2620
    let blockCount = self.blockData.len;
2613 -
    let blocks = try! alloc::allocSlice(
2614 -
        &mut *self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount
2615 -
    ) as *mut [il::Block];
2621 +
    let blocks = try! alloc::allocRawSlice(
2622 +
        self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount
2623 +
    ) as *unsafe mut [il::Block];
2616 2624
2617 2625
    for i in 0..self.blockData.len {
2618 2626
        let data = &self.blockData[i];
2619 2627
2620 2628
        set blocks[i] = il::Block {
2633 2641
// Loop Management //
2634 2642
/////////////////////
2635 2643
2636 2644
/// Enter a loop context for break/continue handling.
2637 2645
/// `continueBlock` is `nil` when the continue target is created lazily.
2638 -
fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) {
2646 +
unsafe fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) {
2639 2647
    assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow";
2640 2648
    let slot = &mut self.loopStack[self.loopDepth];
2641 2649
2642 2650
    set slot.breakTarget = breakBlock;
2643 2651
    set slot.continueTarget = continueBlock;
2649 2657
    assert self.loopDepth <> 0, "exitLoop: loopDepth is zero";
2650 2658
    set self.loopDepth -= 1;
2651 2659
}
2652 2660
2653 2661
/// Get the current loop context.
2654 -
fn currentLoop(self: &mut FnLowerer) -> ?*mut LoopCtx {
2662 +
unsafe fn currentLoop(self: &mut FnLowerer) -> ?*unsafe mut LoopCtx {
2655 2663
    if self.loopDepth == 0 {
2656 2664
        return nil;
2657 2665
    }
2658 2666
    return &mut self.loopStack[self.loopDepth - 1];
2659 2667
}
2670 2678
    set ctx.continueTarget = block;
2671 2679
    return block;
2672 2680
}
2673 2681
2674 2682
/// Allocate a slice of values in the lowering arena.
2675 -
unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) {
2676 -
    return try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val];
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];
2677 2685
}
2678 2686
2679 2687
/// Allocate a single-value slice in the lowering arena.
2680 -
unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) {
2688 +
unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) {
2681 2689
    let args = try allocVals(self, 1);
2682 2690
    set args[0] = val;
2683 2691
    return args;
2684 2692
}
2685 2693
2771 2779
// a block parameter, but defer filling in the terminator arguments. When the
2772 2780
// block is later sealed via [`sealBlock`], all incomplete block params are resolved.
2773 2781
2774 2782
/// Declare a new source-level variable and define its initial value.
2775 2783
/// If called before any block exists (e.g., for parameters), the definition is skipped.
2776 -
fn newVar(
2784 +
unsafe fn newVar(
2777 2785
    self: &mut FnLowerer,
2778 2786
    name: ?*[u8],
2779 2787
    type: il::Type,
2780 2788
    mutable: bool,
2781 2789
    val: il::Val
2792 2800
2793 2801
/// Define (write) a variable. Record the SSA value of a variable in the
2794 2802
/// current block. Called when a variable is assigned or initialized (`let`
2795 2803
/// bindings, assignments, loop updates). When [`useVar`] is later called,
2796 2804
/// it will retrieve this value.
2797 -
fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) {
2805 +
unsafe fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) {
2798 2806
    assert *v < self.vars.len;
2799 2807
    set getBlockMut(self, currentBlock(self)).vars[*v] = val;
2800 2808
}
2801 2809
2802 2810
/// Use (read) the current value of a variable in the current block.
2844 2852
    return try createBlockParam(self, block, v);
2845 2853
}
2846 2854
2847 2855
/// Look up a variable by name in the current scope.
2848 2856
/// Searches from most recently declared to first, enabling shadowing.
2849 -
fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var {
2857 +
unsafe fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var {
2850 2858
    let mut id = self.vars.len;
2851 2859
    while id > 0 {
2852 2860
        set id -= 1;
2853 2861
        if let varName = self.vars[id].name {
2854 2862
            // Names are interned strings, so pointer comparison suffices.
2859 2867
    }
2860 2868
    return nil;
2861 2869
}
2862 2870
2863 2871
/// Look up a local variable bound to an identifier node.
2864 -
fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var {
2872 +
unsafe fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var {
2865 2873
    let case ast::NodeValue::Ident(name) = node.value else {
2866 2874
        return nil;
2867 2875
    };
2868 2876
    return lookupVarByName(self, name);
2869 2877
}
2870 2878
2871 2879
/// Save current lexical variable scope depth.
2872 -
fn enterVarScope(self: &FnLowerer) -> u32 {
2880 +
unsafe fn enterVarScope(self: &FnLowerer) -> u32 {
2873 2881
    return self.vars.len;
2874 2882
}
2875 2883
2876 2884
/// Restore lexical variable scope depth.
2877 2885
unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) {
2878 2886
    set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap);
2879 2887
}
2880 2888
2881 2889
/// Get the metadata for a variable.
2882 -
fn getVar(self: &FnLowerer, v: Var) -> *VarData {
2890 +
unsafe fn getVar(self: &FnLowerer, v: Var) -> *unsafe VarData {
2883 2891
    assert *v < self.vars.len;
2884 2892
    return &self.vars[*v];
2885 2893
}
2886 2894
2887 2895
/// Create a block parameter to merge a variable's value from multiple
3044 3052
        }
3045 3053
    }
3046 3054
}
3047 3055
3048 3056
/// Grow an args array to hold at least the given capacity.
3049 -
unsafe fn growArgs(self: &mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] {
3057 +
unsafe fn growArgs(self: &mut FnLowerer, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] {
3050 3058
    if args.len >= capacity {
3051 3059
        return args;
3052 3060
    }
3053 -
    let newArgs = try! alloc::allocSlice(
3054 -
        &mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3055 -
    ) as *mut [il::Val];
3061 +
    let newArgs = try! alloc::allocRawSlice(
3062 +
        self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3063 +
    ) as *unsafe mut [il::Val];
3056 3064
3057 3065
    for arg, i in args {
3058 3066
        set newArgs[i] = arg;
3059 3067
    }
3060 3068
    for i in args.len..capacity {
3077 3085
/// Lower function parameters. Declares variables for each parameter.
3078 3086
/// When a receiver name is passed, we're handling a trait method.
3079 3087
unsafe fn lowerParams(
3080 3088
    self: &mut FnLowerer,
3081 3089
    fnType: resolver::FnType,
3082 -
    astParams: *mut [*ast::Node],
3090 +
    astParams: *[*ast::Node],
3083 3091
    receiverName: ?*ast::Node
3084 -
) -> *[il::Param] throws (LowerError) {
3092 +
) -> *unsafe [il::Param] throws (LowerError) {
3085 3093
    let offset: u32 = 1 if self.returnReg <> nil else 0;
3086 3094
    let totalLen = fnType.paramTypes.len as u32 + offset;
3087 3095
    if totalLen == 0 {
3088 3096
        return &[];
3089 3097
    }
3090 3098
    assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS;
3091 3099
3092 -
    let params = try! alloc::allocSlice(
3093 -
        &mut *self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3094 -
    ) as *mut [il::Param];
3100 +
    let params = try! alloc::allocRawSlice(
3101 +
        self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3102 +
    ) as *unsafe mut [il::Param];
3095 3103
3096 3104
    if let reg = self.returnReg {
3097 3105
        set params[0] = il::Param { value: reg, type: il::Type::W64 };
3098 3106
    }
3099 3107
    for i in 0..fnType.paramTypes.len as u32 {
3100 -
        let type = ilType(&mut *self.low, *fnType.paramTypes[i]);
3108 +
        let type = ilType(self.low, *fnType.paramTypes[i]);
3101 3109
        let reg = nextReg(self);
3102 3110
3103 3111
        set params[i + offset] = il::Param { value: reg, type };
3104 3112
3105 3113
        // Declare the parameter variable. For the receiver, the name comes
3141 3149
3142 3150
    let mut bindType = unwrapped.effectiveTy;
3143 3151
    if let case resolver::Type::Optional(inner) = unwrapped.effectiveTy {
3144 3152
        set bindType = *inner;
3145 3153
    }
3146 -
    let ilType = ilType(&mut *self.low, unwrapped.effectiveTy);
3154 +
    let ilType = ilType(self.low, unwrapped.effectiveTy);
3147 3155
    let kind = matchSubjectKind(unwrapped.effectiveTy);
3148 3156
3149 3157
    return MatchSubject { val, type: unwrapped.effectiveTy, ilType, bindType, kind, by: unwrapped.by };
3150 3158
}
3151 3159
3245 3253
        case resolver::MatchBy::Value =>
3246 3254
            set payload = tvalPayloadVal(self, base, bindType, valOffset),
3247 3255
        case resolver::MatchBy::Ref, resolver::MatchBy::MutRef =>
3248 3256
            set payload = tvalPayloadAddr(self, base, valOffset),
3249 3257
    };
3250 -
    return newVar(self, name, ilType(&mut *self.low, bindType), mutable, payload);
3258 +
    return newVar(self, name, ilType(self.low, bindType), mutable, payload);
3251 3259
}
3252 3260
3253 3261
/// Bind an identifier from a matched subject.
3254 3262
unsafe fn bindMatchVariable(
3255 3263
    self: &mut FnLowerer,
3266 3274
    if let case MatchSubjectKind::OptionalAggregate = subject.kind {
3267 3275
        let valOffset = resolver::getOptionalValOffset(subject.bindType) as i32;
3268 3276
        return try bindPayloadVariable(self, name, subject.val, subject.bindType, subject.by, valOffset, mutable);
3269 3277
    }
3270 3278
    // Declare the variable in the current block's scope.
3271 -
    return newVar(self, name, ilType(&mut *self.low, subject.bindType), mutable, subject.val);
3279 +
    return newVar(self, name, ilType(self.low, subject.bindType), mutable, subject.val);
3272 3280
}
3273 3281
3274 3282
/// Bind variables from inside case patterns (union variants, records, slices).
3275 3283
/// `failBlock` is passed when nested patterns may require additional tests
3276 3284
/// that branch on mismatch (e.g. nested union variant tests).
3277 3285
unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) {
3278 3286
    for pattern in patterns {
3279 3287
3280 3288
        // Handle simple variant patterns like `Variant(x)`.
3281 -
        if let arg = resolver::variantPatternBinding(&*self.low.resolver, pattern) {
3289 +
        if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) {
3282 3290
            let case MatchSubjectKind::Union(unionInfo) = subject.kind
3283 3291
                else panic "bindPatternVariables: expected union subject";
3284 3292
            let valOffset = unionInfo.valOffset as i32;
3285 3293
3286 3294
            // Get the actual field type from the variant's record info.
3287 3295
            // This preserves the original data layout type (e.g. `*T`) even when
3288 3296
            // the resolver resolved the pattern against a dereferenced type (`T`).
3289 -
            let variantExtra = resolver::nodeData(&*self.low.resolver, pattern).extra;
3297 +
            let variantExtra = resolver::nodeData(self.low.resolver, pattern).extra;
3290 3298
            let case resolver::NodeExtra::UnionVariant { ordinal, .. } = variantExtra
3291 3299
                else panic "bindPatternVariables: expected variant extra";
3292 3300
            let payloadType = unionInfo.variants[ordinal].valueType;
3293 3301
            let payloadRec = resolver::getRecord(payloadType)
3294 3302
                else panic "bindPatternVariables: expected record payload";
3329 3337
/// Each element is either bound as a variable, skipped (placeholder), or
3330 3338
/// tested against the subject element, branching to `failBlock` on mismatch.
3331 3339
unsafe fn bindArrayPatternElements(
3332 3340
    self: &mut FnLowerer,
3333 3341
    subject: &MatchSubject,
3334 -
    items: *mut [*ast::Node],
3342 +
    items: *[*ast::Node],
3335 3343
    failBlock: BlockId
3336 3344
) throws (LowerError) {
3337 3345
    let case resolver::Type::Array(arrInfo) = subject.type
3338 3346
        else throw LowerError::ExpectedSliceOrArray;
3339 3347
3373 3381
    let case MatchSubjectKind::Union(unionInfo) = subject.kind
3374 3382
        else panic "bindRecordPatternFields: expected union subject";
3375 3383
3376 3384
    // Get the variant index from the pattern node.
3377 3385
    let case resolver::NodeExtra::UnionVariant { ordinal: variantOrdinal, .. } =
3378 -
        resolver::nodeData(&*self.low.resolver, pattern).extra
3386 +
        resolver::nodeData(self.low.resolver, pattern).extra
3379 3387
    else throw LowerError::MissingMetadata;
3380 3388
3381 3389
    // Get the record type from the variant's payload type.
3382 3390
    let payloadType = unionInfo.variants[variantOrdinal].valueType;
3383 3391
    let recInfo = resolver::getRecord(payloadType)
3404 3412
    match binding.value {
3405 3413
        case ast::NodeValue::Ident(name) => {
3406 3414
            let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType)
3407 3415
                if matchBy == resolver::MatchBy::Value
3408 3416
                else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset));
3409 -
            newVar(self, name, ilType(&mut *self.low, fieldInfo.fieldType), false, val);
3417 +
            newVar(self, name, ilType(self.low, fieldInfo.fieldType), false, val);
3410 3418
        }
3411 3419
        case ast::NodeValue::Placeholder => {}
3412 3420
        case ast::NodeValue::RecordLit(lit) => {
3413 3421
            // Check if this record literal is a union variant pattern.
3414 3422
            if let keyNode = resolver::patternVariantKeyNode(binding) {
3415 -
                if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(&*self.low.resolver, keyNode).extra {
3423 +
                if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(self.low.resolver, keyNode).extra {
3416 3424
                    try emitNestedFieldTest(self, binding, base, fieldInfo, matchBy, failBlock);
3417 3425
                    return;
3418 3426
                }
3419 3427
            }
3420 3428
            // Plain nested record destructuring pattern.
3464 3472
            set derefBase = ptrReg;
3465 3473
            set fieldType = *target;
3466 3474
        }
3467 3475
    }
3468 3476
    // Build a MatchSubject for the nested field.
3469 -
    let ilTy = ilType(&mut *self.low, fieldType);
3477 +
    let ilTy = ilType(self.low, fieldType);
3470 3478
    let kind = matchSubjectKind(fieldType);
3471 3479
3472 3480
    // Determine the subject value.
3473 3481
    let mut val: il::Val = undefined;
3474 3482
    if let reg = derefBase {
3511 3519
) throws (LowerError) {
3512 3520
    for fieldNode in lit.fields {
3513 3521
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
3514 3522
            throw LowerError::UnexpectedNodeValue(fieldNode);
3515 3523
        };
3516 -
        let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode)
3524 +
        let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode)
3517 3525
            else throw LowerError::MissingMetadata;
3518 3526
        if fieldIdx >= recInfo.fields.len {
3519 3527
            throw LowerError::MissingMetadata;
3520 3528
        }
3521 3529
        let fieldInfo = recInfo.fields[fieldIdx];
3523 3531
        try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock);
3524 3532
    }
3525 3533
}
3526 3534
3527 3535
/// Lower function body to a list of basic blocks.
3528 -
unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) {
3536 +
unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) {
3529 3537
    // Create and switch to entry block.
3530 3538
    let entry = try createBlock(self, "entry");
3531 3539
    set self.entryBlock = entry;
3532 3540
    switchToBlock(self, entry);
3533 3541
3556 3564
    }
3557 3565
    return try finalizeBlocks(self);
3558 3566
}
3559 3567
3560 3568
/// Lower a scalar match as a switch instruction.
3561 -
unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *mut [*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) {
3569 +
unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) {
3562 3570
    let mut blocks: [BlockId; 32] = undefined;
3563 -
    let mut cases: *mut [il::SwitchCase] = &mut [];
3571 +
    let mut cases: *unsafe mut [il::SwitchCase] = &mut [];
3564 3572
    let mut defaultIdx: u32 = 0;
3565 3573
    let entry = currentBlock(self);
3566 3574
3567 3575
    for p, i in prongs {
3568 3576
        let case ast::NodeValue::MatchProng(prong) = p.value
3574 3582
                set defaultIdx = i;
3575 3583
            }
3576 3584
            case ast::ProngArm::Case(pats) => {
3577 3585
                set blocks[i] = try createBlock(self, "case");
3578 3586
                for pat in pats {
3579 -
                    let cv = resolver::constValueEntry(&*self.low.resolver, pat)
3587 +
                    let cv = resolver::constValueEntry(self.low.resolver, pat)
3580 3588
                        else throw LowerError::MissingConst(pat);
3581 3589
3582 3590
                    cases.append(il::SwitchCase {
3583 3591
                        value: constToScalar(cv),
3584 3592
                        target: *blocks[i],
3669 3677
    let subject = try lowerMatchSubject(self, m.subject);
3670 3678
    // Merge block created lazily if any arm needs it (i.e., doesn't diverge).
3671 3679
    let mut mergeBlock: ?BlockId = nil;
3672 3680
3673 3681
    // Use `switch` instruction for matches with constant patterns.
3674 -
    if resolver::isMatchConst(&*self.low.resolver, node) {
3682 +
    if resolver::isMatchConst(self.low.resolver, node) {
3675 3683
        try lowerMatchSwitch(self, prongs, &subject, &mut mergeBlock);
3676 3684
        return;
3677 3685
    }
3678 3686
    // Fallback: chained branches.
3679 3687
    let firstArm = try createBlock(self, "arm");
3685 3693
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
3686 3694
            else panic "lowerMatch: expected match prong";
3687 3695
3688 3696
        let isLastArm = i + 1 == prongs.len;
3689 3697
        let hasGuard = prong.guard <> nil;
3690 -
        let catchAll = resolver::isProngCatchAll(&*self.low.resolver, prongNode);
3698 +
        let catchAll = resolver::isProngCatchAll(self.low.resolver, prongNode);
3691 3699
3692 3700
        // Entry block: guard block if present, otherwise the body block.
3693 3701
        // The guard block must be created before the body block so that
3694 3702
        // block indices are in reverse post-order (RPO), which the register
3695 3703
        // allocator requires.
3712 3720
        // Emit pattern test: branch to entry block on match, next arm on fail.
3713 3721
        match prong.arm {
3714 3722
            case ast::ProngArm::Binding(_) if not catchAll =>
3715 3723
                try emitBindingTest(self, &subject, entryBlock, nextArm),
3716 3724
            case ast::ProngArm::Case(patterns) if not catchAll =>
3717 -
                try emitPatternMatches(self, &subject, &patterns[..], entryBlock, nextArm),
3725 +
                try emitPatternMatches(self, &subject, patterns, entryBlock, nextArm),
3718 3726
            else =>
3719 3727
                try emitJmp(self, entryBlock),
3720 3728
        }
3721 3729
        // Switch to entry block, where any variable bindings need to be created.
3722 3730
        try switchToAndSeal(self, entryBlock);
3727 3735
        // block.
3728 3736
        match prong.arm {
3729 3737
            case ast::ProngArm::Binding(pat) =>
3730 3738
                try bindMatchVariable(self, &subject, pat, false),
3731 3739
            case ast::ProngArm::Case(patterns) =>
3732 -
                try bindPatternVariables(self, &subject, &patterns[..], nextArm),
3740 +
                try bindPatternVariables(self, &subject, patterns, nextArm),
3733 3741
            else => {},
3734 3742
        }
3735 3743
3736 3744
        // Evaluate guard if present; can still fail to next arm.
3737 3745
        if let g = prong.guard {
3947 3955
            try lowerLet(self, node, l);
3948 3956
        }
3949 3957
        case ast::NodeValue::ConstDecl(decl) => {
3950 3958
            // Local constants lower to data declarations and emit no runtime code.
3951 3959
            try registerLocalDataDeclName(self, node);
3952 -
            try lowerDataDecl(&mut *self.low, node, decl.value, true);
3960 +
            try lowerDataDecl(self.low, node, decl.value, true);
3953 3961
        }
3954 3962
        case ast::NodeValue::StaticDecl(decl) => {
3955 3963
            // Local statics lower to data declarations and emit no runtime code.
3956 3964
            try registerLocalDataDeclName(self, node);
3957 -
            try lowerDataDecl(&mut *self.low, node, decl.value, false);
3965 +
            try lowerDataDecl(self.low, node, decl.value, false);
3958 3966
        }
3959 3967
        case ast::NodeValue::If(i) => {
3960 3968
            try lowerIf(self, i);
3961 3969
        }
3962 3970
        case ast::NodeValue::IfLet(i) => {
4039 4047
///////////////////////////////////////
4040 4048
// Record and Aggregate Type Helpers //
4041 4049
///////////////////////////////////////
4042 4050
4043 4051
/// Extract the nominal record info from a resolver type.
4044 -
fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType {
4052 +
unsafe fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType {
4045 4053
    let case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) = typ
4046 4054
        else return nil;
4047 4055
4048 4056
    return recInfo;
4049 4057
}
4050 4058
4051 4059
/// Extract the nominal union info from a resolver type.
4052 -
fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType {
4060 +
unsafe fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType {
4053 4061
    let case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) = typ
4054 4062
        else return nil;
4055 4063
4056 4064
    return unionInfo;
4057 4065
}
4060 4068
/// the resolver. `lowerExpr` already materializes the coercion in the
4061 4069
/// IL value, so the lowerer must use the post-coercion type when
4062 4070
/// choosing how to compare or store that value.
4063 4071
unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
4064 4072
    let ty = try typeOf(self, node);
4065 -
    if let coerce = resolver::coercionFor(&*self.low.resolver, node) {
4073 +
    if let coerce = resolver::coercionFor(self.low.resolver, node) {
4066 4074
        if let case resolver::Coercion::OptionalLift(optTy) = coerce {
4067 4075
            return optTy;
4068 4076
        }
4069 4077
    }
4070 4078
    return ty;
4071 4079
}
4072 4080
4073 4081
/// Check if a resolver type lowers to an aggregate in memory.
4074 -
fn isAggregateType(typ: resolver::Type) -> bool {
4082 +
unsafe fn isAggregateType(typ: resolver::Type) -> bool {
4075 4083
    match typ {
4076 4084
        case resolver::Type::Slice { .. },
4077 4085
             resolver::Type::TraitObject { .. } => return true,
4078 4086
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4079 4087
            // Optional pointers are scalar due to NPO.
4093 4101
    }
4094 4102
}
4095 4103
4096 4104
/// Check if a resolver type is a small aggregate that can be
4097 4105
/// passed or returned by value in a register.
4098 -
fn isSmallAggregate(typ: resolver::Type) -> bool {
4106 +
unsafe fn isSmallAggregate(typ: resolver::Type) -> bool {
4099 4107
    match typ {
4100 4108
        case resolver::Type::Nominal(_) => {
4101 4109
            if resolver::isVoidUnion(typ) {
4102 4110
                return false;
4103 4111
            }
4111 4119
/// Whether a function needs a hidden return parameter.
4112 4120
///
4113 4121
/// This is the case for throwing functions, which return a result aggregate,
4114 4122
/// and for functions returning large aggregates that cannot be passed in
4115 4123
/// registers.
4116 -
fn requiresReturnParam(fnType: *resolver::FnType) -> bool {
4124 +
unsafe fn requiresReturnParam(fnType: *resolver::FnType) -> bool {
4117 4125
    return fnType.throwList.len > 0
4118 4126
        or (isAggregateType(*fnType.returnType)
4119 4127
        and not isSmallAggregate(*fnType.returnType));
4120 4128
}
4121 4129
4122 4130
/// Check if a node is a void union variant literal (e.g. `Color::Red`).
4123 4131
/// If so, returns the variant's tag index. This enables optimized comparisons
4124 4132
/// that only check the tag instead of doing full aggregate comparison.
4125 -
fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 {
4133 +
unsafe fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 {
4126 4134
    let data = resolver::nodeData(res, node);
4127 4135
    let sym = data.sym else {
4128 4136
        return nil;
4129 4137
    };
4130 4138
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else {
4167 4175
        let layout = resolver::getTypeLayout(typ);
4168 4176
4169 4177
        emit(self, il::Instr::Blit { dst, src, size: il::Val::Imm(layout.size as i64) });
4170 4178
    } else {
4171 4179
        emit(self, il::Instr::Store {
4172 -
            typ: ilType(&mut *self.low, typ),
4180 +
            typ: ilType(self.low, typ),
4173 4181
            src,
4174 4182
            dst: base,
4175 4183
            offset,
4176 4184
        });
4177 4185
    }
4298 4306
4299 4307
/// Build a trait object fat pointer from a data pointer and a v-table.
4300 4308
unsafe fn buildTraitObject(
4301 4309
    self: &mut FnLowerer,
4302 4310
    dataVal: il::Val,
4303 -
    traitInfo: *resolver::TraitType,
4311 +
    traitInfo: *unsafe resolver::TraitType,
4304 4312
    inst: &resolver::InstanceEntry
4305 4313
) -> il::Val throws (LowerError) {
4306 -
    let vName = vtableName(&mut *self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name);
4314 +
    let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name);
4307 4315
4308 4316
    // Reserve space for the trait object on the stack.
4309 4317
    let slot = emitReserveLayout(self, resolver::Layout {
4310 4318
        size: resolver::PTR_SIZE * 2,
4311 4319
        alignment: resolver::PTR_SIZE,
4438 4446
    }
4439 4447
    // For scalar types, load and compare directly.
4440 4448
    let a = emitLoad(self, left, offset, fieldType);
4441 4449
    let b = emitLoad(self, right, offset, fieldType);
4442 4450
    let dst = nextReg(self);
4443 -
    emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(&mut *self.low, fieldType), dst, a, b });
4451 +
    emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(self.low, fieldType), dst, a, b });
4444 4452
4445 4453
    return il::Val::Reg(dst);
4446 4454
}
4447 4455
4448 4456
/// Compare two record values for equality.
4630 4638
    addPredecessor(self, mergeBlock, currentBlock(self));
4631 4639
4632 4640
    // Create comparison blocks for each non-void variant and build switch cases.
4633 4641
    // Void variants jump directly to merge with `true`.
4634 4642
    let trueArgs = try allocVal(self, il::Val::Imm(1));
4635 -
    let cases = try! alloc::allocSlice(
4636 -
        &mut *self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4637 -
    ) as *mut [il::SwitchCase];
4643 +
    let cases = try! alloc::allocRawSlice(
4644 +
        self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4645 +
    ) as *unsafe mut [il::SwitchCase];
4638 4646
4639 4647
    let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined;
4640 4648
    for variant, i in unionInfo.variants {
4641 4649
        if variant.valueType == resolver::Type::Void {
4642 4650
            set cases[i] = il::SwitchCase {
4793 4801
/// The `offset` is added to each field's offset when storing.
4794 4802
unsafe fn lowerRecordFields(
4795 4803
    self: &mut FnLowerer,
4796 4804
    dst: il::Reg,
4797 4805
    recInfo: &resolver::RecordType,
4798 -
    fields: *mut [*ast::Node],
4806 +
    fields: *[*ast::Node],
4799 4807
    offset: i32
4800 4808
) throws (LowerError) {
4801 4809
    for fieldNode, i in fields {
4802 4810
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
4803 4811
            throw LowerError::UnexpectedNodeValue(fieldNode);
4804 4812
        };
4805 4813
        let mut fieldIdx: u32 = i;
4806 4814
        if recInfo.labeled {
4807 -
            let idx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) else {
4815 +
            let idx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) else {
4808 4816
                throw LowerError::MissingMetadata;
4809 4817
            };
4810 4818
            set fieldIdx = idx;
4811 4819
        }
4812 4820
        // Skip `undefined` fields, they need no initialization.
4819 4827
        }
4820 4828
    }
4821 4829
}
4822 4830
4823 4831
/// Lower an unlabeled record constructor call.
4824 -
unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
4832 +
unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) {
4825 4833
    let case resolver::NominalType::Record(recInfo) = *nominal else {
4826 4834
        throw LowerError::ExpectedRecord;
4827 4835
    };
4828 4836
    let typ = resolver::Type::Nominal(nominal);
4829 4837
    let dst = try emitReserve(self, typ);
4838 4846
    }
4839 4847
    return il::Val::Reg(dst);
4840 4848
}
4841 4849
4842 4850
/// Lower an array literal expression like `[1, 2, 3]`.
4843 -
unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val
4851 +
unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *[*ast::Node]) -> il::Val
4844 4852
    throws (LowerError)
4845 4853
{
4846 4854
    let typ = try typeOf(self, node);
4847 4855
    let case resolver::Type::Array(arrInfo) = typ else {
4848 4856
        throw LowerError::ExpectedArray;
4885 4893
    }
4886 4894
    return il::Val::Reg(dst);
4887 4895
}
4888 4896
4889 4897
/// Lower a union constructor call like `Union::Variant(...)`.
4890 -
unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val
4898 +
unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val
4891 4899
    throws (LowerError)
4892 4900
{
4893 4901
    let unionTy = try typeOf(self, node);
4894 4902
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else {
4895 4903
        throw LowerError::ExpectedVariant;
4910 4918
4911 4919
/// Lower a field access into a pointer to the field.
4912 4920
unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) {
4913 4921
    let parentTy = try typeOf(self, access.parent);
4914 4922
    let subjectTy = resolver::autoDeref(parentTy);
4915 -
    let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, access.child) else {
4923 +
    let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else {
4916 4924
        throw LowerError::MissingMetadata;
4917 4925
    };
4918 4926
    let fieldInfo = resolver::getRecordField(subjectTy, fieldIdx) else {
4919 4927
        throw LowerError::FieldNotFound;
4920 4928
    };
5007 5015
    self: &mut FnLowerer,
5008 5016
    container: *ast::Node,
5009 5017
    range: ast::Range,
5010 5018
    sliceNode: *ast::Node
5011 5019
) -> il::Val throws (LowerError) {
5012 -
    let info = resolver::sliceRangeInfoFor(&*self.low.resolver, sliceNode) else {
5020 +
    let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else {
5013 5021
        throw LowerError::MissingMetadata;
5014 5022
    };
5015 5023
    let r = try resolveSliceRangePtr(self, container, range, info);
5016 5024
    return try buildSliceValue(
5017 5025
        self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count
5051 5059
                // Already address-taken; return existing stack pointer.
5052 5060
                return val;
5053 5061
            }
5054 5062
            // Materialize a stack slot using the declaration's resolved
5055 5063
            // layout so `align(N)` on locals is honored.
5056 -
            let layout = resolver::getLayout(&*self.low.resolver, addr.target, typ);
5064 +
            let layout = resolver::getLayout(self.low.resolver, addr.target, typ);
5057 5065
            let slot = emitReserveLayout(self, layout);
5058 5066
            try emitStore(self, slot, 0, typ, val);
5059 5067
            let stackVal = il::Val::Reg(slot);
5060 5068
5061 5069
            set self.vars[*v].addressTaken = true;
5062 5070
            defVar(self, v, stackVal);
5063 5071
5064 5072
            return stackVal;
5065 5073
        }
5066 5074
        // Fall back to symbol lookup for constants/statics.
5067 -
        if let sym = resolver::nodeData(&*self.low.resolver, addr.target).sym {
5075 +
        if let sym = resolver::nodeData(self.low.resolver, addr.target).sym {
5068 5076
            return il::Val::Reg(emitDataAddr(self, sym));
5069 5077
        } else {
5070 5078
            throw LowerError::MissingSymbol(node);
5071 5079
        }
5072 5080
    }
5104 5112
    if length == 0 {
5105 5113
        return try buildSliceValue(
5106 5114
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
5107 5115
        );
5108 5116
    }
5109 -
    if resolver::isConstExpr(&*self.low.resolver, arrayNode) {
5117 +
    if resolver::isConstExpr(self.low.resolver, arrayNode) {
5110 5118
        let fnName = self.fnName;
5111 5119
        let mut b = dataBuilder(self.low.allocator);
5112 5120
        match arrayNode.value {
5113 5121
            case ast::NodeValue::ArrayLit(elements) =>
5114 -
                try lowerConstArrayLitInto(&mut *self.low, elements, arrayTy, fnName, &mut b),
5122 +
                try lowerConstArrayLitInto(self.low, elements, arrayTy, fnName, &mut b),
5115 5123
            case ast::NodeValue::ArrayRepeatLit(repeat) =>
5116 -
                try lowerConstArrayRepeatInto(&mut *self.low, repeat, arrayTy, fnName, &mut b),
5124 +
                try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, fnName, &mut b),
5117 5125
            else => throw LowerError::UnexpectedNodeValue(arrayNode),
5118 5126
        }
5119 -
        let result = dataBuilderFinish(&b);
5127 +
        let result = dataBuilderFinish(b);
5120 5128
        let alignment = resolver::getTypeLayout(*item).alignment;
5121 5129
        return try lowerConstDataAsSlice(
5122 5130
            self, &result, alignment, not mutable,
5123 5131
            item, mutable, length
5124 5132
        );
5156 5164
        case resolver::Type::Array(arrInfo) => {
5157 5165
            set elemType = *arrInfo.item;
5158 5166
            // Runtime safety check: index must be strictly less than array length.
5159 5167
            // Skip when the index is a compile-time constant, since we check
5160 5168
            // that in the resolver.
5161 -
            if not resolver::isConstExpr(&*self.low.resolver, index) {
5169 +
            if not resolver::isConstExpr(self.low.resolver, index) {
5162 5170
                let arrLen = il::Val::Imm(arrInfo.length as i64);
5163 5171
                try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen);
5164 5172
            }
5165 5173
        }
5166 5174
        else => throw LowerError::ExpectedSliceOrArray,
5204 5212
    }
5205 5213
    let case ast::NodeValue::Ident(name) = l.ident.value else {
5206 5214
        throw LowerError::ExpectedIdentifier;
5207 5215
    };
5208 5216
    let typ = try typeOf(self, l.value);
5209 -
    let ilType = ilType(&mut *self.low, typ);
5217 +
    let ilType = ilType(self.low, typ);
5210 5218
    let mut varVal = val;
5211 5219
5212 5220
    // Aggregates with persistent storage need a local copy to avoid aliasing.
5213 5221
    // Temporaries such as literals or call results can be adopted directly.
5214 5222
    // This is because aggregates are represented as memory addresses
5218 5226
    // Void variant literals (e.g. `Option::None`) use scope access syntax and
5219 5227
    // are flagged as place expressions, but they are freshly constructed
5220 5228
    // temporaries with no persistent storage.
5221 5229
    if isAggregateType(typ) and
5222 5230
        ast::isPlaceExpr(l.value) and
5223 -
        voidVariantIndex(&*self.low.resolver, l.value) == nil {
5231 +
        voidVariantIndex(self.low.resolver, l.value) == nil {
5224 5232
        set varVal = try emitStackVal(self, typ, val);
5225 5233
    }
5226 5234
5227 5235
    // If the resolver determined that this variable's address is taken
5228 5236
    // anywhere in the function, allocate a stack slot immediately so the
5229 5237
    // SSA value is always a pointer. This avoids mixing integer and pointer
5230 5238
    // values in loop phis when `&var` or `&mut var` appears inside a loop.
5231 5239
    if not isAggregateType(typ) {
5232 -
        if let sym = resolver::nodeData(&*self.low.resolver, node).sym {
5240 +
        if let sym = resolver::nodeData(self.low.resolver, node).sym {
5233 5241
            if let case resolver::SymbolData::Value { addressTaken, .. } = sym.data; addressTaken {
5234 -
                let layout = resolver::getLayout(&*self.low.resolver, node, typ);
5242 +
                let layout = resolver::getLayout(self.low.resolver, node, typ);
5235 5243
                let slot = emitReserveLayout(self, layout);
5236 5244
                try emitStore(self, slot, 0, typ, varVal);
5237 5245
5238 5246
                let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot));
5239 5247
                set self.vars[*v].addressTaken = true;
5356 5364
    let current = emitRead(self, place.base, place.offset, place.fieldType);
5357 5365
    let left = try applyCoercion(self, binop.left, current);
5358 5366
    let right = try lowerExpr(self, binop.right);
5359 5367
    let exprType = try typeOf(self, expr);
5360 5368
    let result = emitScalarBinOp(
5361 -
        self, binop.op, ilType(&mut *self.low, exprType), left, right, isUnsignedType(exprType)
5369 +
        self, binop.op, ilType(self.low, exprType), left, right, isUnsignedType(exprType)
5362 5370
    );
5363 5371
    let assigned = try applyCoercion(self, expr, result);
5364 5372
    try emitStore(self, place.base, place.offset, place.fieldType, assigned);
5365 5373
5366 5374
    return true;
5367 5375
}
5368 5376
5369 5377
/// Lower an assignment statement.
5370 5378
unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) {
5371 5379
    // Slice assignment: `slice[range] = value`.
5372 -
    if let info = resolver::sliceRangeInfoFor(&*self.low.resolver, node) {
5380 +
    if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) {
5373 5381
        let case ast::NodeValue::Subscript { container, index } = a.left.value
5374 5382
            else panic "lowerAssign: slice assign without subscript";
5375 5383
        let case ast::NodeValue::Range(range) = index.value
5376 5384
            else panic "lowerAssign: slice assign without range";
5377 5385
        try lowerSliceAssign(self, a.right, container, range, info);
5647 5655
}
5648 5656
5649 5657
/// Lower a `for` loop over a range, array, or slice.
5650 5658
unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) {
5651 5659
    let savedVarsLen = enterVarScope(self);
5652 -
    let info = resolver::forLoopInfoFor(&*self.low.resolver, node) else {
5660 +
    let info = resolver::forLoopInfoFor(self.low.resolver, node) else {
5653 5661
        throw LowerError::MissingMetadata;
5654 5662
    };
5655 5663
    match info {
5656 5664
        case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName } => {
5657 5665
            let endExpr = range.end else {
5660 5668
            let mut startVal = il::Val::Imm(0);
5661 5669
            if let start = range.start {
5662 5670
                set startVal = try lowerExpr(self, start);
5663 5671
            }
5664 5672
            let endVal = try lowerExpr(self, endExpr);
5665 -
            let iterType = ilType(&mut *self.low, *valType);
5673 +
            let iterType = ilType(self.low, *valType);
5666 5674
            let valVar = newVar(self, bindingName, iterType, false, startVal);
5667 5675
5668 5676
            let mut indexVar: ?Var = nil;
5669 5677
            if indexName <> nil { // Optional index always starts at zero.
5670 5678
                set indexVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0));
5695 5703
            let mut valVar: ?Var = nil;
5696 5704
            if bindingName <> nil {
5697 5705
                set valVar = newVar(
5698 5706
                    self,
5699 5707
                    bindingName,
5700 -
                    ilType(&mut *self.low, *elemType),
5708 +
                    ilType(self.low, *elemType),
5701 5709
                    false,
5702 5710
                    il::Val::Undef
5703 5711
                );
5704 5712
            }
5705 5713
            let iter = ForIter::Collection { valVar, idxVar, dataReg, lengthVal, elemType };
5763 5771
unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) {
5764 5772
    assert self.fnType.throwList.len > 0;
5765 5773
5766 5774
    let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1
5767 5775
        else try typeOf(self, expr);
5768 -
    let tag = getOrAssignErrorTag(&mut *self.low, errType) as i64;
5776 +
    let tag = getOrAssignErrorTag(self.low, errType) as i64;
5769 5777
    let errVal = try lowerExpr(self, expr);
5770 5778
    let resultVal = try buildResult(self, tag, errVal, errType);
5771 5779
5772 5780
    try emitRetVal(self, resultVal);
5773 5781
}
5904 5912
5905 5913
        return il::Val::Reg(dst);
5906 5914
    } else {
5907 5915
        try emitCondBranch(self, cond.condition, thenBlock, elseBlock);
5908 5916
5909 -
        let resultType = ilType(&mut *self.low, typ);
5917 +
        let resultType = ilType(self.low, typ);
5910 5918
        let resultReg = nextReg(self);
5911 5919
        let mergeBlock = try createBlockWithParam(
5912 5920
            self, "cond#merge", il::Param { value: resultReg, type: resultType }
5913 5921
        );
5914 5922
        try switchToAndSeal(self, thenBlock);
5975 5983
5976 5984
    if isComparison {
5977 5985
        let leftTy = try effectiveType(self, binop.left);
5978 5986
        let rightTy = try effectiveType(self, binop.right);
5979 5987
        // Optimize: comparing with a void variant just needs tag comparison.
5980 -
        if let idx = voidVariantIndex(&*self.low.resolver, binop.left) {
5988 +
        if let idx = voidVariantIndex(self.low.resolver, binop.left) {
5981 5989
            return try emitTagCmp(self, binop.op, b, idx, rightTy);
5982 -
        } else if let idx = voidVariantIndex(&*self.low.resolver, binop.right) {
5990 +
        } else if let idx = voidVariantIndex(self.low.resolver, binop.right) {
5983 5991
            return try emitTagCmp(self, binop.op, a, idx, leftTy);
5984 5992
        }
5985 5993
        // Aggregate types require element-wise comparison.
5986 5994
        // When comparing `?T` with `T`, wrap the scalar side.
5987 5995
        if isAggregateType(leftTy) {
5995 6003
            let lhs = try wrapInOptional(self, a, rightTy);
5996 6004
            return try emitAggregateEqOp(self, binop.op, rightTy, lhs, b);
5997 6005
        }
5998 6006
        set resultTy = scalarComparisonType(leftTy, rightTy);
5999 6007
    }
6000 -
    return emitScalarBinOp(self, binop.op, ilType(&mut *self.low, resultTy), a, b, isUnsignedType(resultTy));
6008 +
    return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy));
6001 6009
}
6002 6010
6003 6011
/// Emit an aggregate equality or inequality comparison.
6004 6012
unsafe fn emitAggregateEqOp(
6005 6013
    self: &mut FnLowerer,
6116 6124
            return il::Val::Imm((0 - lit.magnitude) as i64);
6117 6125
        }
6118 6126
    }
6119 6127
    let val = try lowerExpr(self, unop.value);
6120 6128
    let t = try typeOf(self, node);
6121 -
    let typ = ilType(&mut *self.low, t);
6129 +
    let typ = ilType(self.low, t);
6122 6130
    let dst = nextReg(self);
6123 6131
    let mut needsExt: bool = false;
6124 6132
6125 6133
    match unop.op {
6126 6134
        case ast::UnaryOp::Not => {
6179 6187
    let sliceTy = try typeOf(self, node);
6180 6188
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6181 6189
        else throw LowerError::ExpectedSliceOrArray;
6182 6190
    // Build the string data value.
6183 6191
    let ptr = try! alloc::alloc(
6184 -
        &mut *self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6192 +
        self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6185 6193
    ) as *mut il::DataValue;
6186 6194
6187 6195
    set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 };
6188 6196
    let result = ConstDataResult { values: @sliceOf(ptr, 1), zeroInit: false };
6189 6197
6191 6199
        self, &result, 1, true, item, mutable, s.len
6192 6200
    );
6193 6201
}
6194 6202
6195 6203
/// Lower a builtin call expression.
6196 -
unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6204 +
unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) {
6197 6205
    match kind {
6198 6206
        case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args),
6199 6207
        case ast::Builtin::SizeOf, ast::Builtin::AlignOf => {
6200 -
            let constVal = resolver::constValueEntry(&*self.low.resolver, node) else {
6208 +
            let constVal = resolver::constValueEntry(self.low.resolver, node) else {
6201 6209
                throw LowerError::MissingConst(node);
6202 6210
            };
6203 6211
            return try constValueToVal(self, constVal, node);
6204 6212
        }
6205 6213
    }
6206 6214
}
6207 6215
6208 6216
/// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call.
6209 -
unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6217 +
unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) {
6210 6218
    if args.len <> 2 and args.len <> 3 {
6211 6219
        throw LowerError::InvalidArgCount;
6212 6220
    }
6213 6221
    let sliceTy = try typeOf(self, node);
6214 6222
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6239 6247
    // Type of the try expression, which is either the return type of the function
6240 6248
    // if successful, or an optional of it, if using `try?`.
6241 6249
    let tryExprTy = try typeOf(self, node);
6242 6250
    // Check for trait method dispatch or standalone method call.
6243 6251
    let mut resVal: il::Val = undefined;
6244 -
    let callNodeExtra = resolver::nodeData(&*self.low.resolver, t.expr).extra;
6252 +
    let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra;
6245 6253
    if let case resolver::NodeExtra::TraitMethodCall {
6246 6254
        traitInfo, methodIndex
6247 6255
    } = callNodeExtra {
6248 6256
        set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex);
6249 6257
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6250 -
        set resVal = try lowerMethodCall(self, t.expr, callExpr, &*method);
6258 +
        set resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6251 6259
    } else {
6252 6260
        set resVal = try lowerCall(self, t.expr, callExpr);
6253 6261
    }
6254 6262
    let base = emitValToReg(self, resVal); // The result value.
6255 6263
    let tagReg = resultTagReg(self, base); // The result tag.
6317 6325
                let case ast::NodeValue::Ident(name) = binding.value else {
6318 6326
                    throw LowerError::ExpectedIdentifier;
6319 6327
                };
6320 6328
                let errTy = *calleeInfo.throwList[0];
6321 6329
                let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET);
6322 -
                let _ = newVar(self, name, ilType(&mut *self.low, errTy), false, errVal);
6330 +
                let _ = newVar(self, name, ilType(self.low, errTy), false, errVal);
6323 6331
            }
6324 6332
            try lowerBlock(self, first.body);
6325 6333
            try emitMergeIfUnterminated(self, &mut mergeBlock);
6326 6334
            exitVarScope(self, savedVarsLen);
6327 6335
        }
6370 6378
/// Emits a switch on the global error tag to dispatch to the correct catch
6371 6379
/// clause. Each typed clause extracts the error payload for its specific type
6372 6380
/// and binds it to the clause's identifier.
6373 6381
unsafe fn lowerMultiCatch(
6374 6382
    self: &mut FnLowerer,
6375 -
    catches: *mut [*ast::Node],
6383 +
    catches: *[*ast::Node],
6376 6384
    calleeInfo: *resolver::FnType,
6377 6385
    base: il::Reg,
6378 6386
    tagReg: il::Reg,
6379 6387
    mergeBlock: &mut ?BlockId
6380 6388
) throws (LowerError) {
6381 6389
    let entry = currentBlock(self);
6382 6390
6383 6391
    // First pass: create blocks, resolve error types, and build switch cases.
6384 6392
    let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined;
6385 6393
    let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined;
6386 -
    let mut cases: *mut [il::SwitchCase] = &mut [];
6394 +
    let mut cases: *unsafe mut [il::SwitchCase] = &mut [];
6387 6395
    let mut defaultIdx: ?u32 = nil;
6388 6396
6389 6397
    for clauseNode, i in catches {
6390 6398
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value
6391 6399
            else panic "lowerMultiCatch: expected CatchClause";
6396 6404
        if let typeNode = clause.typeNode {
6397 6405
            let errTy = try typeOf(self, typeNode);
6398 6406
            set errTypes[i] = errTy;
6399 6407
6400 6408
            cases.append(il::SwitchCase {
6401 -
                value: getOrAssignErrorTag(&mut *self.low, errTy) as i64,
6409 +
                value: getOrAssignErrorTag(self.low, errTy) as i64,
6402 6410
                target: *blocks[i],
6403 6411
                args: &mut []
6404 6412
            }, self.allocator);
6405 6413
        } else {
6406 6414
            set errTypes[i] = nil;
6436 6444
                throw LowerError::ExpectedIdentifier;
6437 6445
            };
6438 6446
            let errTy = errTypes[i] else panic "lowerMultiCatch: catch-all with binding";
6439 6447
            let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET);
6440 6448
6441 -
            newVar(self, name, ilType(&mut *self.low, errTy), false, errVal);
6449 +
            newVar(self, name, ilType(self.low, errTy), false, errVal);
6442 6450
        }
6443 6451
        try lowerBlock(self, clause.body);
6444 6452
        try emitMergeIfUnterminated(self, mergeBlock);
6445 6453
6446 6454
        exitVarScope(self, savedVarsLen);
6642 6650
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
6643 6651
}
6644 6652
6645 6653
/// Lower a call expression, which may be a function call or type constructor.
6646 6654
unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6647 -
    let nodeData = resolver::nodeData(&*self.low.resolver, node).extra;
6655 +
    let nodeData = resolver::nodeData(self.low.resolver, node).extra;
6648 6656
6649 6657
    // Check for slice method dispatch.
6650 6658
    if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData {
6651 6659
        return try lowerSliceAppend(self, call, elemType);
6652 6660
    }
6658 6666
    if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData {
6659 6667
        return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex);
6660 6668
    }
6661 6669
    // Check for standalone method call.
6662 6670
    if let case resolver::NodeExtra::MethodCall { method } = nodeData {
6663 -
        return try lowerMethodCall(self, node, call, &*method);
6671 +
        return try lowerMethodCall(self, node, call, method);
6664 6672
    }
6665 -
    if let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym {
6673 +
    if let sym = resolver::nodeData(self.low.resolver, call.callee).sym {
6666 6674
        if let case resolver::SymbolData::Type(nominal) = sym.data {
6667 6675
            let case resolver::NominalType::Record(_) = *nominal else {
6668 6676
                throw LowerError::ExpectedRecord;
6669 6677
            };
6670 6678
            return try lowerRecordCtor(self, nominal, call.args);
6687 6695
///
6688 6696
unsafe fn lowerTraitMethodCall(
6689 6697
    self: &mut FnLowerer,
6690 6698
    node: *ast::Node,
6691 6699
    call: ast::Call,
6692 -
    traitInfo: *resolver::TraitType,
6700 +
    traitInfo: *unsafe resolver::TraitType,
6693 6701
    methodIndex: u32
6694 6702
) -> il::Val throws (LowerError) {
6695 6703
    // Method calls look like field accesses.
6696 6704
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6697 6705
        else throw LowerError::MissingMetadata;
6769 6777
/// return parameter; that slot is filled by this function.
6770 6778
unsafe fn emitCallValue(
6771 6779
    self: &mut FnLowerer,
6772 6780
    callee: il::Val,
6773 6781
    fnInfo: *resolver::FnType,
6774 -
    args: *mut [il::Val],
6782 +
    args: *unsafe mut [il::Val],
6775 6783
) -> il::Val throws (LowerError) {
6776 6784
    let retTy = *fnInfo.returnType;
6777 6785
6778 6786
    if requiresReturnParam(fnInfo) {
6779 6787
        if fnInfo.throwList.len > 0 {
6795 6803
    let mut dst: ?il::Reg = nil;
6796 6804
    if retTy <> resolver::Type::Void {
6797 6805
        set dst = nextReg(self);
6798 6806
    }
6799 6807
    emit(self, il::Instr::Call {
6800 -
        retTy: ilType(&mut *self.low, retTy),
6808 +
        retTy: ilType(self.low, retTy),
6801 6809
        dst,
6802 6810
        func: callee,
6803 6811
        args,
6804 6812
    });
6805 6813
6838 6846
    let val = try lowerExpr(self, parent);
6839 6847
    if isAggregateType(parentTy) {
6840 6848
        return val;
6841 6849
    }
6842 6850
    // Scalar value: store to a stack slot and return the slot pointer.
6843 -
    let layout = resolver::getLayout(&*self.low.resolver, parent, parentTy);
6851 +
    let layout = resolver::getLayout(self.low.resolver, parent, parentTy);
6844 6852
    let slot = emitReserveLayout(self, layout);
6845 6853
    try emitStore(self, slot, 0, parentTy, val);
6846 6854
6847 6855
    return il::Val::Reg(slot);
6848 6856
}
6865 6873
6866 6874
    // Get the receiver as a pointer.
6867 6875
    let parentTy = try typeOf(self, access.parent);
6868 6876
    let receiverVal = try lowerReceiver(self, access.parent, parentTy);
6869 6877
6870 -
    let qualName = instanceMethodName(&mut *self.low, nil, method.concreteTypeName, method.name);
6878 +
    let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name);
6871 6879
    let case resolver::SymbolData::Value { type: resolver::Type::Fn(fnInfo), .. } = method.symbol.data
6872 6880
        else panic "lowerMethodCall: expected Fn type on method symbol";
6873 6881
6874 6882
    // Build args: optional return param slot + receiver + user args.
6875 6883
    let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
6884 6892
}
6885 6893
6886 6894
/// Check if a call is to a compiler intrinsic and lower it directly.
6887 6895
unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) {
6888 6896
    // Get the callee symbol and check if it's marked as an intrinsic.
6889 -
    let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym else {
6897 +
    let sym = resolver::nodeData(self.low.resolver, call.callee).sym else {
6890 6898
        // Expressions or function pointers may not have an associated symbol.
6891 6899
        return nil;
6892 6900
    };
6893 6901
    if not ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic) {
6894 6902
        return nil;
6943 6951
6944 6952
/// Resolve callee to an IL value. For direct function calls, use the symbol name.
6945 6953
/// For variables holding function pointers or complex expressions (eg. `array[i]()`),
6946 6954
/// lower the callee expression.
6947 6955
unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) {
6948 -
    if let sym = resolver::nodeData(&*self.low.resolver, callee).sym {
6956 +
    if let sym = resolver::nodeData(self.low.resolver, callee).sym {
6949 6957
        if let case ast::NodeValue::FnDecl(_) = sym.node.value {
6950 6958
            // First try to look up the symbol in our registered functions.
6951 6959
            // This handles cross-package calls correctly, since packages are
6952 6960
            // lowered in dependency order.
6953 -
            if let qualName = lookupFnSym(&mut *self.low, sym) {
6961 +
            if let qualName = lookupFnSym(self.low, sym) {
6954 6962
                return il::Val::FnAddr(qualName);
6955 6963
            }
6956 6964
            // Fall back to computing the qualified name from the module graph.
6957 6965
            // This works for functions in the current package.
6958 -
            let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym) else {
6966 +
            let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else {
6959 6967
                throw LowerError::MissingMetadata;
6960 6968
            };
6961 -
            return il::Val::FnAddr(qualifyName(&mut *self.low, modId, sym.name));
6969 +
            return il::Val::FnAddr(qualifyName(self.low, modId, sym.name));
6962 6970
        }
6963 6971
    }
6964 6972
    return try lowerExpr(self, callee);
6965 6973
}
6966 6974
6986 6994
    return try emitCallValue(self, callee, fnInfo, args);
6987 6995
}
6988 6996
6989 6997
/// Apply coercions requested by the resolver.
6990 6998
unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) {
6991 -
    let coerce = resolver::coercionFor(&*self.low.resolver, node) else {
6999 +
    let coerce = resolver::coercionFor(self.low.resolver, node) else {
6992 7000
        return val;
6993 7001
    };
6994 7002
    match coerce {
6995 7003
        case resolver::Coercion::OptionalLift(optType) => {
6996 7004
            if let case ast::NodeValue::Nil = node.value {
7004 7012
        case resolver::Coercion::ResultWrap => {
7005 7013
            let payloadType = *self.fnType.returnType;
7006 7014
            return try buildResult(self, 0, val, payloadType);
7007 7015
        }
7008 7016
        case resolver::Coercion::TraitObject { traitInfo, inst } => {
7009 -
            return try buildTraitObject(self, val, traitInfo, &*inst);
7017 +
            return try buildTraitObject(self, val, traitInfo, inst);
7010 7018
        }
7011 7019
        case resolver::Coercion::Identity => return val,
7012 7020
    }
7013 7021
}
7014 7022
7025 7033
        return val;
7026 7034
    }
7027 7035
    // Widening: extend based on source signedness.
7028 7036
    // Narrowing: truncate and normalize to destination width.
7029 7037
    let widening = srcLayout.size < dstLayout.size;
7030 -
    let extType = ilType(&mut *self.low, srcType) if widening else ilType(&mut *self.low, dstType);
7038 +
    let extType = ilType(self.low, srcType) if widening else ilType(self.low, dstType);
7031 7039
    let signed = isSignedType(srcType) if widening else isSignedType(dstType);
7032 7040
    let dst = nextReg(self);
7033 7041
7034 7042
    if signed {
7035 7043
        emit(self, il::Instr::Sext { typ: extType, dst, val });
7038 7046
    }
7039 7047
    return il::Val::Reg(dst);
7040 7048
}
7041 7049
7042 7050
/// Lower a global value symbol.
7043 -
unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val {
7051 +
unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val {
7044 7052
    // Function pointer reference: return the function's address directly.
7045 7053
    // Functions have no separate storage cell in the data section.
7046 7054
    if let case resolver::Type::Fn(_) = ty {
7047 7055
        return il::Val::Reg(emitFnAddr(self, sym));
7048 7056
    }
7052 7060
}
7053 7061
7054 7062
/// Lower an identifier that refers to a global symbol.
7055 7063
unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7056 7064
    // First try to get a compile-time constant value.
7057 -
    if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7065 +
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7058 7066
        return try constValueToVal(self, constVal, node);
7059 7067
    }
7060 7068
    // Otherwise get the symbol.
7061 7069
    let sym = try symOf(self, node);
7062 7070
7085 7093
7086 7094
/// Lower a scope access expression like `Module::Const` or `Union::Variant`.
7087 7095
/// This doesn't handle record literal variants.
7088 7096
unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7089 7097
    // First try to get a compile-time constant value.
7090 -
    if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7098 +
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7091 7099
        return try constValueToVal(self, constVal, node);
7092 7100
    }
7093 7101
    // Otherwise get the associated symbol.
7094 -
    let data = resolver::nodeData(&*self.low.resolver, node);
7102 +
    let data = resolver::nodeData(self.low.resolver, node);
7095 7103
    let sym = data.sym else {
7096 7104
        throw LowerError::MissingSymbol(node);
7097 7105
    };
7098 7106
    match sym.data {
7099 7107
        case resolver::SymbolData::Variant { index, .. } => {
7100 7108
            let mut indexValue = index as i64;
7101 -
            if let idx = voidVariantIndex(&*self.low.resolver, node) {
7109 +
            if let idx = voidVariantIndex(self.low.resolver, node) {
7102 7110
                set indexValue = idx;
7103 7111
            }
7104 7112
            // Void union variant like `Option::None`.
7105 7113
            if data.ty == resolver::Type::Unknown {
7106 7114
                throw LowerError::MissingType(node);
7214 7222
        case ast::NodeValue::Try(t) => {
7215 7223
            set val = try lowerTry(self, node, t);
7216 7224
        }
7217 7225
        case ast::NodeValue::FieldAccess(access) => {
7218 7226
            // Check for compile-time constant (e.g., `arr.len` on fixed-size arrays).
7219 -
            if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7227 +
            if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7220 7228
                match constVal {
7221 7229
                    // TODO: Handle `u32` values that don't fit in an `i32`.
7222 7230
                    //       Perhaps just store the `ConstInt`.
7223 7231
                    case resolver::ConstValue::Int(i) => set val = il::Val::Imm(constIntToI64(i)),
7224 7232
                    else => set val = try lowerFieldAccess(self, access),
7273 7281
            set val = il::Val::Undef;
7274 7282
        }
7275 7283
        // Lower these as statements.
7276 7284
        case ast::NodeValue::ConstDecl(decl) => {
7277 7285
            try registerLocalDataDeclName(self, node);
7278 -
            try lowerDataDecl(&mut *self.low, node, decl.value, true);
7286 +
            try lowerDataDecl(self.low, node, decl.value, true);
7279 7287
            set val = il::Val::Undef;
7280 7288
        }
7281 7289
        case ast::NodeValue::StaticDecl(decl) => {
7282 7290
            try registerLocalDataDeclName(self, node);
7283 -
            try lowerDataDecl(&mut *self.low, node, decl.value, false);
7291 +
            try lowerDataDecl(self.low, node, decl.value, false);
7284 7292
            set val = il::Val::Undef;
7285 7293
        }
7286 7294
        case ast::NodeValue::Throw { .. },
7287 7295
             ast::NodeValue::Return { .. },
7288 7296
             ast::NodeValue::Continue,
7303 7311
/// are used. If a Radiance type doesn't fit in a machine word, it is passed
7304 7312
/// by reference.
7305 7313
///
7306 7314
/// The IL doesn't track signedness - that's encoded in the instructions
7307 7315
/// (e.g., Slt vs Ult).
7308 -
fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type {
7316 +
unsafe fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type {
7309 7317
    match typ {
7310 7318
        case resolver::Type::Bool,
7311 7319
             resolver::Type::I8,
7312 7320
             resolver::Type::U8 => return il::Type::W8,
7313 7321
        case resolver::Type::I16,
lib/std/lang/module.rad +18 -22
77 77
    /// Child module identifiers declared directly inside this module.
78 78
    children: [u16; MAX_MODULES],
79 79
    /// Number of entries stored in `children`.
80 80
    childrenLen: u32,
81 81
    /// Parsed AST root for this module when available.
82 -
    ast: ?*mut ast::Node,
82 +
    ast: ?*ast::Node,
83 83
    /// Source text for this module (for error reporting).
84 84
    source: ?*[u8],
85 85
}
86 86
87 87
/// Dense storage for all modules referenced by the compilation unit.
88 -
export record ModuleGraph: Copy {
88 +
export record ModuleGraph {
89 89
    /// Permanent storage for module entries.
90 90
    entries: *mut [ModuleEntry],
91 91
    /// Number of initialized entries.
92 92
    entriesLen: u32,
93 -
    /// Permanent storage for interned names.
94 -
    pool: *mut strings::Pool,
95 93
    /// AST arena. It must outlive the graph.
96 94
    arena: ?*unsafe ast::NodeArena,
97 95
}
98 96
99 97
/// Initialize an empty module graph backed by the provided storage.
100 98
/// The AST arena must outlive the returned graph.
101 99
export unsafe fn moduleGraph(
102 100
    storage: *mut [ModuleEntry],
103 -
    pool: *mut strings::Pool,
104 101
    arena: &mut ast::NodeArena
105 102
) -> ModuleGraph {
106 103
    return ModuleGraph {
107 104
        entries: storage,
108 105
        entriesLen: 0,
109 -
        pool,
110 106
        arena: arena as *unsafe ast::NodeArena,
111 107
    };
112 108
}
113 109
114 110
/// Register a root module residing at `path` for a package.
115 -
export fn registerRoot(graph: &mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
111 +
export fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
116 112
    let name = try basenameSlice(filePath);
117 -
    return try registerRootWithName(graph, packageId, name, filePath);
113 +
    return try registerRootWithName(graph, pool, packageId, name, filePath);
118 114
}
119 115
120 116
/// Register a root module with an explicit name and file path for a package.
121 117
export fn registerRootWithName(
122 -
    graph: &mut ModuleGraph,
118 +
    graph: &mut ModuleGraph, pool: &mut strings::Pool,
123 119
    packageId: u16,
124 120
    name: *[u8],
125 121
    filePath: *[u8]
126 122
) -> u16 throws (ModuleError) {
127 -
    let m = try allocModule(graph, packageId, name, filePath);
128 -
    try appendPathSegment(m, m.name);
123 +
    let m = try allocModule(graph, pool, packageId, name, filePath);
124 +
    try appendPathSegment(m, name);
129 125
130 126
    set m.state = ModuleState::Registered;
131 127
132 128
    return m.id;
133 129
}
134 130
135 131
/// Register a child module.
136 132
/// Returns the module identifier.
137 133
export fn registerChild(
138 -
    graph: &mut ModuleGraph,
134 +
    graph: &mut ModuleGraph, pool: &mut strings::Pool,
139 135
    parentId: u16,
140 136
    name: *[u8],
141 137
    filePath: *[u8]
142 138
) -> u16 throws (ModuleError) {
143 139
    assert name.len > 0, "registerChild: name must not be empty";
149 145
    // If the child already exists under this parent, return it.
150 146
    if let child = findChild(graph, name, parentId) {
151 147
        return child.id;
152 148
    }
153 149
    // Inherit packageId from parent.
154 -
    let m = try allocModule(graph, parent.packageId, name, filePath);
150 +
    let m = try allocModule(graph, pool, parent.packageId, name, filePath);
155 151
156 152
    set m.state = ModuleState::Registered;
157 153
    set m.parent = parentId;
158 154
159 155
    // Inherit path prefix from parent.
160 156
    for p in moduleQualifiedPath(parent) {
161 157
        try appendPathSegment(m, p);
162 158
    }
163 -
    try appendPathSegment(m, m.name);
159 +
    try appendPathSegment(m, name);
164 160
165 161
    return try addChild(parent, m.id);
166 162
}
167 163
168 164
/// Fetch a read-only view of the module identified by `id`.
205 201
    };
206 202
    return m.state;
207 203
}
208 204
209 205
/// Record the parsed AST root for `id`.
210 -
export fn setAst(graph: &mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) {
206 +
export fn setAst(graph: &mut ModuleGraph, id: u16, root: *ast::Node) throws (ModuleError) {
211 207
    let m = getMut(graph, id) else throw ModuleError::NotFound(id);
212 208
    set m.ast = root;
213 209
    set m.state = ModuleState::Parsed;
214 210
}
215 211
270 266
271 267
/// Register a module from a file path, creating the full hierarchy as needed.
272 268
/// The path is split into components and the module hierarchy is built accordingly.
273 269
/// If `rootId` is `nil`, registers a new root for the given package.
274 270
/// Returns the module ID of the last component.
275 -
export fn registerFromPath(graph: &mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
271 +
export fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
276 272
    let root = rootId else {
277 -
        return try registerRoot(graph, packageId, filePath);
273 +
        return try registerRoot(graph, pool, packageId, filePath);
278 274
    };
279 275
    let rootEntry = get(graph, root) else {
280 276
        panic "registerFromPath: root is missing from storage";
281 277
    };
282 278
311 307
        let child = findChild(graph, part, parentId) else {
312 308
            throw ModuleError::MissingParent;
313 309
        };
314 310
        set parentId = child.id;
315 311
    }
316 -
    return try registerChild(graph, parentId, childName, filePath);
312 +
    return try registerChild(graph, pool, parentId, childName, filePath);
317 313
}
318 314
319 315
/// Allocate a fresh entry in the graph.
320 -
fn allocModule(graph: &mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
316 +
fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
321 317
    if graph.entriesLen >= graph.entries.len {
322 318
        throw ModuleError::CapacityExceeded;
323 319
    }
324 320
    let idx = graph.entriesLen;
325 321
    set graph.entriesLen += 1;
330 326
        id: idx as u16,
331 327
        packageId,
332 328
        parent: nil,
333 329
        filePath,
334 330
        dirLen: 0,
335 -
        name: strings::intern(graph.pool, name),
331 +
        name: strings::intern(pool, name),
336 332
        path: [""; MAX_MODULE_PATH_DEPTH],
337 333
        pathDepth: 0,
338 334
        state: ModuleState::Vacant,
339 335
        children: [0; MAX_MODULES],
340 336
        childrenLen: 0,
345 341
346 342
    return m;
347 343
}
348 344
349 345
/// Append a logical path segment (module identifier) to the entry.
350 -
fn appendPathSegment(entry: *mut ModuleEntry, segment: *[u8]) throws (ModuleError) {
346 +
fn appendPathSegment(entry: &mut ModuleEntry, segment: *[u8]) throws (ModuleError) {
351 347
    if entry.pathDepth >= entry.path.len {
352 348
        throw ModuleError::PathTooDeep;
353 349
    }
354 350
    set entry.path[entry.pathDepth] = segment;
355 351
    set entry.pathDepth += 1;
356 352
}
357 353
358 354
/// Append a child identifier to the parent's child list.
359 -
fn addChild(parent: *mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) {
355 +
fn addChild(parent: &mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) {
360 356
    if parent.childrenLen >= parent.children.len {
361 357
        throw ModuleError::CapacityExceeded;
362 358
    }
363 359
    set parent.children[parent.childrenLen] = childId;
364 360
    set parent.childrenLen += 1;
lib/std/lang/module/tests.rad +24 -24
29 29
}
30 30
31 31
@test unsafe fn testRegisterChildren() throws (testing::TestError) {
32 32
    static storage: [super::ModuleEntry; 4] = undefined;
33 33
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
34 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
34 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
35 35
36 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/root.rad") catch {
36 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch {
37 37
        throw testing::TestError::Failed;
38 38
    };
39 39
    let root = super::get(&graph, rootId) else {
40 40
        throw testing::TestError::Failed;
41 41
    };
42 42
    try expectPathSegments(root, &["root"]);
43 43
44 44
    // First child.
45 -
    let firstId = try super::registerFromPath(&mut graph, 0, rootId, "src/root/first.rad") catch {
45 +
    let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/first.rad") catch {
46 46
        throw testing::TestError::Failed;
47 47
    };
48 48
    let first = super::get(&graph, firstId) else {
49 49
        throw testing::TestError::Failed;
50 50
    };
51 51
    try expectSliceEq(first.filePath, "src/root/first.rad");
52 52
    try expectSliceEq(first.name, "first");
53 53
    try expectPathSegments(first, &["root", "first"]);
54 54
55 55
    // Second child.
56 -
    let secondId = try super::registerFromPath(&mut graph, 0, rootId, "src/root/second.rad") catch {
56 +
    let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/second.rad") catch {
57 57
        throw testing::TestError::Failed;
58 58
    };
59 59
    let second = super::get(&graph, secondId) else {
60 60
        throw testing::TestError::Failed;
61 61
    };
73 73
}
74 74
75 75
@test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) {
76 76
    static storage: [super::ModuleEntry; 4] = undefined;
77 77
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
78 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
78 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
79 79
80 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/main.rad") catch {
80 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch {
81 81
        throw testing::TestError::Failed;
82 82
    };
83 -
    let firstId = try super::registerFromPath(&mut graph, 0, rootId, "src/main/util.rad") catch {
83 +
    let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch {
84 84
        throw testing::TestError::Failed;
85 85
    };
86 -
    let secondId = try super::registerFromPath(&mut graph, 0, rootId, "src/main/util.rad") catch {
86 +
    let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch {
87 87
        throw testing::TestError::Failed;
88 88
    };
89 89
    try testing::expect(firstId == secondId);
90 90
91 91
    let parent = super::get(&graph, rootId) else {
144 144
}
145 145
146 146
@test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) {
147 147
    static storage: [super::ModuleEntry; 8] = undefined;
148 148
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
149 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
149 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
150 150
151 151
    // Register root module.
152 -
    let stdId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch {
152 +
    let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch {
153 153
        throw testing::TestError::Failed;
154 154
    };
155 155
    let std = super::get(&graph, stdId) else {
156 156
        throw testing::TestError::Failed;
157 157
    };
158 158
    try expectSliceEq(std.filePath, "lib/std.rad");
159 159
    try expectSliceEq(super::moduleDir(std), "lib/");
160 160
    try expectPathSegments(std, &["std"]);
161 161
162 162
    // Register child of root.
163 -
    let langId = try super::registerFromPath(&mut graph, 0, stdId, "lib/std/lang.rad") catch {
163 +
    let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang.rad") catch {
164 164
        throw testing::TestError::Failed;
165 165
    };
166 166
    let lang = super::get(&graph, langId) else {
167 167
        throw testing::TestError::Failed;
168 168
    };
170 170
    try expectSliceEq(lang.name, "lang");
171 171
    try expectSliceEq(super::moduleDir(lang), "lib/std/");
172 172
    try expectPathSegments(lang, &["std", "lang"]);
173 173
174 174
    // Register grandchild.
175 -
    let parserId = try super::registerFromPath(&mut graph, 0, stdId, "lib/std/lang/parser.rad") catch {
175 +
    let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang/parser.rad") catch {
176 176
        throw testing::TestError::Failed;
177 177
    };
178 178
    let parser = super::get(&graph, parserId) else {
179 179
        throw testing::TestError::Failed;
180 180
    };
193 193
}
194 194
195 195
@test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) {
196 196
    static storage: [super::ModuleEntry; 8] = undefined;
197 197
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
198 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
198 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
199 199
200 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch {
200 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch {
201 201
        throw testing::TestError::Failed;
202 202
    };
203 -
    let _ = try super::registerFromPath(&mut graph, 0, rootId, "std/lang/parser.rad") catch {
203 +
    let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "std/lang/parser.rad") catch {
204 204
        // Expected to fail due to missing intermediate parent.
205 205
        return;
206 206
    };
207 207
    throw testing::TestError::Failed;
208 208
}
209 209
210 210
@test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) {
211 211
    static storage: [super::ModuleEntry; 8] = undefined;
212 212
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
213 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
213 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
214 214
215 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch {
215 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch {
216 216
        throw testing::TestError::Failed;
217 217
    };
218 -
    let _ = try super::registerFromPath(&mut graph, 0, rootId, "parser.rad") catch {
218 +
    let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "parser.rad") catch {
219 219
        // Expected to fail due to missing intermediate parent.
220 220
        return;
221 221
    };
222 222
    throw testing::TestError::Failed;
223 223
}
224 224
225 225
@test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) {
226 226
    static storage: [super::ModuleEntry; 8] = undefined;
227 227
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
228 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
228 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
229 229
230 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch {
230 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch {
231 231
        throw testing::TestError::Failed;
232 232
    };
233 233
    // Verify root module was registered with correct ID.
234 234
    try testing::expect(rootId == 0);
235 235
242 242
}
243 243
244 244
@test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) {
245 245
    static storage: [super::ModuleEntry; 8] = undefined;
246 246
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
247 -
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
247 +
    let mut graph = super::moduleGraph(&mut storage[..], &mut arena);
248 248
249 -
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/pkg/root.rad") catch {
249 +
    let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch {
250 250
        throw testing::TestError::Failed;
251 251
    };
252 252
    // Verify root module was registered with correct ID.
253 253
    try testing::expect(rootId == 0);
254 254
255 -
    let langId = try super::registerFromPath(&mut graph, 0, rootId, "src/pkg/root/lang.rad") catch {
255 +
    let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang.rad") catch {
256 256
        throw testing::TestError::Failed;
257 257
    };
258 258
    let lang = super::get(&graph, langId) else {
259 259
        throw testing::TestError::Failed;
260 260
    };
261 261
    try expectSliceEq(lang.name, "lang");
262 262
    try expectPathSegments(lang, &["root", "lang"]);
263 263
264 -
    let parserId = try super::registerFromPath(&mut graph, 0, rootId, "src/pkg/root/lang/parser.rad") catch {
264 +
    let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang/parser.rad") catch {
265 265
        throw testing::TestError::Failed;
266 266
    };
267 267
    let parser = super::get(&graph, parserId) else {
268 268
        throw testing::TestError::Failed;
269 269
    };
lib/std/lang/package.rad +3 -3
22 22
/// Initialize `pkg` with the provided name and ID.
23 23
export fn init(
24 24
    pkg: &mut Package,
25 25
    id: u16,
26 26
    name: *[u8],
27 -
    pool: *mut strings::Pool
27 +
    pool: &mut strings::Pool
28 28
) {
29 29
    set pkg.id = id;
30 30
    set pkg.name = strings::intern(pool, name);
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, filePath: *[u8]) -> u16
36 +
export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16
37 37
    throws (module::ModuleError)
38 38
{
39 -
    let modId = try module::registerFromPath(graph, pkg.id, pkg.rootModuleId, filePath);
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 {
42 42
        set pkg.rootModuleId = modId;
43 43
    }
44 44
    return modId;
lib/std/lang/parser.rad +37 -41
160 160
    /// Current parsing context (normal or conditional).
161 161
    context: Context,
162 162
}
163 163
164 164
/// Create a new parser initialized with the given source kind, source and node arena.
165 -
/// The node arena must outlive the returned parser.
166 -
export unsafe fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> Parser {
165 +
/// The node arena and string pool must outlive every copy of the parser.
166 +
export unsafe fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: &mut ast::NodeArena, pool: *unsafe mut strings::Pool) -> Parser {
167 167
    return Parser {
168 168
        scanner: scanner::scanner(sourceLoc, source, pool),
169 169
        current: scanner::invalid(0, ""),
170 170
        previous: scanner::invalid(0, ""),
171 171
        errors: ErrorList { list: undefined, count: 0 },
172 -
        arena: arena as *unsafe mut ast::NodeArena,
172 +
        arena: (&mut *arena) as *unsafe mut ast::NodeArena,
173 173
        allocator: alloc::arenaAllocator(&mut arena.arena),
174 174
        context: Context::Normal,
175 175
    };
176 176
}
177 177
265 265
        return node(p, ast::NodeValue::ArrayRepeatLit(
266 266
            ast::ArrayRepeatLit { item: firstExpr, count }
267 267
        ));
268 268
    }
269 269
    // Regular array literal: `[a, b, ...]`.
270 -
    let mut items = ast::nodeSlice(&mut *p.arena, 64).append(firstExpr, p.allocator);
270 +
    let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator);
271 271
272 272
    while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) {
273 273
        let elem = try parseNormalExpr(p);
274 274
        items.append(elem, p.allocator);
275 275
    }
698 698
    }
699 699
    try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name");
700 700
701 701
    // Parse arguments into a list. Use capacity 4 to handle any valid argument count
702 702
    // plus some extra for error recovery.
703 -
    let mut args = ast::nodeSlice(&mut *p.arena, 4);
703 +
    let mut args = ast::nodeSlice(p.arena, 4);
704 704
705 705
    if kind == ast::Builtin::SliceOf {
706 706
        // Parse comma-separated expressions until closing paren.
707 707
        // Argument count validation is done in semantic analysis.
708 708
        while not check(p, scanner::TokenKind::RParen) {
730 730
    let expr = try parseBinary(p, left, -1);
731 731
    return try parseCondExpr(p, expr);
732 732
}
733 733
734 734
/// Try to consume a compound assignment operator and return its binary op.
735 -
fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp {
735 +
unsafe fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp {
736 736
    match p.current.kind {
737 737
        case scanner::TokenKind::PlusEqual =>    { advance(p); return ast::BinaryOp::Add; }
738 738
        case scanner::TokenKind::MinusEqual =>   { advance(p); return ast::BinaryOp::Sub; }
739 739
        case scanner::TokenKind::StarEqual =>    { advance(p); return ast::BinaryOp::Mul; }
740 740
        case scanner::TokenKind::SlashEqual =>   { advance(p); return ast::BinaryOp::Div; }
784 784
    throw failParsing(p, "expected assignment after `set`");
785 785
}
786 786
787 787
/// Parse leading attributes and declaration modifiers.
788 788
unsafe fn parseAttributes(p: &mut Parser) -> ?ast::Attributes {
789 -
    let mut attrs = ast::nodeSlice(&mut *p.arena, 4);
789 +
    let mut attrs = ast::nodeSlice(p.arena, 4);
790 790
791 791
    if let attr = tryParseAnnotation(p) {
792 792
        attrs.append(attr, p.allocator);
793 793
    }
794 794
    if consume(p, scanner::TokenKind::Export) {
810 810
unsafe fn tryParseAnnotation(p: &mut Parser) -> ?*ast::Node {
811 811
    if not check(p, scanner::TokenKind::AtIdent) {
812 812
        return nil;
813 813
    }
814 814
    // Token is @identifier, skip the '@' to get the name.
815 -
    let ident = &p.current.source[..];
815 +
    let ident = p.current.source;
816 816
    if ident == "@default" {
817 817
        advance(p); // Consume `@default`.
818 818
        return nodeAttribute(p, ast::Attribute::Default);
819 819
    }
820 820
    if ident == "@test" {
954 954
    }
955 955
}
956 956
957 957
/// Parse statements until the specified ending token is encountered.
958 958
///
959 -
/// Adds each parsed statement to the given block's statement list.
960 -
export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, blk: &mut ast::Block)
959 +
/// Returns the completed immutable statement list.
960 +
export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, capacity: u32) -> *[*ast::Node]
961 961
    throws (ParseError)
962 962
{
963 +
    let mut statements = ast::nodeSlice(p.arena, capacity);
963 964
    while not check(p, end) {
964 965
        let stmt = try parseStmt(p);
965 -
        blk.statements.append(stmt, p.allocator);
966 +
        statements.append(stmt, p.allocator);
966 967
967 968
        if check(p, end) or check(p, scanner::TokenKind::Eof) {
968 969
            break;
969 970
        }
970 971
        if not consume(p, scanner::TokenKind::Semicolon) {
972 973
            if expectsSemicolon(stmt) {
973 974
                throw failParsing(p, "expected `;` after statement");
974 975
            }
975 976
        }
976 977
    }
978 +
    return statements;
977 979
}
978 980
979 981
/// Parse a block of statements enclosed in curly braces.
980 982
export unsafe fn parseBlock(p: &mut Parser) -> *ast::Node
981 983
    throws (ParseError)
986 988
/// Parse a statement block with the specified unsafe permission.
987 989
unsafe fn parseBlockBody(p: &mut Parser, isUnsafe: bool) -> *ast::Node
988 990
    throws (ParseError)
989 991
{
990 992
    let start = p.current;
991 -
    let mut blk = mkBlock(p, 64);
992 -
    set blk.isUnsafe = isUnsafe;
993 993
994 994
    if not consume(p, scanner::TokenKind::LBrace) {
995 995
        throw failParsing(p, "expected `{`");
996 996
    }
997 -
    try parseStmtsUntil(p, scanner::TokenKind::RBrace, &mut blk);
997 +
    let statements = try parseStmtsUntil(p, scanner::TokenKind::RBrace, 64);
998 +
    let blk = ast::Block { statements, isUnsafe };
998 999
    try expect(p, scanner::TokenKind::RBrace, "expected `}`");
999 1000
1000 1001
    return node(p, ast::NodeValue::Block(blk));
1001 1002
}
1002 1003
1003 -
/// Create an empty block with no statements.
1004 -
unsafe fn mkBlock(p: &mut Parser, cap: u32) -> ast::Block {
1005 -
    return ast::Block { statements: ast::nodeSlice(&mut *p.arena, cap), isUnsafe: false };
1006 -
}
1007 -
1008 1004
/// Create a block containing a single statement node.
1009 1005
unsafe fn mkBlockWith(p: &mut Parser, node: *ast::Node) -> ast::Block {
1010 -
    let stmts = ast::nodeSlice(&mut *p.arena, 1).append(node, p.allocator);
1006 +
    let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator);
1011 1007
    return ast::Block { statements: stmts, isUnsafe: false };
1012 1008
}
1013 1009
1014 1010
/// Parse the branch that follows `else` in let-else style constructs.
1015 1011
///
1033 1029
unsafe fn node(p: &mut Parser, value: ast::NodeValue) -> *mut ast::Node {
1034 1030
    let span = ast::Span {
1035 1031
        offset: p.previous.offset,
1036 1032
        length: p.previous.source.len,
1037 1033
    };
1038 -
    let n = ast::allocNode(&mut *p.arena, span, value);
1034 +
    let n = ast::allocNode(p.arena, span, value);
1039 1035
    finishSpan(p, n);
1040 1036
1041 1037
    return n;
1042 1038
}
1043 1039
1044 1040
/// Update the span of `node` using the most recently consumed token.
1045 -
fn finishSpan(p: &mut Parser, node: *mut ast::Node) {
1041 +
fn finishSpan(p: &mut Parser, node: &mut ast::Node) {
1046 1042
    let start: u32 = node.span.offset;
1047 1043
    let mut end: u32 = p.previous.offset + p.previous.source.len;
1048 1044
1049 1045
    if end >= start {
1050 1046
        set node.span.length = end - start;
1123 1119
export fn check(p: &Parser, kind: scanner::TokenKind) -> bool {
1124 1120
    return p.current.kind == kind;
1125 1121
}
1126 1122
1127 1123
/// Advance the parser by one token.
1128 -
export fn advance(p: &mut Parser) {
1124 +
export unsafe fn advance(p: &mut Parser) {
1129 1125
    set p.previous = p.current;
1130 1126
    set p.current = scanner::next(&mut p.scanner);
1131 1127
}
1132 1128
1133 1129
/// Parse an `if let` pattern matching statement.
1352 1348
    try expect(p, scanner::TokenKind::Try, "expected `try`");
1353 1349
1354 1350
    let shouldPanic = consume(p, scanner::TokenKind::Bang);
1355 1351
    let returnsOptional = consume(p, scanner::TokenKind::Question);
1356 1352
    let expr = try parseUnaryExpr(p);
1357 -
    let mut catches = ast::nodeSlice(&mut *p.arena, 4);
1353 +
    let mut catches = ast::nodeSlice(p.arena, 4);
1358 1354
1359 1355
    while consume(p, scanner::TokenKind::Catch) {
1360 1356
        let mut binding: ?*ast::Node = nil;
1361 1357
        let mut typeNode: ?*ast::Node = nil;
1362 1358
1455 1451
    try expect(p, scanner::TokenKind::Match, "expected `match`");
1456 1452
1457 1453
    let subject = try parseCond(p);
1458 1454
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs");
1459 1455
1460 -
    let mut prongs = ast::nodeSlice(&mut *p.arena, 128);
1456 +
    let mut prongs = ast::nodeSlice(p.arena, 128);
1461 1457
    while not check(p, scanner::TokenKind::RBrace) and
1462 1458
          not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF.
1463 1459
    {
1464 1460
        let prongNode = try parseMatchProng(p);
1465 1461
        prongs.append(prongNode, p.allocator);
1478 1474
{
1479 1475
    let mut guard: ?*ast::Node = nil;
1480 1476
1481 1477
    // Case prong: `case <pattern>, ... if <guard> => <body>`.
1482 1478
    if consume(p, scanner::TokenKind::Case) {
1483 -
        let mut patterns = ast::nodeSlice(&mut *p.arena, 16);
1479 +
        let mut patterns = ast::nodeSlice(p.arena, 16);
1484 1480
        loop {
1485 1481
            let pattern = try parseMatchPattern(p);
1486 1482
            patterns.append(pattern, p.allocator);
1487 1483
1488 1484
            if not consume(p, scanner::TokenKind::Comma) {
1582 1578
) -> *mut [*ast::Node]
1583 1579
    throws (ParseError)
1584 1580
{
1585 1581
    let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled
1586 1582
        else scanner::TokenKind::RParen;
1587 -
    let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS);
1583 +
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1588 1584
    while not check(p, terminator) {
1589 1585
        let mut recordField: ast::NodeValue = undefined;
1590 1586
        match mode {
1591 1587
            case RecordFieldMode::Labeled => {
1592 1588
                // Allow optional `let` keyword before field name.
1624 1620
    return fields;
1625 1621
}
1626 1622
1627 1623
/// Parse an optional derives list (`: Trait + Trait`).
1628 1624
unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1629 -
    let mut derives = ast::nodeSlice(&mut *p.arena, 4);
1625 +
    let mut derives = ast::nodeSlice(p.arena, 4);
1630 1626
1631 1627
    if not consume(p, scanner::TokenKind::Colon) {
1632 1628
        return derives;
1633 1629
    }
1634 1630
    loop {
1665 1661
/// Eg. `{ x: 1, y: 2 }`
1666 1662
/// Eg. `{ x: 1, .. }`
1667 1663
unsafe fn parseRecordLit(p: &mut Parser, typeName: ?*ast::Node) -> *ast::Node
1668 1664
    throws (ParseError)
1669 1665
{
1670 -
    let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS);
1666 +
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1671 1667
    let mut ignoreRest = false;
1672 1668
    try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal");
1673 1669
1674 1670
    while not check(p, scanner::TokenKind::RBrace) {
1675 1671
        // Check for `..` to ignore remaining fields.
1726 1722
    let name = try parseIdent(p, "expected union name");
1727 1723
    let derives = try parseDerives(p);
1728 1724
1729 1725
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body");
1730 1726
1731 -
    let mut variants = ast::nodeSlice(&mut *p.arena, 128);
1727 +
    let mut variants = ast::nodeSlice(p.arena, 128);
1732 1728
    while not check(p, scanner::TokenKind::RBrace) {
1733 1729
        // Allow optional `case` keyword before variant name.
1734 1730
        consume(p, scanner::TokenKind::Case);
1735 1731
1736 1732
        let variantName = try parseIdent(p, "expected variant name");
1789 1785
/// Parse an optional `throws` clause and return the collected type list.
1790 1786
unsafe fn parseThrowList(p: &mut Parser) -> *mut [*ast::Node]
1791 1787
    throws (ParseError)
1792 1788
{
1793 1789
    if not consume(p, scanner::TokenKind::Throws) {
1794 -
        return ast::nodeSlice(&mut *p.arena, 0);
1790 +
        return ast::nodeSlice(p.arena, 0);
1795 1791
    }
1796 1792
    return try parseList(
1797 1793
        p,
1798 1794
        scanner::TokenKind::LParen,
1799 1795
        scanner::TokenKind::RParen,
1828 1824
/// Parse a function signature following the function name.
1829 1825
unsafe fn parseFnTypeSig(p: &mut Parser) -> ast::FnSig
1830 1826
    throws (ParseError)
1831 1827
{
1832 1828
    try expect(p, scanner::TokenKind::LParen, "expected `(` after function name");
1833 -
    let mut params = ast::nodeSlice(&mut *p.arena, 8);
1829 +
    let mut params = ast::nodeSlice(p.arena, 8);
1834 1830
1835 1831
    while not check(p, scanner::TokenKind::RParen) {
1836 1832
        let param = try parseFnParam(p);
1837 1833
        params.append(param, p.allocator);
1838 1834
1868 1864
1869 1865
    if consume(p, scanner::TokenKind::Semicolon) {
1870 1866
        if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) {
1871 1867
            // Keep existing attributes unchanged.
1872 1868
        } else {
1873 -
            let mut list = ast::nodeSlice(&mut *p.arena, 4);
1869 +
            let mut list = ast::nodeSlice(p.arena, 4);
1874 1870
            if let a = attrs {
1875 1871
                for i in 0..a.list.len {
1876 1872
                    list.append(a.list[i], p.allocator);
1877 1873
                }
1878 1874
            }
2202 2198
        ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable,
2203 2199
    }));
2204 2200
}
2205 2201
2206 2202
/// Parse a module from source text using the provided arena for node storage.
2207 -
export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node
2203 +
export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *unsafe mut strings::Pool) -> *mut ast::Node
2208 2204
    throws (ParseError)
2209 2205
{
2210 2206
    let mut p = mkParser(sourceLoc, input, arena, pool);
2211 2207
    return try parseModule(&mut p) catch {
2212 2208
        printErrors(&p);
2221 2217
export unsafe fn parseModule(p: &mut Parser) -> *mut ast::Node
2222 2218
    throws (ParseError)
2223 2219
{
2224 2220
    advance(p); // Set the parser up with a first token.
2225 2221
2226 -
    let mut blk = mkBlock(p, 512);
2227 -
    try parseStmtsUntil(p, scanner::TokenKind::Eof, &mut blk);
2222 +
    let statements = try parseStmtsUntil(p, scanner::TokenKind::Eof, 512);
2223 +
    let blk = ast::Block { statements, isUnsafe: false };
2228 2224
    consume(p, scanner::TokenKind::Eof);
2229 2225
2230 2226
    return node(p, ast::NodeValue::Block(blk));
2231 2227
}
2232 2228
2233 2229
/// Consume a token of the given kind if present.
2234 -
export fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool {
2230 +
export unsafe fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool {
2235 2231
    if check(p, kind) {
2236 2232
        advance(p);
2237 2233
        return true;
2238 2234
    }
2239 2235
    return false;
2240 2236
}
2241 2237
2242 2238
/// Expect a token of the given kind or report an error.
2243 -
export fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8]
2239 +
export unsafe fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8]
2244 2240
    throws (ParseError)
2245 2241
{
2246 2242
    if not consume(p, kind) {
2247 2243
        let token = p.current;
2248 2244
        reportError(p, token, message);
2272 2268
    try expect(p, scanner::TokenKind::Trait, "expected `trait`");
2273 2269
    let name = try parseIdent(p, "expected trait name");
2274 2270
    let supertraits = try parseDerives(p);
2275 2271
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name");
2276 2272
2277 -
    let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS);
2273 +
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2278 2274
    while not check(p, scanner::TokenKind::RBrace) and
2279 2275
          not check(p, scanner::TokenKind::Eof)
2280 2276
    {
2281 2277
        let method = try parseTraitMethodSig(p);
2282 2278
        methods.append(method, p.allocator);
2318 2314
    let traitName = try parseTypePath(p);
2319 2315
    try expect(p, scanner::TokenKind::For, "expected `for` after trait name");
2320 2316
    let targetType = try parseTypePath(p);
2321 2317
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type");
2322 2318
2323 -
    let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS);
2319 +
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2324 2320
2325 2321
    while not check(p, scanner::TokenKind::RBrace) and
2326 2322
          not check(p, scanner::TokenKind::Eof)
2327 2323
    {
2328 2324
        let attrs = parseAttributes(p);
2367 2363
    open: scanner::TokenKind,
2368 2364
    close: scanner::TokenKind,
2369 2365
    parseItem: unsafe fn (&mut Parser) -> *ast::Node throws (ParseError)
2370 2366
) -> *mut [*ast::Node] throws (ParseError) {
2371 2367
    try expect(p, open, listExpectMessage(open));
2372 -
    let mut items = ast::nodeSlice(&mut *p.arena, 8);
2368 +
    let mut items = ast::nodeSlice(p.arena, 8);
2373 2369
2374 2370
    while not check(p, close) {
2375 2371
        let item = try parseItem(p);
2376 2372
        items.append(item, p.allocator);
2377 2373
lib/std/lang/parser/tests.rad +2 -2
178 178
    try testing::expect(sign == expectedSign);
179 179
}
180 180
181 181
/// Extract a union variant and verify its name and index.
182 182
/// Returns the payload's fields slice for further checking with `expectField`.
183 -
fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> ?*mut [*ast::Node]
183 +
fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> ?*[*ast::Node]
184 184
    throws (testing::TestError)
185 185
{
186 186
    let case ast::NodeValue::UnionDeclVariant(v) = varNode.value
187 187
        else throw testing::TestError::Failed;
188 188
    try expectIdent(v.name, name);
198 198
    return fields;
199 199
}
200 200
201 201
/// Check a record field has the expected name and type signature.
202 202
fn expectFieldSig(
203 -
    fields: *mut [*ast::Node], index: u32, name: ?*[u8], sig: ast::TypeSig
203 +
    fields: *[*ast::Node], index: u32, name: ?*[u8], sig: ast::TypeSig
204 204
) throws (testing::TestError) {
205 205
    let fieldNode = fields[index];
206 206
    let case ast::NodeValue::RecordField { field, type: fieldType, .. } = fieldNode.value
207 207
        else throw testing::TestError::Failed;
208 208
lib/std/lang/resolver.rad +368 -265
55 55
/// Trait definition stored in the resolver.
56 56
export record TraitType: Copy {
57 57
    /// Trait name.
58 58
    name: *[u8],
59 59
    /// Method signatures, including from supertraits.
60 -
    methods: *mut [TraitMethod],
60 +
    methods: *unsafe mut [TraitMethod],
61 61
    /// Supertraits that must also be implemented.
62 -
    supertraits: *mut [*TraitType],
62 +
    supertraits: *unsafe mut [*unsafe TraitType],
63 63
}
64 64
65 65
/// A single method signature within a trait.
66 66
export record TraitMethod: Copy {
67 67
    /// Method name.
68 68
    name: *[u8],
69 69
    /// Function type for the method, excluding the receiver.
70 -
    fnType: *mut FnType,
70 +
    fnType: *FnType,
71 71
    /// Whether the receiver is mutable.
72 72
    mutable: bool,
73 73
    /// Pointer-like class used by the receiver.
74 74
    receiverClass: types::PointerClass,
75 75
    /// V-table slot index.
77 77
}
78 78
79 79
/// An entry in the trait instance registry.
80 80
export record InstanceEntry: Copy {
81 81
    /// Trait type descriptor.
82 -
    traitType: *TraitType,
82 +
    traitType: *unsafe TraitType,
83 83
    /// Concrete type that implements the trait.
84 84
    concreteType: Type,
85 85
    /// Name of the concrete type.
86 86
    concreteTypeName: *[u8],
87 87
    /// Module where this instance was declared.
88 88
    moduleId: u16,
89 89
    /// Method symbols for each trait method, in declaration order.
90 -
    methods: *mut [*mut Symbol],
90 +
    methods: *unsafe mut [*unsafe mut Symbol],
91 91
}
92 92
93 93
/// An entry in the method registry.
94 94
export record MethodEntry: Copy {
95 95
    /// Concrete type that owns the method.
97 97
    /// Name of the concrete type.
98 98
    concreteTypeName: *[u8],
99 99
    /// Method name.
100 100
    name: *[u8],
101 101
    /// Function type excluding the receiver.
102 -
    fnType: *mut FnType,
102 +
    fnType: *FnType,
103 103
    /// Whether the receiver is mutable.
104 104
    mutable: bool,
105 105
    /// Pointer-like class used by the receiver.
106 106
    receiverClass: types::PointerClass,
107 107
    /// Symbol for the method.
108 -
    symbol: *mut Symbol,
108 +
    symbol: *unsafe mut Symbol,
109 109
}
110 110
111 111
/// Identifier for the synthetic `len` field.
112 112
export constant LEN_FIELD: *[u8] = "len";
113 113
/// Identifier for the synthetic `ptr` field.
153 153
154 154
/// Information about a union variant.
155 155
record UnionVariant: Copy {
156 156
    name: *[u8],
157 157
    valueType: Type,
158 -
    symbol: *mut Symbol,
158 +
    symbol: *unsafe mut Symbol,
159 159
}
160 160
161 161
/// Array type payload.
162 162
export record ArrayType: Copy {
163 163
    item: *Type,
164 164
    length: u32,
165 165
}
166 166
167 167
/// Record nominal type.
168 168
export record RecordType: Copy {
169 -
    fields: *[RecordField],
169 +
    fields: *unsafe [RecordField],
170 170
    labeled: bool,
171 171
    /// Cached layout.
172 172
    layout: Layout,
173 173
    /// Whether the declaration explicitly carries the `Once` marker.
174 174
    declaredLinear: bool,
176 176
    declaredCopy: bool,
177 177
}
178 178
179 179
/// Union nominal type.
180 180
export record UnionType: Copy {
181 -
    variants: *[UnionVariant],
181 +
    variants: *unsafe [UnionVariant],
182 182
    /// Cached layout.
183 183
    layout: Layout,
184 184
    /// Cached payload offset within the union aggregate.
185 185
    valOffset: u32,
186 186
    /// If all variants have void payloads.
211 211
    /// Wrap return value in success variant of result type.
212 212
    ResultWrap,
213 213
    /// Coerce a concrete pointer to a trait object.
214 214
    TraitObject {
215 215
        /// Trait type information.
216 -
        traitInfo: *TraitType,
216 +
        traitInfo: *unsafe TraitType,
217 217
        /// Instance entry for v-table lookup.
218 218
        inst: *unsafe InstanceEntry,
219 219
    },
220 220
}
221 221
222 222
/// Result of resolving a module path.
223 223
record ResolvedModule: Copy {
224 224
    /// Module entry in the graph.
225 225
    entry: *module::ModuleEntry,
226 226
    /// Scope containing the module's declarations.
227 -
    scope: *mut Scope,
227 +
    scope: *unsafe mut Scope,
228 228
}
229 229
230 230
/// Type layout.
231 231
export record Layout: Copy {
232 232
    /// Size in bytes.
272 272
    },
273 273
}
274 274
275 275
/// Resolved function signature details.
276 276
export record FnType: Copy {
277 +
    /// Parameter types in call order.
277 278
    paramTypes: *[*Type],
279 +
    /// Return value type.
278 280
    returnType: *Type,
281 +
    /// Error types that the function can throw.
279 282
    throwList: *[*Type],
280 283
    /// Whether calling this function requires an unsafe context.
281 284
    isUnsafe: bool,
282 -
    localCount: u32,
283 285
}
284 286
285 287
/// Describes a type computed during semantic analysis.
286 288
export union Type: Copy {
287 289
    /// A type that couldn't be decided.
312 314
    /// Eg. `[i32; 32]`.
313 315
    Array(ArrayType),
314 316
    /// Eg. `?T`.
315 317
    Optional(*Type),
316 318
    /// Eg. `fn id(i32) -> i32`.
317 -
    Fn(*mut FnType),
319 +
    Fn(*FnType),
318 320
    /// Named, ie. user-defined types, includes union variants.
319 -
    Nominal(*NominalType),
321 +
    Nominal(*unsafe NominalType),
320 322
    /// Owning trait object. An erased type with v-table.
321 323
    TraitObject {
322 324
        /// Ownership and safety class.
323 325
        class: types::PointerClass,
324 326
        /// Trait definition.
325 -
        traitInfo: *TraitType,
327 +
        traitInfo: *unsafe TraitType,
326 328
        /// Whether the pointer is mutable.
327 329
        mutable: bool,
328 330
    },
329 331
}
330 332
381 383
    /// Module reference.
382 384
    Module {
383 385
        /// Module entry in the graph.
384 386
        entry: *module::ModuleEntry,
385 387
        /// Module scope.
386 -
        scope: *mut Scope,
388 +
        scope: *unsafe mut Scope,
387 389
    },
388 390
    /// Payload describing type symbols with their resolved type.
389 -
    Type(*mut NominalType),
391 +
    Type(*unsafe mut NominalType),
390 392
    /// Trait symbol.
391 -
    Trait(*mut TraitType),
393 +
    Trait(*unsafe mut TraitType),
392 394
}
393 395
394 396
/// Resolved symbol allocated during semantic analysis.
395 397
export record Symbol: Copy {
396 398
    /// Symbol name in source code.
455 457
    DuplicateBinding(*[u8]),
456 458
    /// Identifier referenced before it was declared.
457 459
    UnresolvedSymbol(*[u8]),
458 460
    /// Attempted to assign to an immutable binding.
459 461
    ImmutableBinding,
462 +
    /// Slice append requires a valid allocator record and callback.
463 +
    InvalidSliceAllocator,
460 464
    /// Expected a compile-time constant expression.
461 465
    ConstExprRequired,
462 466
    /// Symbol arena exhausted while binding identifiers.
463 467
    SymbolOverflow,
464 468
    /// Expression has the wrong type.
635 639
    Internal,
636 640
}
637 641
638 642
/// Diagnostics returned by the analyzer.
639 643
export record Diagnostics: Copy {
640 -
    errors: *mut [Error],
644 +
    /// Immutable errors captured at the end of an analysis operation.
645 +
    errors: *[Error],
646 +
}
647 +
648 +
/// Mutable diagnostic storage owned by a resolver.
649 +
record DiagnosticBuffer {
650 +
    /// Backing entries. Only the prefix below `len` is initialized.
651 +
    entries: *mut [Error],
652 +
    /// Number of recorded errors.
653 +
    len: u32,
641 654
}
642 655
643 656
/// Call context.
644 657
union CallCtx: Copy {
645 658
    /// Normal function call.
649 662
}
650 663
651 664
/// Result of resolving a record literal's type name.
652 665
record ResolvedRecordLitType: Copy {
653 666
    /// The record nominal type to use for field checking.
654 -
    recordType: *NominalType,
667 +
    recordType: *unsafe NominalType,
655 668
    /// The result type of the literal (record type or union type for variants).
656 669
    resultType: Type,
657 670
}
658 671
659 672
/// Result of checking for a `super` path prefix.
660 673
record SuperAccessResult: Copy {
661 -
    scope: *mut Scope,
674 +
    scope: *unsafe mut Scope,
662 675
    child: *ast::Node,
663 676
}
664 677
665 678
/// Node-specific resolver metadata.
666 679
export union NodeExtra: Copy {
679 692
    /// For-loop iteration metadata.
680 693
    ForLoop(ForLoopInfo),
681 694
    /// Trait method call metadata.
682 695
    TraitMethodCall {
683 696
        /// Trait definition.
684 -
        traitInfo: *TraitType,
697 +
        traitInfo: *unsafe TraitType,
685 698
        /// Method index in the v-table.
686 699
        methodIndex: u32,
687 700
    },
688 701
    /// Standalone method call metadata.
689 702
    MethodCall { method: *unsafe MethodEntry },
693 706
    SliceDelete { elemType: *Type },
694 707
}
695 708
696 709
/// Combined resolver metadata for a single AST node.
697 710
export record NodeData: Copy {
711 +
    /// Number of local bindings and internal iteration variables in this function.
712 +
    localCount: u32,
698 713
    /// Resolved type for this node.
699 714
    ty: Type,
700 715
    /// Coercion plan applied to this node.
701 716
    coercion: Coercion,
702 717
    /// Symbol associated with this node.
703 -
    sym: ?*mut Symbol,
718 +
    sym: ?*unsafe mut Symbol,
704 719
    /// Constant value for literal nodes.
705 720
    constValue: ?ConstValue,
706 721
    /// Lexical scope owned by this node.
707 -
    scope: ?*mut Scope,
722 +
    scope: ?*unsafe mut Scope,
708 723
    /// Node-specific extra data.
709 724
    extra: NodeExtra,
710 725
}
711 726
712 727
/// Table storing all resolver metadata indexed by node ID.
713 -
record NodeDataTable: Copy {
728 +
record NodeDataTable {
729 +
    /// Semantic data indexed by AST node ID.
714 730
    entries: *mut [NodeData],
715 731
}
716 732
717 733
/// Lexical scope.
718 734
export record Scope: Copy {
719 735
    /// Owning AST node, or `nil` for the root scope.
720 736
    owner: ?*ast::Node,
721 737
    /// Parent/enclosing scope.
722 -
    parent: ?*mut Scope,
738 +
    parent: ?*unsafe mut Scope,
723 739
    /// Module ID if this is a module scope.
724 740
    moduleId: ?u16,
725 741
    /// Symbols introduced inside the scope, allocated from the arena.
726 -
    symbols: *mut [*mut Symbol],
742 +
    symbols: *unsafe mut [*unsafe mut Symbol],
727 743
    /// Number of live symbols.
728 744
    symbolsLen: u32,
729 745
}
730 746
731 747
/// An object used by the enter and exit functions for module scopes.
733 749
    /// Module root node.
734 750
    root: *ast::Node,
735 751
    /// Module entry in graph.
736 752
    entry: *module::ModuleEntry,
737 753
    /// The newly entered scope.
738 -
    newScope: *mut Scope,
754 +
    newScope: *unsafe mut Scope,
739 755
    /// The previous scope.
740 -
    prevScope: *mut Scope,
756 +
    prevScope: *unsafe mut Scope,
741 757
    /// The previous module.
742 758
    prevMod: u16,
743 759
}
744 760
745 761
/// Loop context for tracking control flow within loops.
798 814
799 815
/// Per-control-flow-path ownership state.
800 816
/// Read only the initialized symbol prefix below `len`.
801 817
record LinearEnv: Copy {
802 818
    /// Symbol pointers. Entries below `len` are initialized and not optional.
803 -
    symbols: [*mut Symbol; MAX_LINEAR_BINDINGS],
819 +
    symbols: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS],
804 820
    /// Bit set for each binding that remains available.
805 821
    available: u64,
806 822
    /// Number of initialized entries in `symbols`.
807 823
    len: u32,
808 824
    /// Whether this control-flow path has terminated.
814 830
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
815 831
record LinearChecker: Copy {
816 832
    /// Resolver that owns the symbols and diagnostics.
817 833
    resolver: *unsafe mut Resolver,
818 834
    /// Source symbols protected by active pattern references.
819 -
    loans: [*mut Symbol; MAX_LINEAR_BINDINGS],
835 +
    loans: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS],
820 836
    /// Number of initialized entries in `loans`.
821 837
    loanLen: u32,
822 838
    /// Binding count at entry to each active loop.
823 839
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
824 840
    /// Available bindings at entry to each active loop.
841 857
    }
842 858
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
843 859
}
844 860
845 861
/// Global resolver state.
846 -
export record Resolver: Copy {
862 +
export record Resolver {
847 863
    /// Current scope.
848 -
    scope: *mut Scope,
864 +
    scope: *unsafe mut Scope,
849 865
    /// Package scope containing package roots and top-level symbols.
850 -
    pkgScope: *mut Scope,
866 +
    pkgScope: *unsafe mut Scope,
851 867
    /// Stack of loop contexts for nested loops.
852 868
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
853 869
    /// Current loop depth, indexes into loop stack.
854 870
    loopDepth: u32,
855 871
    /// Signature of the function currently being analyzed.
856 -
    currentFn: ?*unsafe mut FnType,
872 +
    currentFn: ?FnType,
873 +
    /// Declaration that owns the active function body and its local bindings.
874 +
    currentFnNode: ?*ast::Node,
857 875
    /// Current module being analyzed.
858 876
    currentMod: u16,
859 877
    /// Whether the current lexical context permits unsafe operations.
860 878
    inUnsafeContext: bool,
861 879
    /// Configuration for semantic analysis.
865 883
    /// Combined semantic metadata table indexed by node ID.
866 884
    nodeData: NodeDataTable,
867 885
    /// Linked list of interned types.
868 886
    types: ?*TypeNode,
869 887
    /// Diagnostics recorded so far.
870 -
    errors: *mut [Error],
888 +
    errors: DiagnosticBuffer,
871 889
    /// Module graph for the current package.
872 890
    moduleGraph: *unsafe module::ModuleGraph,
873 891
    /// Cache of module scopes indexed by module ID.
874 -
    moduleScopes: [?*mut Scope; module::MAX_MODULES],
892 +
    moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES],
875 893
    /// Trait instance registry.
876 894
    instances: [InstanceEntry; MAX_INSTANCES],
877 895
    /// Number of registered instances.
878 896
    instancesLen: u32,
879 897
    /// Standalone method registry.
907 925
    let node = try! alloc::alloc(
908 926
        &mut self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
909 927
    ) as *mut TypeNode;
910 928
911 929
    set *node = TypeNode { ty, next: self.types };
912 -
    set self.types = node;
930 +
    let frozen: *TypeNode = node;
931 +
    set self.types = frozen;
913 932
914 -
    return &node.ty;
933 +
    return &frozen.ty;
915 934
}
916 935
917 936
/// Allocate a nominal type descriptor and return a pointer to it.
918 -
unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType {
937 +
unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *unsafe mut NominalType {
919 938
    // Nb. We don't attempt to de-duplicate nominal type entries,
920 939
    // since they don't carry node information and we create
921 940
    // placeholder entries when binding symbols.
922 -
    let entry = try! alloc::alloc(
941 +
    let entry = try! alloc::allocRaw(
923 942
        &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType)
924 -
    ) as *mut NominalType;
943 +
    ) as *unsafe mut NominalType;
925 944
926 945
    set *entry = info;
927 946
928 947
    return entry;
929 948
}
930 949
931 950
/// Allocate a function type descriptor and return a pointer to it.
932 -
unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *mut FnType {
951 +
unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType {
933 952
    let entry = try! alloc::alloc(
934 953
        &mut self.arena, @sizeOf(FnType), @alignOf(FnType)
935 954
    ) as *mut FnType;
936 955
937 956
    set *entry = info;
938 957
939 958
    return entry;
940 959
}
941 960
942 961
/// Returns an error, if any, associated with the given node.
943 -
fn errorForNode(self: &Resolver, node: *ast::Node) -> ?*Error {
962 +
fn errorForNode(self: &Resolver, node: *ast::Node) -> ?Error {
944 963
    for i in 0..self.errors.len {
945 -
        let err = &self.errors[i];
964 +
        let err = self.errors.entries[i];
946 965
        if err.node == node {
947 966
            return err;
948 967
        }
949 968
    }
950 969
    return nil;
951 970
}
952 971
953 972
/// Storage buffers used by the analyzer.
954 -
export record ResolverStorage: Copy {
973 +
export record ResolverStorage {
955 974
    /// Unified arena for symbols, scopes, and nominal type.
956 975
    arena: alloc::Arena,
957 976
    /// Node semantic metadata indexed by node ID.
958 977
    nodeData: *mut [NodeData],
959 978
    /// Package scope.
960 -
    pkgScope: *mut Scope,
979 +
    pkgScope: *unsafe mut Scope,
961 980
    /// Error storage.
962 981
    errors: *mut [Error],
963 982
}
964 983
965 984
/// Input for resolving a single package.
973 992
/// Construct a resolver with module context and backing storage.
974 993
export unsafe fn resolver(
975 994
    storage: ResolverStorage,
976 995
    config: Config
977 996
) -> Resolver {
978 -
    let mut arena = storage.arena;
979 -
    let symbols = try! alloc::allocSlice(
980 -
        &mut arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), MAX_MODULE_SYMBOLS
981 -
    ) as *mut [*mut Symbol];
997 +
    let case ResolverStorage { arena: initialArena, nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
998 +
    let mut arena = initialArena;
999 +
    let symbols = try! alloc::allocRawSlice(
1000 +
        &mut arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS
1001 +
    ) as *unsafe mut [*unsafe mut Symbol];
982 1002
983 1003
    // Initialize the root scope.
984 1004
    // TODO: Set this up when declaring `PKG_SCOPE`, not here.
985 -
    set *storage.pkgScope = Scope {
1005 +
    set *pkgScope = Scope {
986 1006
        owner: nil,
987 1007
        parent: nil,
988 1008
        moduleId: nil,
989 1009
        symbols,
990 1010
        symbolsLen: 0,
991 1011
    };
992 1012
993 1013
    // Clear all node semantic metadata to sentinel values.
994 1014
    // TODO: Use array repeat literal?
995 -
    for i in 0..storage.nodeData.len {
996 -
        set storage.nodeData[i] = NodeData {
1015 +
    for i in 0..nodeData.len {
1016 +
        set nodeData[i] = NodeData {
1017 +
            localCount: 0,
997 1018
            ty: Type::Unknown,
998 1019
            coercion: Coercion::Identity,
999 1020
            sym: nil,
1000 1021
            constValue: nil,
1001 1022
            scope: nil,
1002 1023
            extra: NodeExtra::None,
1003 1024
        };
1004 1025
    }
1005 1026
1006 -
    let mut moduleScopes: [?*mut Scope; module::MAX_MODULES] = undefined;
1027 +
    let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined;
1007 1028
    // TODO: Simplify.
1008 1029
    for i in 0..moduleScopes.len {
1009 1030
        set moduleScopes[i] = nil;
1010 1031
    }
1011 1032
    return Resolver {
1012 -
        scope: storage.pkgScope,
1013 -
        pkgScope: storage.pkgScope,
1033 +
        scope: pkgScope,
1034 +
        pkgScope: pkgScope,
1014 1035
        loopStack: undefined,
1015 1036
        loopDepth: 0,
1016 1037
        currentFn: nil,
1038 +
        currentFnNode: nil,
1017 1039
        currentMod: 0,
1018 1040
        inUnsafeContext: false,
1019 1041
        config,
1020 1042
        arena,
1021 -
        nodeData: NodeDataTable { entries: storage.nodeData },
1043 +
        nodeData: NodeDataTable { entries: nodeData },
1022 1044
        types: nil,
1023 -
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1045 +
        errors: DiagnosticBuffer { entries: errors, len: 0 },
1024 1046
        // TODO: Shouldn't be undefined.
1025 1047
        moduleGraph: undefined,
1026 1048
        moduleScopes,
1027 1049
        instances: undefined,
1028 1050
        instancesLen: 0,
1029 1051
        methods: undefined,
1030 1052
        methodsLen: 0,
1031 1053
    };
1032 1054
}
1033 1055
1056 +
/// Capture the current errors in an immutable arena allocation.
1057 +
/// The allocation must remain valid while the diagnostics are used.
1058 +
export unsafe fn diagnostics(self: &mut Resolver) -> Diagnostics {
1059 +
    let count = self.errors.len;
1060 +
    let entries = try! alloc::allocSlice(
1061 +
        &mut self.arena, @sizeOf(Error), @alignOf(Error), count
1062 +
    ) as *mut [Error];
1063 +
    for i in 0..self.errors.len {
1064 +
        set entries[i] = self.errors.entries[i];
1065 +
    }
1066 +
    return Diagnostics { errors: entries };
1067 +
}
1068 +
1034 1069
/// Return `true` if there are no errors in the diagnostics.
1035 1070
export fn success(diag: &Diagnostics) -> bool {
1036 1071
    return diag.errors.len == 0;
1037 1072
}
1038 1073
1039 1074
/// Retrieve an error diagnostic by index, if present.
1040 -
export fn errorAt(errs: *[Error], index: u32) -> ?*Error {
1075 +
export fn errorAt(errs: &[Error], index: u32) -> ?Error {
1041 1076
    if index >= errs.len {
1042 1077
        return nil;
1043 1078
    }
1044 -
    return &errs[index];
1079 +
    return errs[index];
1045 1080
}
1046 1081
1047 1082
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1048 -
unsafe fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1083 +
fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1049 1084
    // If our error list is full, just return an error without recording it.
1050 -
    if self.errors.len >= self.errors.cap {
1085 +
    if self.errors.len >= self.errors.entries.len {
1051 1086
        return ResolveError::Failure;
1052 1087
    }
1053 1088
    // Don't record more than one error per node.
1054 1089
    if let n = node; errorForNode(self, n) <> nil {
1055 1090
        return ResolveError::Failure;
1056 1091
    }
1057 1092
    let idx = self.errors.len;
1058 -
    set self.errors = @sliceOf(self.errors.ptr, idx + 1, self.errors.cap);
1059 -
    set self.errors[idx] = Error { kind, node, moduleId: self.currentMod };
1093 +
    set self.errors.entries[idx] = Error { kind, node, moduleId: self.currentMod };
1094 +
    set self.errors.len = idx + 1;
1060 1095
1061 1096
    return ResolveError::Failure;
1062 1097
}
1063 1098
1064 1099
/// Like [`emitError`], but for type mismatches specifically.
1065 1100
unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1066 1101
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1067 1102
}
1068 1103
1069 1104
/// Allocate a scope object with the given symbol capacity.
1070 -
unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1105 +
unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1071 1106
    // Check for an existing scope for this node, and don't allocate a new
1072 1107
    // one in that case.
1073 1108
    if let scope = scopeFor(self, owner) {
1074 1109
        return scope;
1075 1110
    }
1076 1111
    assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds";
1077 -
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(Scope), @alignOf(Scope));
1078 -
    let entry = p as *mut Scope;
1112 +
    let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(Scope), @alignOf(Scope));
1113 +
    let entry = p as *unsafe mut Scope;
1079 1114
1080 1115
    // Allocate symbols from the arena.
1081 -
    let symbols = try! alloc::allocSlice(
1082 -
        &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), capacity
1083 -
    ) as *mut [*mut Symbol];
1116 +
    let symbols = try! alloc::allocRawSlice(
1117 +
        &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity
1118 +
    ) as *unsafe mut [*unsafe mut Symbol];
1084 1119
1085 1120
    set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 };
1086 1121
    set self.nodeData.entries[owner.id].scope = entry;
1087 1122
1088 1123
    return entry;
1089 1124
}
1090 1125
1091 1126
/// Enter a new local scope that is the child of the current scope.
1092 1127
/// This creates a parent/child relationship that means that lookups in the
1093 1128
/// child scope can recurse upwards.
1094 -
export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope {
1129 +
export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *unsafe Scope {
1095 1130
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1096 1131
    set scope.parent = self.scope;
1097 1132
    set self.scope = scope;
1098 1133
    return scope;
1099 1134
}
1113 1148
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1114 1149
}
1115 1150
1116 1151
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1117 1152
unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1118 -
    let modEntry = module::findChild(&*self.moduleGraph, name, self.currentMod)
1153 +
    let modEntry = module::findChild(self.moduleGraph, name, self.currentMod)
1119 1154
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1120 1155
    let modRoot = modEntry.ast
1121 1156
        else panic "enterSubModule: analyzing module that wasn't parsed";
1122 1157
1123 1158
    return enterModuleScope(self, modRoot, modEntry);
1128 1163
    set self.scope = entry.prevScope;
1129 1164
    set self.currentMod = entry.prevMod;
1130 1165
}
1131 1166
1132 1167
/// Exit the most recent scope.
1133 -
export fn exitScope(self: &mut Resolver) {
1168 +
export unsafe fn exitScope(self: &mut Resolver) {
1134 1169
    let parent = self.scope.parent else {
1135 1170
        // TODO: This should be a panic, but one of the tests hits this
1136 1171
        // clause, which might be a bug in the generator.
1137 1172
        return;
1138 1173
    };
1182 1217
        }
1183 1218
    }
1184 1219
}
1185 1220
1186 1221
/// Set the expected return type for a new function body.
1187 -
unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &mut FnType) {
1222 +
unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) {
1188 1223
    assert self.currentFn == nil, "enterFn: already in a function";
1189 -
    set self.currentFn = ty as *unsafe mut FnType;
1224 +
    set self.currentFn = *ty;
1225 +
    set self.currentFnNode = node;
1190 1226
    enterScope(self, node);
1191 1227
}
1192 1228
1193 1229
/// Clear the expected return type when leaving a function body.
1194 -
fn exitFn(self: &mut Resolver) {
1230 +
unsafe fn exitFn(self: &mut Resolver) {
1195 1231
    if self.currentFn == nil {
1196 1232
        // TODO: This should be a panic, but one of the tests hits this
1197 1233
        // clause, which might be a bug in the generator.
1198 1234
        return;
1199 1235
    }
1200 1236
    set self.currentFn = nil;
1237 +
    set self.currentFnNode = nil;
1201 1238
    exitScope(self);
1202 1239
}
1203 1240
1204 1241
/// Extract the identifier text from a node.
1205 1242
unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8]
1209 1246
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1210 1247
    return name;
1211 1248
}
1212 1249
1213 1250
/// Associate a resolved symbol with an AST node.
1214 -
fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *mut Symbol) {
1251 +
fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *unsafe mut Symbol) {
1215 1252
    if let existingSym = self.nodeData.entries[node.id].sym {
1216 1253
        panic "setNodeSymbol: a symbol is already associated with this node";
1217 1254
    }
1218 1255
    set self.nodeData.entries[node.id].sym = symbol;
1219 1256
}
1270 1307
fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
1271 1308
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
1272 1309
}
1273 1310
1274 1311
/// Associate trait method call metadata with a call node.
1275 -
fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) {
1312 +
fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) {
1276 1313
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
1277 1314
}
1278 1315
1279 1316
/// Associate for-loop metadata with a for-loop node.
1280 1317
fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) {
1335 1372
    }
1336 1373
    return false;
1337 1374
}
1338 1375
1339 1376
/// Get the resolver metadata for a node.
1340 -
export fn nodeData(self: &Resolver, node: *ast::Node) -> *NodeData {
1341 -
    return &self.nodeData.entries[node.id];
1377 +
export fn nodeData(self: &Resolver, node: *ast::Node) -> NodeData {
1378 +
    return self.nodeData.entries[node.id];
1342 1379
}
1343 1380
1344 1381
/// Get the type for a node, or `nil` if unknown.
1345 1382
export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type {
1346 1383
    let ty = self.nodeData.entries[node.id].ty;
1349 1386
    }
1350 1387
    return ty;
1351 1388
}
1352 1389
1353 1390
/// Get the scope associated with a node.
1354 -
export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*mut Scope {
1391 +
export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Scope {
1355 1392
    return self.nodeData.entries[node.id].scope;
1356 1393
}
1357 1394
1358 1395
/// Get the symbol bound to a node.
1359 -
export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*mut Symbol {
1396 +
export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Symbol {
1360 1397
    return self.nodeData.entries[node.id].sym;
1361 1398
}
1362 1399
1363 1400
/// Get the coercion plan associated with a node, if any.
1364 1401
export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion {
1368 1405
    }
1369 1406
    return c;
1370 1407
}
1371 1408
1372 1409
/// Get the module ID for a symbol by walking up its scope chain.
1373 -
export fn moduleIdForSymbol(self: &Resolver, sym: *Symbol) -> ?u16 {
1410 +
export unsafe fn moduleIdForSymbol(self: &Resolver, sym: *unsafe Symbol) -> ?u16 {
1374 1411
    // For module-level symbols, return the cached module ID.
1375 1412
    if let id = sym.moduleId {
1376 1413
        return id;
1377 1414
    }
1378 1415
    // For module symbols, return the module ID directly.
1386 1423
    return nil;
1387 1424
}
1388 1425
1389 1426
/// Get the binding node for a variant pattern.
1390 1427
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
1391 -
export fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node {
1428 +
export unsafe fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node {
1392 1429
    let case ast::NodeValue::Call(call) = pattern.value
1393 1430
        else return nil;
1394 1431
    let sym = symbolFor(self, call.callee)
1395 1432
        else return nil;
1396 1433
    let case SymbolData::Variant { .. } = sym.data
1406 1443
    }
1407 1444
    return arg;
1408 1445
}
1409 1446
1410 1447
/// Allocate a new symbol, and return a reference to it.
1411 -
unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1412 -
    let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol;
1448 +
unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol {
1449 +
    let sym = try! alloc::allocRaw(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol;
1413 1450
    set *sym = Symbol { name, data, attrs, node, moduleId: nil };
1414 1451
1415 1452
    return sym;
1416 1453
}
1417 1454
1454 1491
    }
1455 1492
    return b;
1456 1493
}
1457 1494
1458 1495
/// Get the layout of a type.
1459 -
export fn getTypeLayout(ty: Type) -> Layout {
1496 +
export unsafe fn getTypeLayout(ty: Type) -> Layout {
1460 1497
    match ty {
1461 1498
        case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1462 1499
        case Type::Slice { .. }, Type::TraitObject { .. } =>
1463 1500
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1464 1501
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
1476 1513
        }
1477 1514
    }
1478 1515
}
1479 1516
1480 1517
/// Get the layout of a type or value.
1481 -
export fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout {
1518 +
export unsafe fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout {
1482 1519
    let mut layout = getTypeLayout(ty);
1483 1520
    // Check for symbol-specific alignment override.
1484 1521
    if let sym = symbolFor(self, node) {
1485 1522
        if let case SymbolData::Value { alignment, .. } = sym.data {
1486 1523
            if alignment > 0 {
1490 1527
    }
1491 1528
    return layout;
1492 1529
}
1493 1530
1494 1531
/// Get the layout of an array type.
1495 -
export fn getArrayLayout(arr: ArrayType) -> Layout {
1532 +
export unsafe fn getArrayLayout(arr: ArrayType) -> Layout {
1496 1533
    let itemLayout = getTypeLayout(*arr.item);
1497 1534
    return Layout {
1498 1535
        size: itemLayout.size * arr.length,
1499 1536
        alignment: itemLayout.alignment,
1500 1537
    };
1501 1538
}
1502 1539
1503 1540
/// Get the layout of an optional type.
1504 -
export fn getOptionalLayout(inner: Type) -> Layout {
1541 +
export unsafe fn getOptionalLayout(inner: Type) -> Layout {
1505 1542
    // Nullable types use null pointer optimization -- no tag byte needed.
1506 1543
    if isNullableType(inner) {
1507 1544
        return getTypeLayout(inner);
1508 1545
    }
1509 1546
    let innerLayout = getTypeLayout(inner);
1516 1553
        alignment,
1517 1554
    };
1518 1555
}
1519 1556
1520 1557
/// Get the payload offset within an optional aggregate.
1521 -
export fn getOptionalValOffset(inner: Type) -> u32 {
1558 +
export unsafe fn getOptionalValOffset(inner: Type) -> u32 {
1522 1559
    let innerLayout = getTypeLayout(inner);
1523 1560
    return mem::alignUp(1, innerLayout.alignment);
1524 1561
}
1525 1562
1526 1563
/// Check if a type is optional.
1572 1609
        }
1573 1610
    }
1574 1611
}
1575 1612
1576 1613
/// Get the layout of a result aggregate with a tag and the larger payload.
1577 -
export fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout {
1614 +
export unsafe fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout {
1578 1615
    let payloadLayout = getTypeLayout(payload);
1579 1616
    let mut maxSize = payloadLayout.size;
1580 1617
    let mut maxAlign = payloadLayout.alignment;
1581 1618
1582 1619
    for errType in throwList {
1589 1626
        alignment: max(PTR_SIZE, maxAlign),
1590 1627
    };
1591 1628
}
1592 1629
1593 1630
/// Compute the layout for a union given its resolved variants.
1594 -
fn computeUnionLayout(variants: *[UnionVariant]) -> UnionLayoutInfo {
1631 +
unsafe fn computeUnionLayout(variants: *unsafe [UnionVariant]) -> UnionLayoutInfo {
1595 1632
    let tagSize: u32 = 1;
1596 1633
    let mut maxVarSize: u32 = 0;
1597 1634
    let mut maxVarAlign: u32 = 1;
1598 1635
    let mut isAllVoid: bool = true;
1599 1636
1626 1663
    set *iota = tag + 1;
1627 1664
    return tag;
1628 1665
}
1629 1666
1630 1667
/// Check if a type is a union without payloads.
1631 -
export fn isVoidUnion(ty: Type) -> bool {
1668 +
export unsafe fn isVoidUnion(ty: Type) -> bool {
1632 1669
    let case Type::Nominal(NominalType::Union(unionType)) = ty
1633 1670
        else return false;
1634 1671
    return unionType.isAllVoid;
1635 1672
}
1636 1673
1720 1757
        else => {},
1721 1758
    }
1722 1759
}
1723 1760
1724 1761
/// Ensure a nominal type has its body resolved.
1725 -
unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *NominalType, site: *ast::Node)
1762 +
unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *unsafe NominalType, site: *ast::Node)
1726 1763
    throws (ResolveError)
1727 1764
{
1728 1765
    if let case NominalType::Placeholder(declNode) = *tyInfo {
1729 1766
        // When resolving on-demand (e.g. from a child module), switch to the
1730 1767
        // declaring module's scope so field type lookups find the right symbols.
1755 1792
        set self.currentMod = prevMod;
1756 1793
    }
1757 1794
}
1758 1795
1759 1796
/// Check if all elements in a node list are assignable to the target type.
1760 -
unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool {
1797 +
unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *[*ast::Node]) -> bool {
1761 1798
    for itemNode in items {
1762 1799
        let elemTy = typeFor(self, itemNode)
1763 1800
            else return false;
1764 1801
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
1765 1802
            // Do nothing.
1958 1995
    }
1959 1996
    return nil;
1960 1997
}
1961 1998
1962 1999
/// Check if two function type descriptors are structurally equivalent.
1963 -
fn fnTypeEqual(a: &FnType, b: *FnType) -> bool {
2000 +
fn fnTypeEqual(a: &FnType, b: &FnType) -> bool {
1964 2001
    if a.isUnsafe <> b.isUnsafe {
1965 2002
        return false;
1966 2003
    }
1967 2004
    return fnSignatureEqual(a, b);
1968 2005
}
1969 2006
1970 2007
/// Compare parameter, return, and error types of functions.
1971 -
fn fnSignatureEqual(a: &FnType, b: *FnType) -> bool {
2008 +
fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool {
1972 2009
    if a.paramTypes.len <> b.paramTypes.len {
1973 2010
        return false;
1974 2011
    }
1975 2012
    if a.throwList.len <> b.throwList.len {
1976 2013
        return false;
1991 2028
    return true;
1992 2029
}
1993 2030
1994 2031
/// Check if two types are structurally equal.
1995 2032
export fn typesEqual(a: Type, b: Type) -> bool {
2033 +
    // Nominal and trait types compare by descriptor identity.
1996 2034
    if a == b {
1997 2035
        return true;
1998 2036
    }
1999 2037
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
2000 2038
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
2006 2044
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
2007 2045
            else return false;
2008 2046
        return aClass == bClass and aMutable == bMutable
2009 2047
            and typesEqual(*aItem, *bItem);
2010 2048
    }
2011 -
    if let case Type::TraitObject { class: aClass, traitInfo: aTraitInfo, mutable: aMutable } = a {
2012 -
        let case Type::TraitObject { class: bClass, traitInfo: bTraitInfo, mutable: bMutable } = b
2013 -
            else return false;
2014 -
        return aClass == bClass and aMutable == bMutable
2015 -
            and aTraitInfo == bTraitInfo;
2016 -
    }
2017 2049
    match a {
2018 2050
        case Type::Array(aa) => {
2019 2051
            let case Type::Array(ab) = b else return false;
2020 2052
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
2021 2053
        }
2061 2093
        else => return false,
2062 2094
    }
2063 2095
}
2064 2096
2065 2097
/// Return whether `ty` may be duplicated implicitly.
2066 -
export fn isCopy(ty: Type) -> bool {
2098 +
export unsafe fn isCopy(ty: Type) -> bool {
2067 2099
    match ty {
2100 +
        case Type::Pointer { class, mutable, .. } =>
2101 +
            return class == types::PointerClass::Unsafe or not mutable,
2102 +
        case Type::Slice { class, mutable, .. } =>
2103 +
            return class == types::PointerClass::Unsafe or not mutable,
2104 +
        case Type::TraitObject { class, mutable, .. } =>
2105 +
            return class == types::PointerClass::Unsafe or not mutable,
2068 2106
        case Type::Array(array) => return isCopy(*array.item),
2069 2107
        case Type::Optional(inner) => return isCopy(*inner),
2070 2108
        case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy,
2071 2109
        case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy,
2072 2110
        case Type::Nominal(NominalType::Placeholder(_)) => return false,
2073 2111
        else => return true,
2074 2112
    }
2075 2113
}
2076 2114
2077 2115
/// Return whether a type must be consumed exactly once.
2078 -
export fn isLinear(ty: Type) -> bool {
2116 +
export unsafe fn isLinear(ty: Type) -> bool {
2079 2117
    match ty {
2080 2118
        case Type::Array(array) => return isLinear(*array.item),
2081 2119
        case Type::Optional(inner) => return isLinear(*inner),
2082 2120
        case Type::Nominal(NominalType::Record(recInfo)) => {
2083 2121
            if recInfo.declaredLinear {
2104 2142
        else => return false,
2105 2143
    }
2106 2144
}
2107 2145
2108 2146
/// Return whether a by-value use moves `ty`.
2109 -
fn isMoveOnly(ty: Type) -> bool {
2147 +
unsafe fn isMoveOnly(ty: Type) -> bool {
2110 2148
    return not isCopy(ty);
2111 2149
}
2112 2150
2113 2151
/// Return whether `ty` is a direct unsafe pointer-like value.
2114 2152
fn isUnsafePointerType(ty: Type) -> bool {
2119 2157
        else => return false,
2120 2158
    }
2121 2159
}
2122 2160
2123 2161
/// Get the record info from a record type.
2124 -
export fn getRecord(ty: Type) -> ?RecordType {
2162 +
export unsafe fn getRecord(ty: Type) -> ?RecordType {
2125 2163
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
2126 2164
    return recInfo;
2127 2165
}
2128 2166
2129 2167
/// Auto-dereference a type: if it's a pointer, return the target type.
2133 2171
    }
2134 2172
    return ty;
2135 2173
}
2136 2174
2137 2175
/// Get field info for a record-like type (records, slices) by field index.
2138 -
export fn getRecordField(ty: Type, index: u32) -> ?RecordField {
2176 +
export unsafe fn getRecordField(ty: Type, index: u32) -> ?RecordField {
2139 2177
    if let case Type::Slice { class, item, mutable } = ty {
2140 2178
        match index {
2141 2179
            case 0 => return RecordField {
2142 2180
                name: PTR_FIELD,
2143 2181
                fieldType: Type::Pointer { class, target: item, mutable },
2163 2201
    }
2164 2202
    return nil;
2165 2203
}
2166 2204
2167 2205
/// Check if the two types can be compared for equality.
2168 -
fn isComparable(left: Type, right: Type) -> bool {
2206 +
unsafe fn isComparable(left: Type, right: Type) -> bool {
2169 2207
    if left == Type::Unknown or right == Type::Unknown {
2170 2208
        return false;
2171 2209
    }
2172 2210
    if left == right {
2173 2211
        return true;
2239 2277
    self: &mut Resolver,
2240 2278
    name: *[u8],
2241 2279
    owner: *ast::Node,
2242 2280
    data: SymbolData,
2243 2281
    attrs: u32,
2244 -
    scope: *mut Scope
2245 -
) -> *mut Symbol throws (ResolveError) {
2282 +
    scope: *unsafe mut Scope
2283 +
) -> *unsafe mut Symbol throws (ResolveError) {
2246 2284
    let sym = allocSymbol(self, data, name, owner, attrs);
2247 2285
    try addSymbolToScope(self, sym, scope, owner);
2248 2286
    setNodeSymbol(self, owner, sym);
2249 2287
2250 2288
    return sym;
2251 2289
}
2252 2290
2253 2291
/// Add a symbol to the given scope.
2254 -
unsafe fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2292 +
unsafe fn addSymbolToScope(self: &mut Resolver, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) {
2255 2293
    for i in 0..scope.symbolsLen {
2256 2294
        if scope.symbols[i].name == sym.name {
2257 2295
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
2258 2296
        }
2259 2297
    }
2279 2317
    owner: *ast::Node,
2280 2318
    type: Type,
2281 2319
    mutable: bool,
2282 2320
    alignment: u32,
2283 2321
    attrs: u32
2284 -
) -> ?*mut Symbol throws (ResolveError) {
2322 +
) -> ?*unsafe mut Symbol throws (ResolveError) {
2285 2323
    if let case ast::NodeValue::Placeholder = ident.value {
2286 2324
        setNodeType(self, owner, type);
2287 2325
        return nil;
2288 2326
    }
2289 2327
    let name = try nodeName(self, ident);
2292 2330
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
2293 2331
    setNodeType(self, owner, type);
2294 2332
    setNodeType(self, ident, type);
2295 2333
2296 2334
    // Track number of local bindings for lowering stage.
2297 -
    if let mut fnType = self.currentFn {
2298 -
        set fnType.localCount += 1;
2335 +
    if let owner = self.currentFnNode {
2336 +
        set self.nodeData.entries[owner.id].localCount += 1;
2299 2337
    }
2300 2338
    return sym;
2301 2339
}
2302 2340
2303 2341
/// Bind a constant identifier in the current scope.
2306 2344
    ident: *ast::Node,
2307 2345
    owner: *ast::Node,
2308 2346
    type: Type,
2309 2347
    val: ?ConstValue,
2310 2348
    attrs: u32
2311 -
) -> *mut Symbol throws (ResolveError) {
2349 +
) -> *unsafe mut Symbol throws (ResolveError) {
2312 2350
    let name = try nodeName(self, ident);
2313 2351
    let data = SymbolData::Constant { type, value: val };
2314 2352
    let scope = self.scope;
2315 2353
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
2316 2354
    setNodeType(self, owner, type);
2323 2361
/// This is used when declaring modules with `mod` or
2324 2362
/// importing modules with `use`.
2325 2363
unsafe fn bindModuleIdent(
2326 2364
    self: &mut Resolver,
2327 2365
    entry: *module::ModuleEntry,
2328 -
    scope: *mut Scope,
2366 +
    scope: *unsafe mut Scope,
2329 2367
    owner: *ast::Node,
2330 2368
    attrs: u32,
2331 -
    bindingScope: *mut Scope
2332 -
) -> *mut Symbol throws (ResolveError) {
2369 +
    bindingScope: *unsafe mut Scope
2370 +
) -> *unsafe mut Symbol throws (ResolveError) {
2333 2371
    let data = SymbolData::Module { entry, scope };
2334 2372
    let name = entry.name;
2335 2373
2336 2374
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
2337 2375
}
2339 2377
/// Bind a type identifier in the current scope.
2340 2378
unsafe fn bindTypeIdent(
2341 2379
    self: &mut Resolver,
2342 2380
    ident: *ast::Node,
2343 2381
    owner: *ast::Node,
2344 -
    type: *mut NominalType,
2382 +
    type: *unsafe mut NominalType,
2345 2383
    attrs: u32
2346 -
) -> *mut Symbol throws (ResolveError) {
2384 +
) -> *unsafe mut Symbol throws (ResolveError) {
2347 2385
    let name = try nodeName(self, ident);
2348 2386
    let data = SymbolData::Type(type);
2349 2387
    let scope = self.scope;
2350 2388
    return try bindIdent(self, name, owner, data, attrs, scope);
2351 2389
}
2352 2390
2353 2391
/// Predicate that matches any symbol.
2354 -
fn isAnySymbol(_sym: *mut Symbol) -> bool {
2392 +
fn isAnySymbol(_sym: *unsafe mut Symbol) -> bool {
2355 2393
    return true;
2356 2394
}
2357 2395
2358 2396
/// Predicate that matches value or constant symbols.
2359 -
fn isValueSymbol(sym: *mut Symbol) -> bool {
2397 +
unsafe fn isValueSymbol(sym: *unsafe mut Symbol) -> bool {
2360 2398
    if let case SymbolData::Value { .. } = sym.data {
2361 2399
        return true;
2362 2400
    }
2363 2401
    if let case SymbolData::Constant { .. } = sym.data {
2364 2402
        return true;
2365 2403
    }
2366 2404
    return false;
2367 2405
}
2368 2406
2369 2407
/// Predicate that matches type symbols.
2370 -
fn isTypeSymbol(sym: *mut Symbol) -> bool {
2408 +
unsafe fn isTypeSymbol(sym: *unsafe mut Symbol) -> bool {
2371 2409
    if let case SymbolData::Type(_) = sym.data {
2372 2410
        return true;
2373 2411
    }
2374 2412
    return false;
2375 2413
}
2376 2414
2377 2415
/// Find a symbol by name in a specific scope, filtered by a predicate.
2378 -
fn findInScope(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol {
2416 +
unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol {
2379 2417
    for i in 0..scope.symbolsLen {
2380 2418
        let sym = scope.symbols[i];
2381 2419
        if sym.name == name and predicate(sym) {
2382 2420
            return sym;
2383 2421
        }
2384 2422
    }
2385 2423
    return nil;
2386 2424
}
2387 2425
2388 2426
/// Find a symbol by name, traversing scopes upwards, filtered by a predicate.
2389 -
fn findInScopeRecursive(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol {
2427 +
unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol {
2390 2428
    let mut curr = scope;
2391 2429
    loop {
2392 2430
        if let sym = findInScope(curr, name, predicate) {
2393 2431
            return sym;
2394 2432
        }
2400 2438
    }
2401 2439
    return nil;
2402 2440
}
2403 2441
2404 2442
/// Find a symbol by name in a specific scope (matches any symbol kind).
2405 -
export fn findSymbolInScope(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2443 +
export unsafe fn findSymbolInScope(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
2406 2444
    return findInScope(scope, name, isAnySymbol);
2407 2445
}
2408 2446
2409 2447
/// Look up a value symbol by name, searching from the given scope outward.
2410 -
fn findValueSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2448 +
unsafe fn findValueSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
2411 2449
    return findInScopeRecursive(scope, name, isValueSymbol);
2412 2450
}
2413 2451
2414 2452
/// Look up a type symbol by name, searching from the given scope outward.
2415 -
fn findTypeSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2453 +
unsafe fn findTypeSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
2416 2454
    return findInScopeRecursive(scope, name, isTypeSymbol);
2417 2455
}
2418 2456
2419 2457
/// Like `findValueSymbol`, but finds symbols of any kinds.
2420 -
fn findAnySymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2458 +
unsafe fn findAnySymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
2421 2459
    return findInScopeRecursive(scope, name, isAnySymbol);
2422 2460
}
2423 2461
2424 2462
/// Flatten an identifier or scope access chain into an array of name segments.
2425 2463
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
2461 2499
    return out;
2462 2500
}
2463 2501
2464 2502
/// Find the module ID for a given scope by walking up the scope chain until
2465 2503
/// we hit the module's scope.
2466 -
fn findModuleForScope(scope: *Scope) -> ?u16 {
2504 +
unsafe fn findModuleForScope(scope: *unsafe Scope) -> ?u16 {
2467 2505
    let mut s = scope;
2468 2506
    loop {
2469 2507
        if let id = s.moduleId {
2470 2508
            return id;
2471 2509
        }
2477 2515
    }
2478 2516
}
2479 2517
2480 2518
/// Get the parent module scope for the current module.
2481 2519
/// Returns the scope of the parent module, or `nil` if this is a root module.
2482 -
unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) {
2483 -
    let currentMod = module::get(&*self.moduleGraph, self.currentMod)
2520 +
unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) {
2521 +
    let currentMod = module::get(self.moduleGraph, self.currentMod)
2484 2522
        else throw emitError(self, node, ErrorKind::Internal);
2485 2523
    let parentId = currentMod.parent
2486 2524
        else return nil; // No parent module.
2487 2525
2488 2526
    return self.moduleScopes[parentId as u32];
2514 2552
2515 2553
/// Check if a symbol is accessible from the given scope.
2516 2554
/// A symbol is accessible if:
2517 2555
/// * It has the `export` attribute, OR
2518 2556
/// * It's being accessed from within the module where it was defined.
2519 -
fn isSymbolVisible(sym: *Symbol, symScope: *Scope, fromScope: *Scope) -> bool {
2557 +
unsafe fn isSymbolVisible(sym: *unsafe Symbol, symScope: *unsafe Scope, fromScope: *unsafe Scope) -> bool {
2520 2558
    // Public symbols are visible from anywhere.
2521 2559
    if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
2522 2560
        return true;
2523 2561
    }
2524 2562
    // In test mode, @test symbols are visible from anywhere
2537 2575
/// starting from the given scope.
2538 2576
unsafe fn resolveAccess(
2539 2577
    self: &mut Resolver,
2540 2578
    node: *ast::Node,
2541 2579
    access: ast::Access,
2542 -
    scope: *Scope
2543 -
) -> *mut Symbol throws (ResolveError) {
2580 +
    scope: *unsafe Scope
2581 +
) -> *unsafe mut Symbol throws (ResolveError) {
2544 2582
    // Handle `super` access by adjusting scope and node.
2545 2583
    let mut startScope = scope;
2546 2584
    let mut pathNode = node;
2547 2585
    if let superAccess = try checkSuperAccess(self, node) {
2548 2586
        set startScope = superAccess.scope;
2561 2599
unsafe fn resolvePath(
2562 2600
    self: &mut Resolver,
2563 2601
    node: *ast::Node,
2564 2602
    access: ast::Access,
2565 2603
    path: &[*[u8]],
2566 -
    scope: *Scope
2567 -
) -> *mut Symbol throws (ResolveError) {
2604 +
    scope: *unsafe Scope
2605 +
) -> *unsafe mut Symbol throws (ResolveError) {
2568 2606
    assert path.len <> 0, "resolvePath: empty path";
2569 2607
    // Start by finding the root of the path.
2570 2608
    let root = path[0];
2571 2609
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
2572 2610
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2642 2680
/// Recursively resolve the remaining path segments by traversing child modules.
2643 2681
unsafe fn resolveModulePathRecursive(
2644 2682
    self: &mut Resolver,
2645 2683
    node: *ast::Node,
2646 2684
    path: &[*[u8]],
2647 -
    sym: *Symbol
2685 +
    sym: *unsafe Symbol
2648 2686
) -> ResolvedModule throws (ResolveError) {
2649 2687
    let case SymbolData::Module { entry, scope } = sym.data
2650 2688
        else throw emitError(self, node, ErrorKind::Internal);
2651 2689
2652 2690
    if path.len == 0 {
2666 2704
        childSym
2667 2705
    );
2668 2706
}
2669 2707
2670 2708
/// Resolve a type name, which could be an identifier or scoped path.
2671 -
unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) {
2709 +
unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) {
2672 2710
    match node.value {
2673 2711
        case ast::NodeValue::Ident(name) => {
2674 2712
            let sym = findTypeSymbol(self.scope, name)
2675 2713
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2676 2714
            let case SymbolData::Type(ty) = sym.data
3014 3052
    }
3015 3053
    return nil;
3016 3054
}
3017 3055
3018 3056
/// Visit every node contained in a list, returning the last resolved type.
3019 -
unsafe fn visitList(self: &mut Resolver, list: *mut [*ast::Node]) -> Type
3057 +
unsafe fn visitList(self: &mut Resolver, list: *[*ast::Node]) -> Type
3020 3058
    throws (ResolveError)
3021 3059
{
3022 3060
    let mut diverges = false;
3023 3061
    for item in list {
3024 3062
        if try infer(self, item) == Type::Never {
3136 3174
        else => return false,
3137 3175
    }
3138 3176
}
3139 3177
3140 3178
/// Determine whether a node represents a compile-time constant expression.
3141 -
export fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool {
3179 +
export unsafe fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool {
3142 3180
    match node.value {
3143 3181
        case ast::NodeValue::Bool(_),
3144 3182
             ast::NodeValue::Char(_),
3145 3183
             ast::NodeValue::Number(_),
3146 3184
             ast::NodeValue::String(_),
3301 3339
3302 3340
/// Check that constructor arguments match record fields.
3303 3341
///
3304 3342
/// Verifies argument count matches field count, and that each argument is
3305 3343
/// assignable to its corresponding field type.
3306 -
unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType)
3344 +
unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType)
3307 3345
    throws (ResolveError)
3308 3346
{
3309 3347
    try checkRecordArity(self, args, recInfo, node);
3310 3348
    for arg, i in args {
3311 3349
        let fieldType = recInfo.fields[i].fieldType;
3312 3350
        try checkAssignable(self, arg, fieldType);
3313 3351
    }
3314 3352
}
3315 3353
3316 3354
/// Check that the argument count of a constructor pattern or call matches the record field count.
3317 -
unsafe fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3355 +
unsafe fn checkRecordArity(self: &mut Resolver, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3318 3356
    if args.len <> recInfo.fields.len {
3319 3357
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch {
3320 3358
            expected: recInfo.fields.len as u32,
3321 3359
            actual: args.len,
3322 3360
        }));
3384 3422
    let mut fnType = FnType {
3385 3423
        paramTypes: &[],
3386 3424
        returnType: allocType(self, retTy),
3387 3425
        throwList: &[],
3388 3426
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3389 -
        localCount: 0,
3390 3427
    };
3391 3428
    // Enter the function scope to process parameters.
3392 -
    enterFn(self, node, &mut fnType);
3429 +
    enterFn(self, node, &fnType);
3393 3430
3394 3431
    if decl.sig.params.len > MAX_FN_PARAMS {
3395 3432
        exitFn(self);
3396 3433
        throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
3397 3434
            expected: MAX_FN_PARAMS,
3461 3498
3462 3499
/// Resolve a function or method body and restore the enclosing context.
3463 3500
unsafe fn resolveExecutableBody(
3464 3501
    self: &mut Resolver,
3465 3502
    node: *ast::Node,
3466 -
    fnType: *mut FnType,
3503 +
    fnType: *FnType,
3467 3504
    receiverName: ?*ast::Node,
3468 -
    params: *mut [*ast::Node],
3505 +
    params: *[*ast::Node],
3469 3506
    body: *ast::Node,
3470 3507
) throws (ResolveError) {
3471 3508
    let wasUnsafe = self.inUnsafeContext;
3472 3509
    set self.inUnsafeContext = fnType.isUnsafe;
3473 3510
    // Enter function scope.
3489 3526
/// Return whether a required return is missing.
3490 3527
unsafe fn checkExecutableBody(
3491 3528
    self: &mut Resolver,
3492 3529
    fnType: *FnType,
3493 3530
    receiverName: ?*ast::Node,
3494 -
    params: *mut [*ast::Node],
3531 +
    params: *[*ast::Node],
3495 3532
    body: *ast::Node,
3496 3533
) -> bool throws (ResolveError) {
3497 3534
    if let receiver = receiverName {
3498 3535
        // Bind the receiver parameter.
3499 3536
        let receiverTy = *fnType.paramTypes[0];
3530 3567
    /// The declaration permits implicit copies.
3531 3568
    copy: bool,
3532 3569
}
3533 3570
3534 3571
/// Resolve compiler-known ownership markers from a derive list.
3535 -
unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers
3572 +
unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *[*ast::Node]) -> OwnershipMarkers
3536 3573
    throws (ResolveError)
3537 3574
{
3538 3575
    let mut result = OwnershipMarkers { linear: false, copy: false };
3539 3576
    for derive in derives {
3540 3577
        let name = try nodeName(self, derive);
3561 3598
    }
3562 3599
    return result;
3563 3600
}
3564 3601
3565 3602
/// Resolve record fields from a node list.
3566 -
unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3603 +
unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
3567 3604
    throws (ResolveError)
3568 3605
{
3569 3606
    let a = alloc::arenaAllocator(&mut self.arena);
3570 -
    let mut result: *mut [RecordField] = &mut [];
3607 +
    let mut result: *unsafe mut [RecordField] = &mut [];
3571 3608
    let mut currentOffset: u32 = 0;
3572 3609
    let mut maxAlignment: u32 = 1;
3573 3610
3574 3611
    if fields.len > parser::MAX_RECORD_FIELDS {
3575 3612
        throw emitError(self, node, ErrorKind::Internal);
3655 3692
3656 3693
    set *nominalTy = NominalType::Record(recordType);
3657 3694
}
3658 3695
3659 3696
/// Bind a type name.
3660 -
unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3697 +
unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3661 3698
    throws (ResolveError)
3662 3699
{
3663 3700
    let attrMask = resolveAttributes(self, attrs);
3664 3701
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3665 3702
3669 3706
3670 3707
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3671 3708
}
3672 3709
3673 3710
/// Allocate a trait type descriptor and return a pointer to it.
3674 -
unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType {
3675 -
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3676 -
    let entry = p as *mut TraitType;
3711 +
unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *unsafe mut TraitType {
3712 +
    let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3713 +
    let entry = p as *unsafe mut TraitType;
3677 3714
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
3678 3715
3679 3716
    return entry;
3680 3717
}
3681 3718
3682 3719
/// Bind a trait name in the current scope.
3683 -
unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3720 +
unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3684 3721
    throws (ResolveError)
3685 3722
{
3686 3723
    let attrMask = resolveAttributes(self, attrs);
3687 3724
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3688 3725
3697 3734
3698 3735
    return sym;
3699 3736
}
3700 3737
3701 3738
/// Find a trait method by name.
3702 -
export fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod {
3739 +
export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod {
3703 3740
    for i in 0..traitType.methods.len {
3704 3741
        if traitType.methods[i].name == name {
3705 3742
            return &traitType.methods[i];
3706 3743
        }
3707 3744
    }
3708 3745
    return nil;
3709 3746
}
3710 3747
3711 3748
/// Resolve a trait declaration body: supertrait methods, then own methods.
3712 -
unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node])
3749 +
unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
3713 3750
    throws (ResolveError)
3714 3751
{
3715 3752
    let sym = symbolFor(self, node)
3716 3753
        else return;
3717 3754
    let case SymbolData::Trait(traitType) = sym.data
3826 3863
        let fnType = FnType {
3827 3864
            paramTypes: &paramTypes[..],
3828 3865
            returnType: retType,
3829 3866
            throwList: &throwList[..],
3830 3867
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3831 -
            localCount: 0,
3832 3868
        };
3833 3869
        traitType.methods.append(TraitMethod {
3834 3870
            name: methodName,
3835 3871
            fnType: allocFnType(self, fnType),
3836 3872
            mutable,
3842 3878
    }
3843 3879
}
3844 3880
3845 3881
/// Resolve a name path node to a symbol.
3846 3882
/// Used for trait and type references in instance declarations and trait objects.
3847 -
unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *mut Symbol
3883 +
unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *unsafe mut Symbol
3848 3884
    throws (ResolveError)
3849 3885
{
3850 3886
    match node.value {
3851 3887
        case ast::NodeValue::Ident(name) => {
3852 3888
            let sym = findAnySymbol(self.scope, name)
3869 3905
unsafe fn resolveInstanceDecl(
3870 3906
    self: &mut Resolver,
3871 3907
    node: *ast::Node,
3872 3908
    traitName: *ast::Node,
3873 3909
    targetType: *ast::Node,
3874 -
    methods: *mut [*ast::Node]
3910 +
    methods: *[*ast::Node]
3875 3911
) throws (ResolveError) {
3876 3912
    // Look up the trait.
3877 3913
    let traitSym = try resolveNamePath(self, traitName);
3878 3914
    let case SymbolData::Trait(traitInfo) = traitSym.data
3879 3915
        else throw emitError(self, traitName, ErrorKind::Internal);
3896 3932
3897 3933
    // Build the instance entry.
3898 3934
    if self.instancesLen >= MAX_INSTANCES {
3899 3935
        throw emitError(self, node, ErrorKind::Internal);
3900 3936
    }
3901 -
    let methodSlice = try! alloc::allocSlice(
3902 -
        &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), traitInfo.methods.len as u32
3903 -
    ) as *mut [*mut Symbol];
3937 +
    let methodSlice = try! alloc::allocRawSlice(
3938 +
        &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
3939 +
    ) as *unsafe mut [*unsafe mut Symbol];
3904 3940
    let mut entry = InstanceEntry {
3905 3941
        traitType: traitInfo,
3906 3942
        concreteType,
3907 3943
        concreteTypeName: typeSym.name,
3908 3944
        moduleId: self.currentMod,
4023 4059
        let fnType = FnType {
4024 4060
            paramTypes: &paramTypes[..],
4025 4061
            returnType: tm.fnType.returnType,
4026 4062
            throwList: tm.fnType.throwList,
4027 4063
            isUnsafe: tm.fnType.isUnsafe,
4028 -
            localCount: 0,
4029 4064
        };
4030 4065
4031 4066
        // Create a symbol for the instance method without binding it into the
4032 4067
        // module scope. Instance methods are dispatched via v-table, so they
4033 4068
        // must not pollute the enclosing scope.
4071 4106
4072 4107
    setNodeType(self, node, Type::Void);
4073 4108
}
4074 4109
4075 4110
/// Resolve instance method bodies.
4076 -
unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *mut [*ast::Node])
4111 +
unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *[*ast::Node])
4077 4112
    throws (ResolveError)
4078 4113
{
4079 4114
    for methodNode in methods {
4080 4115
        let case ast::NodeValue::MethodDecl {
4081 4116
            name, receiverName, receiverType, sig, body, ..
4197 4232
    let fullFnType = FnType {
4198 4233
        paramTypes: &paramTypes[..],
4199 4234
        returnType: retTypePtr,
4200 4235
        throwList,
4201 4236
        isUnsafe,
4202 -
        localCount: 0,
4203 4237
    };
4204 4238
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
4205 4239
4206 4240
    // Function type excluding receiver, for call arg checking.
4207 4241
    let checkFnType = FnType {
4208 4242
        paramTypes: &paramTypes[1..],
4209 4243
        returnType: retTypePtr,
4210 4244
        throwList,
4211 4245
        isUnsafe,
4212 -
        localCount: 0,
4213 4246
    };
4214 4247
4215 4248
    // Create a symbol for the method without binding it into the module scope.
4216 4249
    let sym = allocSymbol(self, SymbolData::Value {
4217 4250
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4236 4269
    };
4237 4270
    set self.methodsLen += 1;
4238 4271
}
4239 4272
4240 4273
/// Look up an instance entry by trait and concrete type.
4241 -
unsafe fn findInstance(self: &Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
4274 +
unsafe fn findInstance(self: &Resolver, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
4242 4275
    for i in 0..self.instancesLen {
4243 4276
        let entry: *unsafe InstanceEntry = &self.instances[i];
4244 4277
        if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) {
4245 4278
            return entry;
4246 4279
        }
4258 4291
    }
4259 4292
    return nil;
4260 4293
}
4261 4294
4262 4295
/// Look up a standalone method entry by its symbol.
4263 -
export unsafe fn findMethodBySymbol(self: &Resolver, sym: *mut Symbol) -> ?*unsafe MethodEntry {
4296 +
export unsafe fn findMethodBySymbol(self: &Resolver, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry {
4264 4297
    for i in 0..self.methodsLen {
4265 4298
        let entry: *unsafe MethodEntry = &self.methods[i];
4266 4299
        if entry.symbol == sym {
4267 4300
            return entry;
4268 4301
        }
4285 4318
    // do it again.
4286 4319
    if let case NominalType::Union(_) = *nominalTy {
4287 4320
        return;
4288 4321
    }
4289 4322
    let a = alloc::arenaAllocator(&mut self.arena);
4290 -
    let mut variants: *mut [UnionVariant] = &mut [];
4323 +
    let mut variants: *unsafe mut [UnionVariant] = &mut [];
4291 4324
4292 4325
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
4293 4326
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
4294 4327
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4295 4328
    let markers = try resolveOwnershipMarkers(self, decl.derives);
4678 4711
    if let pat = forStmt.index {
4679 4712
        try bindForLoopPattern(self, pat, Type::U32, false);
4680 4713
    }
4681 4714
    // The lowerer always creates at least one internal variable for iteration,
4682 4715
    // even when the binding is a placeholder or no explicit index is given.
4683 -
    if let mut fnType = self.currentFn {
4684 -
        set fnType.localCount += 1;
4716 +
    if let owner = self.currentFnNode {
4717 +
        set self.nodeData.entries[owner.id].localCount += 1;
4685 4718
    }
4686 4719
    try visitLoop(self, forStmt.body);
4687 4720
    exitScope(self);
4688 4721
4689 4722
    try visitOptional(self, forStmt.elseBranch, Type::Void);
4784 4817
4785 4818
/// Check whether any pattern in a case prong matches unconditionally.
4786 4819
/// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies.
4787 4820
/// Note: top-level identifiers in `case` are comparisons, not bindings,
4788 4821
/// so they do not count as wildcards.
4789 -
fn hasWildcardPattern(patterns: *mut [*ast::Node]) -> bool {
4822 +
fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool {
4790 4823
    for pattern in patterns {
4791 4824
        match pattern.value {
4792 4825
            case ast::NodeValue::Placeholder => return true,
4793 4826
            case ast::NodeValue::ArrayLit(items) => {
4794 4827
                if isIrrefutableArrayPattern(items) {
4801 4834
    return false;
4802 4835
}
4803 4836
4804 4837
/// Check whether all elements of an array pattern are irrefutable.
4805 4838
/// Inside array patterns, identifiers are bindings, not comparisons.
4806 -
fn isIrrefutableArrayPattern(items: *mut [*ast::Node]) -> bool {
4839 +
fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool {
4807 4840
    for item in items {
4808 4841
        match item.value {
4809 4842
            case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {}
4810 4843
            case ast::NodeValue::ArrayLit(inner) => {
4811 4844
                if not isIrrefutableArrayPattern(inner) {
5420 5453
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
5421 5454
unsafe fn resolveBuiltinCall(
5422 5455
    self: &mut Resolver,
5423 5456
    node: *ast::Node,
5424 5457
    kind: ast::Builtin,
5425 -
    args: *mut [*ast::Node]
5458 +
    args: *[*ast::Node]
5426 5459
) -> Type throws (ResolveError) {
5427 5460
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
5428 5461
    if kind == ast::Builtin::SliceOf {
5429 5462
        if args.len <> 2 and args.len <> 3 {
5430 5463
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5612 5645
5613 5646
    // Associate return type to call.
5614 5647
    return setNodeType(self, node, *info.returnType);
5615 5648
}
5616 5649
5650 +
/// Check the allocator layout and the callback ABI used by slice append.
5651 +
unsafe fn isSliceAllocator(ty: Type) -> bool {
5652 +
    let case Type::Nominal(NominalType::Record(rec)) = ty else return false;
5653 +
    if rec.fields.len <> 2 or not rec.labeled {
5654 +
        return false;
5655 +
    }
5656 +
    let func = rec.fields[0];
5657 +
    let ctx = rec.fields[1];
5658 +
    let funcName = func.name else return false;
5659 +
    let ctxName = ctx.name else return false;
5660 +
    if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or
5661 +
       func.offset <> 0 or ctx.offset <> 8
5662 +
    {
5663 +
        return false;
5664 +
    }
5665 +
    let case Type::Fn(callback) = func.fieldType else return false;
5666 +
    let case Type::Pointer { target, .. } = ctx.fieldType else return false;
5667 +
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 {
5668 +
        return false;
5669 +
    }
5670 +
    if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or
5671 +
       *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32
5672 +
    {
5673 +
        return false;
5674 +
    }
5675 +
    let case Type::Pointer { class, target: result, mutable } = *callback.returnType
5676 +
        else return false;
5677 +
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
5678 +
}
5679 +
5617 5680
/// Resolve `slice.append(val, allocator)`.
5618 5681
unsafe fn resolveSliceAppend(
5619 5682
    self: &mut Resolver,
5620 5683
    node: *ast::Node,
5621 5684
    parent: *ast::Node,
5622 5685
    parentType: Type,
5623 -
    args: *mut [*ast::Node],
5686 +
    args: *[*ast::Node],
5624 5687
    elemType: *Type,
5625 5688
    mutable: bool
5626 5689
) -> Type throws (ResolveError) {
5627 5690
    if not mutable {
5628 5691
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5633 5696
            actual: args.len as u32,
5634 5697
        }));
5635 5698
    }
5636 5699
    // First argument must be assignable to the element type.
5637 5700
    try checkAssignable(self, args[0], *elemType);
5638 -
    // Second argument: the allocator. We accept any type -- the lowerer
5639 -
    // reads `.func` and `.ctx` at fixed offsets.
5640 -
    try visit(self, args[1], Type::Unknown);
5701 +
    // The allocator stores its callback and context at fixed offsets.
5702 +
    let allocatorTy = try infer(self, args[1]);
5703 +
    if let case Type::Nominal(info) = allocatorTy {
5704 +
        try ensureNominalResolved(self, info, args[1]);
5705 +
    }
5706 +
    if not isSliceAllocator(allocatorTy) {
5707 +
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
5708 +
    }
5641 5709
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
5642 5710
5643 5711
    // Return the parent's type so the caller can rebind:
5644 5712
    return setNodeType(self, node, parentType);
5645 5713
}
5647 5715
/// Resolve `slice.delete(index)`.
5648 5716
unsafe fn resolveSliceDelete(
5649 5717
    self: &mut Resolver,
5650 5718
    node: *ast::Node,
5651 5719
    parent: *ast::Node,
5652 -
    args: *mut [*ast::Node],
5720 +
    args: *[*ast::Node],
5653 5721
    elemType: *Type,
5654 5722
    mutable: bool
5655 5723
) -> Type throws (ResolveError) {
5656 5724
    if not mutable {
5657 5725
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5835 5903
    }
5836 5904
    return nil;
5837 5905
}
5838 5906
5839 5907
/// Analyze a union constructor call with payload.
5840 -
unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type
5908 +
unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
5841 5909
    throws (ResolveError)
5842 5910
{
5843 5911
    // Get the union nominal type.
5844 5912
    let case NominalType::Union(unionType) = *unionNominal
5845 5913
        else panic "resolveUnionConstructorCall: not a union type";
5870 5938
/// Analyze an unlabeled record constructor call.
5871 5939
///
5872 5940
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
5873 5941
/// number of arguments matches the record's field count and that each argument
5874 5942
/// is assignable to its corresponding field type.
5875 -
unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type
5943 +
unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
5876 5944
    throws (ResolveError)
5877 5945
{
5878 5946
    let case NominalType::Record(recInfo) = *recordType
5879 5947
        else panic "resolveRecordConstructorCall: not a record type";
5880 5948
6044 6112
    }
6045 6113
    return setNodeType(self, node, innerHint);
6046 6114
}
6047 6115
6048 6116
/// Analyze an array literal expression.
6049 -
unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type
6117 +
unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
6050 6118
    throws (ResolveError)
6051 6119
{
6052 6120
    let length = items.len;
6053 6121
    let mut expectedTy: Type = Type::Unknown;
6054 6122
6103 6171
    self: &mut Resolver,
6104 6172
    node: *ast::Node,
6105 6173
    access: ast::Access,
6106 6174
    unionType: UnionType,
6107 6175
    variantName: *[u8]
6108 -
) -> *mut Symbol throws (ResolveError) {
6176 +
) -> *unsafe mut Symbol throws (ResolveError) {
6109 6177
    // Look up the variant in the union's nominal type.
6110 6178
    for i in 0..unionType.variants.len {
6111 6179
        let variant = &unionType.variants[i];
6112 6180
        if variant.name == variantName {
6113 6181
            let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data
6351 6419
        }
6352 6420
    }
6353 6421
}
6354 6422
6355 6423
/// Return the storage class of an addressed location.
6356 -
fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass {
6424 +
unsafe fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass {
6357 6425
    match node.value {
6358 6426
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
6359 6427
            if let sym = symbolFor(self, node) {
6360 6428
                match sym.node.value {
6361 6429
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
6545 6613
    }
6546 6614
    return false;
6547 6615
}
6548 6616
6549 6617
/// Check if an `as` cast between two types is valid.
6550 -
fn isValidCast(source: Type, target: Type) -> bool {
6618 +
unsafe fn isValidCast(source: Type, target: Type) -> bool {
6551 6619
    // Allow identity casts.
6552 6620
    if source == target {
6553 6621
        return true;
6554 6622
    }
6555 6623
    // Allow numeric to numeric.
6796 6864
/// body and returns the result type. Multi-error callees with inferred bindings
6797 6865
/// are rejected; you must use typed catches.
6798 6866
unsafe fn resolveTryCatches(
6799 6867
    self: &mut Resolver,
6800 6868
    node: *ast::Node,
6801 -
    catches: *mut [*ast::Node],
6869 +
    catches: *[*ast::Node],
6802 6870
    calleeInfo: *FnType,
6803 6871
    resultTy: Type,
6804 6872
    hint: Type
6805 6873
) -> Type throws (ResolveError) {
6806 6874
    let firstNode = catches[0];
6836 6904
/// Validates that each type annotation is in the callee's throw list, that
6837 6905
/// there are no duplicate catch types, and that the clauses are exhaustive.
6838 6906
unsafe fn resolveTypedCatches(
6839 6907
    self: &mut Resolver,
6840 6908
    node: *ast::Node,
6841 -
    catches: *mut [*ast::Node],
6909 +
    catches: *[*ast::Node],
6842 6910
    calleeInfo: *FnType,
6843 6911
    resultTy: Type,
6844 6912
    hint: Type
6845 6913
) -> Type throws (ResolveError) {
6846 6914
    // Track which of the callee's throw types have been covered.
7364 7432
            let fnType = FnType {
7365 7433
                paramTypes: &paramTypes[..],
7366 7434
                returnType: retType,
7367 7435
                throwList: &throwList[..],
7368 7436
                isUnsafe,
7369 -
                localCount: 0,
7370 7437
            };
7371 7438
            return Type::Fn(allocFnType(self, fnType));
7372 7439
        }
7373 7440
        // Resolve an opaque trait object signature.
7374 7441
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
7406 7473
7407 7474
    let case ast::NodeValue::Block(block) = module.modBody.value
7408 7475
        else panic "resolveExpr: expected block for module body";
7409 7476
    enterScope(self, module.modBody);
7410 7477
    try resolveModuleDecls(self, &block) catch {
7411 -
        return Diagnostics { errors: self.errors };
7478 +
        return diagnostics(self);
7412 7479
    };
7413 7480
    try resolveModuleDefs(self, &block) catch {
7414 -
        return Diagnostics { errors: self.errors };
7481 +
        return diagnostics(self);
7415 7482
    };
7416 7483
    exitScope(self);
7417 7484
7418 -
    return Diagnostics { errors: self.errors };
7485 +
    return diagnostics(self);
7419 7486
}
7420 7487
7421 7488
/// Analyze a parsed module root, ie. a block of top-level statements.
7422 7489
export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7423 7490
    let case ast::NodeValue::Block(block) = root.value
7424 7491
        else panic "resolveModuleRoot: expected block for module root";
7425 7492
7426 7493
    enterScope(self, root);
7427 7494
    try resolveModuleDecls(self, &block) catch {
7428 -
        return Diagnostics { errors: self.errors };
7495 +
        return diagnostics(self);
7429 7496
    };
7430 7497
    try resolveModuleDefs(self, &block) catch {
7431 -
        return Diagnostics { errors: self.errors };
7498 +
        return diagnostics(self);
7432 7499
    };
7433 7500
    exitScope(self);
7434 7501
    setNodeType(self, root, Type::Void);
7435 7502
7436 -
    return Diagnostics { errors: self.errors };
7503 +
    return diagnostics(self);
7437 7504
}
7438 7505
7439 7506
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
7440 7507
/// and scopes for them, and also binds type names in each module so that cross-module
7441 7508
/// type references work regardless of declaration order.
7557 7624
        try visitDecl(res, stmt);
7558 7625
    }
7559 7626
}
7560 7627
7561 7628
/// Find a tracked binding by symbol identity.
7562 -
fn findLinearBinding(env: &LinearEnv, sym: *mut Symbol) -> ?u32 {
7629 +
unsafe fn findLinearBinding(env: &LinearEnv, sym: *unsafe mut Symbol) -> ?u32 {
7563 7630
    for i in 0..env.len {
7564 7631
        if env.symbols[i] == sym {
7565 7632
            return i;
7566 7633
        }
7567 7634
    }
7575 7642
7576 7643
/// Add a local binding when its resolved type moves by value.
7577 7644
unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node)
7578 7645
    throws (ResolveError)
7579 7646
{
7580 -
    let sym = symbolFor(&mut *checker.resolver, node) else return;
7647 +
    let sym = symbolFor(checker.resolver, node) else return;
7581 7648
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7582 7649
    if not isMoveOnly(ty) {
7583 7650
        return;
7584 7651
    }
7585 7652
    if env.len >= MAX_LINEAR_BINDINGS {
7586 -
        throw emitError(&mut *checker.resolver, node, ErrorKind::Internal);
7653 +
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7587 7654
    }
7588 7655
    set env.symbols[env.len] = sym;
7589 7656
    set env.available |= (1 as u64) << (env.len as u64);
7590 7657
    set env.len += 1;
7591 7658
}
7592 7659
7593 7660
/// Mark a tracked binding as uninitialized.
7594 -
fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) {
7661 +
unsafe fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) {
7595 7662
    let sym = symbolFor(self, node) else return;
7596 7663
    let index = findLinearBinding(env, sym) else return;
7597 7664
    set env.available &= ~((1 as u64) << (index as u64));
7598 7665
}
7599 7666
7609 7676
                let sym = env.symbols[i];
7610 7677
                let case SymbolData::Value { type: ty, .. } = sym.data
7611 7678
                    else panic "finishLinearScope: expected value symbol";
7612 7679
                if isLinear(ty) {
7613 7680
                    throw emitError(
7614 -
                        &mut *checker.resolver,
7681 +
                        checker.resolver,
7615 7682
                        sym.node,
7616 7683
                        ErrorKind::LinearNotConsumed(sym.name),
7617 7684
                    );
7618 7685
                }
7619 7686
            }
7620 7687
        }
7621 7688
    }
7622 7689
    set env.len = start;
7623 7690
}
7624 7691
7625 -
/// Move or consume a tracked identifier once.
7626 -
unsafe fn consumeLinearIdent(
7692 +
/// Require a tracked identifier to remain available for any access.
7693 +
unsafe fn checkLinearIdent(
7627 7694
    checker: &mut LinearChecker,
7628 7695
    env: &mut LinearEnv,
7629 7696
    node: *ast::Node,
7630 7697
) throws (ResolveError) {
7631 -
    let sym = symbolFor(&mut *checker.resolver, node) else return;
7698 +
    let sym = symbolFor(checker.resolver, node) else return;
7632 7699
    let index = findLinearBinding(env, sym) else return;
7633 7700
    if not linearBindingAvailable(env, index) {
7634 7701
        let case SymbolData::Value { type: ty, .. } = sym.data
7635 7702
            else panic "consumeLinearIdent: expected value symbol";
7636 7703
        let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty)
7637 7704
            else ErrorKind::AffineUseAfterMove(sym.name);
7638 -
        throw emitError(&mut *checker.resolver, node, kind);
7705 +
        throw emitError(checker.resolver, node, kind);
7639 7706
    }
7707 +
}
7708 +
7709 +
/// Move or consume a tracked identifier once.
7710 +
unsafe fn consumeLinearIdent(
7711 +
    checker: &mut LinearChecker,
7712 +
    env: &mut LinearEnv,
7713 +
    node: *ast::Node,
7714 +
) throws (ResolveError) {
7715 +
    try checkLinearIdent(checker, env, node);
7716 +
    let sym = symbolFor(checker.resolver, node) else return;
7717 +
    let index = findLinearBinding(env, sym) else return;
7640 7718
    set env.available &= ~((1 as u64) << (index as u64));
7641 7719
}
7642 7720
7643 7721
/// Merge ownership availability across two live branches.
7644 7722
/// Validate both inputs before writing to an output that can alias either input.
7669 7747
            let sym = left.symbols[i];
7670 7748
            let case SymbolData::Value { type: ty, .. } = sym.data
7671 7749
                else panic "joinLinearBranches: expected value symbol";
7672 7750
            if isLinear(ty) {
7673 7751
                throw emitError(
7674 -
                    &mut *checker.resolver,
7752 +
                    checker.resolver,
7675 7753
                    node,
7676 7754
                    ErrorKind::LinearBranchMismatch(sym.name),
7677 7755
                );
7678 7756
            }
7679 7757
            set available &= ~((1 as u64) << (i as u64));
7693 7771
            let sym = env.symbols[i];
7694 7772
            let case SymbolData::Value { type: ty, .. } = sym.data
7695 7773
                else panic "finishLinearExit: expected value symbol";
7696 7774
            if isLinear(ty) {
7697 7775
                throw emitError(
7698 -
                    &mut *checker.resolver,
7776 +
                    checker.resolver,
7699 7777
                    sym.node,
7700 7778
                    ErrorKind::LinearNotConsumed(sym.name),
7701 7779
                );
7702 7780
            }
7703 7781
        }
7704 7782
    }
7705 7783
    set env.terminated = true;
7706 7784
}
7707 7785
7708 7786
/// Find the local root borrowed or consumed by an argument expression.
7709 -
fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7787 +
fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Symbol {
7710 7788
    match node.value {
7711 7789
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) =>
7712 7790
            return symbolFor(self, node),
7713 7791
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
7714 7792
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7723 7801
7724 7802
/// Protect a pattern source until its reference bindings leave scope.
7725 7803
unsafe fn addPatternLoan(checker: &mut LinearChecker, subject: *ast::Node)
7726 7804
    throws (ResolveError)
7727 7805
{
7728 -
    let root = linearRootSymbol(&mut *checker.resolver, subject) else return;
7806 +
    let root = linearRootSymbol(checker.resolver, subject) else return;
7729 7807
    if checker.loanLen >= MAX_LINEAR_BINDINGS {
7730 -
        throw emitError(&mut *checker.resolver, subject, ErrorKind::Internal);
7808 +
        throw emitError(checker.resolver, subject, ErrorKind::Internal);
7731 7809
    }
7732 7810
    set checker.loans[checker.loanLen] = root;
7733 7811
    set checker.loanLen += 1;
7734 7812
}
7735 7813
7736 7814
/// Reject a write, mutable loan, or ownership transfer of a pattern source.
7737 7815
unsafe fn checkPatternLoan(checker: &mut LinearChecker, node: *ast::Node)
7738 7816
    throws (ResolveError)
7739 7817
{
7740 -
    let root = linearRootSymbol(&mut *checker.resolver, node) else return;
7818 +
    let root = linearRootSymbol(checker.resolver, node) else return;
7741 7819
    for i in 0..checker.loanLen {
7742 7820
        if checker.loans[i] == root {
7743 -
            throw emitError(&mut *checker.resolver, node, ErrorKind::BorrowConflict(root.name));
7821 +
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
7744 7822
        }
7745 7823
    }
7746 7824
}
7747 7825
7748 7826
/// Return whether a parameter can mutate or consume its argument's storage.
7749 -
fn isExclusiveArgument(ty: Type) -> bool {
7827 +
unsafe fn isExclusiveArgument(ty: Type) -> bool {
7750 7828
    match ty {
7751 7829
        case Type::Pointer { mutable, .. } => return mutable,
7752 7830
        case Type::Slice { mutable, .. } => return mutable,
7753 7831
        case Type::TraitObject { mutable, .. } => return mutable,
7754 7832
        else => return isMoveOnly(ty),
7764 7842
) -> bool throws (ResolveError) {
7765 7843
    let mut hasReferences = false;
7766 7844
    match pattern.value {
7767 7845
        case ast::NodeValue::Ident(_) => {
7768 7846
            try addLinearBinding(checker, env, pattern);
7769 -
            if let ty = typeFor(&mut *checker.resolver, pattern) {
7847 +
            if let ty = typeFor(checker.resolver, pattern) {
7770 7848
                return isRefType(ty);
7771 7849
            }
7772 7850
        }
7773 7851
        case ast::NodeValue::Call(call) => {
7774 7852
            for arg in call.args {
7845 7923
    for i in 0..mark {
7846 7924
        let bit = (1 as u64) << (i as u64);
7847 7925
        if (env.available & bit) <> (entryAvailable & bit) {
7848 7926
            let sym = env.symbols[i];
7849 7927
            throw emitError(
7850 -
                &mut *checker.resolver,
7928 +
                checker.resolver,
7851 7929
                node,
7852 7930
                ErrorKind::LinearBranchMismatch(sym.name),
7853 7931
            );
7854 7932
        }
7855 7933
    }
7877 7955
        for i in 0..mark {
7878 7956
            let bit = (1 as u64) << (i as u64);
7879 7957
            if (env.available & bit) <> (expected & bit) {
7880 7958
                let sym = env.symbols[i];
7881 7959
                throw emitError(
7882 -
                    &mut *checker.resolver,
7960 +
                    checker.resolver,
7883 7961
                    node,
7884 7962
                    ErrorKind::LinearBranchMismatch(sym.name),
7885 7963
                );
7886 7964
            }
7887 7965
        }
7930 8008
    let mut elseEnv = base;
7931 8009
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7932 8010
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7933 8011
}
7934 8012
8013 +
/// Pointer patterns borrow their subject; value patterns consume it.
8014 +
unsafe fn patternSubjectUse(self: &Resolver, subject: *ast::Node) -> LinearUse {
8015 +
    if let ty = typeFor(self, subject) {
8016 +
        if let case Type::Pointer { .. } = ty {
8017 +
            return LinearUse::Borrow;
8018 +
        }
8019 +
    }
8020 +
    return LinearUse::Consume;
8021 +
}
8022 +
7935 8023
/// Check a match expression, including ownership transferred into patterns.
7936 8024
unsafe fn checkLinearMatch(
7937 8025
    checker: &mut LinearChecker,
7938 8026
    env: &mut LinearEnv,
7939 8027
    node: *ast::Node,
7940 8028
    matchExpr: ast::Match,
7941 8029
) throws (ResolveError) {
7942 -
    try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume);
8030 +
    try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject));
7943 8031
    let base = *env;
7944 8032
    let mut haveResult = false;
7945 8033
    let mut result = base;
7946 8034
    for prongNode in matchExpr.prongs {
7947 8035
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
7968 8056
            for i in bindingsStart..branch.len {
7969 8057
                let sym = branch.symbols[i];
7970 8058
                let case SymbolData::Value { type: ty, .. } = sym.data
7971 8059
                    else panic "checkLinearMatch: expected value symbol";
7972 8060
                if isLinear(ty) {
7973 -
                    throw emitError(&mut *checker.resolver, prongNode, ErrorKind::LinearDiscard);
8061 +
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7974 8062
                }
7975 8063
            }
7976 8064
        }
7977 8065
        if let guard = prong.guard {
7978 8066
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
8005 8093
    match checker.resolver.nodeData.entries[node.id].extra {
8006 8094
        case NodeExtra::TraitMethodCall { traitInfo, methodIndex } =>
8007 8095
            set fnInfo = traitInfo.methods[methodIndex].fnType,
8008 8096
        case NodeExtra::MethodCall { method } => set fnInfo = method.fnType,
8009 8097
        else => {
8010 -
            if let calleeTy = typeFor(&mut *checker.resolver, call.callee) {
8098 +
            if let calleeTy = typeFor(checker.resolver, call.callee) {
8011 8099
                if let case Type::Fn(info) = calleeTy {
8012 8100
                    set fnInfo = info;
8013 8101
                }
8014 8102
            }
8015 8103
        }
8018 8106
        for arg in call.args {
8019 8107
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
8020 8108
        }
8021 8109
        return;
8022 8110
    };
8023 -
    let mut roots: [?*mut Symbol; MAX_FN_PARAMS + 1] = undefined;
8111 +
    let mut roots: [?*unsafe mut Symbol; MAX_FN_PARAMS + 1] = undefined;
8024 8112
    let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined;
8025 8113
    let mut rootsLen: u32 = 0;
8026 8114
8027 8115
    // Method function types exclude their implicit receiver. Account for it
8028 8116
    // explicitly so owning receivers are consumed and reference receivers
8048 8136
        if haveReceiver {
8049 8137
            if receiverMutable or receiverClass == types::PointerClass::Owned {
8050 8138
                try checkPatternLoan(checker, access.parent);
8051 8139
            }
8052 8140
            if receiverClass <> types::PointerClass::Unsafe {
8053 -
                let root = linearRootSymbol(&mut *checker.resolver, access.parent);
8141 +
                let root = linearRootSymbol(checker.resolver, access.parent);
8054 8142
                if let rootSym = root {
8055 8143
                    set roots[rootsLen] = rootSym;
8056 8144
                    set exclusive[rootsLen] =
8057 8145
                        receiverClass == types::PointerClass::Owned or receiverMutable;
8058 8146
                    set rootsLen += 1;
8069 8157
    for arg, i in call.args {
8070 8158
        let expected = *info.paramTypes[i];
8071 8159
        if isExclusiveArgument(expected) {
8072 8160
            try checkPatternLoan(checker, arg);
8073 8161
        }
8074 -
        let root = linearRootSymbol(&mut *checker.resolver, arg);
8162 +
        let root = linearRootSymbol(checker.resolver, arg);
8075 8163
        let mut argExclusive = isMoveOnly(expected);
8076 8164
        if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected {
8077 8165
            set argExclusive = mutable;
8078 8166
        } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected {
8079 8167
            set argExclusive = mutable;
8086 8174
            if let rootSym = root {
8087 8175
                for j in 0..rootsLen {
8088 8176
                    if let previous = roots[j] {
8089 8177
                        if previous == rootSym and (exclusive[j] or argExclusive) {
8090 8178
                            throw emitError(
8091 -
                                &mut *checker.resolver,
8179 +
                                checker.resolver,
8092 8180
                                arg,
8093 8181
                                ErrorKind::BorrowConflict(rootSym.name),
8094 8182
                            );
8095 8183
                        }
8096 8184
                    }
8113 8201
    checker: &mut LinearChecker,
8114 8202
    env: &mut LinearEnv,
8115 8203
    node: *ast::Node,
8116 8204
    conditional: ast::IfLet,
8117 8205
) throws (ResolveError) {
8118 -
    if let subjectTy = typeFor(&mut *checker.resolver, conditional.pattern.scrutinee);
8206 +
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
8119 8207
        isLinear(subjectTy)
8120 8208
    {
8121 8209
        throw emitError(
8122 -
            &mut *checker.resolver,
8210 +
            checker.resolver,
8123 8211
            conditional.pattern.scrutinee,
8124 8212
            ErrorKind::LinearPartialMove,
8125 8213
        );
8126 8214
    }
8127 8215
    try checkLinearNode(
8128 8216
        checker,
8129 8217
        env,
8130 8218
        conditional.pattern.scrutinee,
8131 -
        LinearUse::Consume,
8219 +
        patternSubjectUse(checker.resolver, conditional.pattern.scrutinee),
8132 8220
    );
8133 8221
    let base = *env;
8134 8222
    let mut thenEnv = base;
8135 8223
    let bindingsStart = thenEnv.len;
8136 8224
    let loanStart = checker.loanLen;
8160 8248
    if env.terminated {
8161 8249
        return;
8162 8250
    }
8163 8251
    match node.value {
8164 8252
        case ast::NodeValue::Ident(_) => {
8253 +
            if usage <> LinearUse::Place {
8254 +
                try checkLinearIdent(checker, env, node);
8255 +
            }
8165 8256
            if usage == LinearUse::Consume {
8166 -
                if let ty = typeFor(&mut *checker.resolver, node); isExclusiveArgument(ty) {
8257 +
                if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) {
8167 8258
                    try checkPatternLoan(checker, node);
8168 8259
                }
8169 8260
                try consumeLinearIdent(checker, env, node);
8170 8261
            }
8171 8262
        }
8172 8263
        case ast::NodeValue::ExprStmt(expr) => {
8173 -
            if let exprTy = typeFor(&mut *checker.resolver, expr) {
8264 +
            if let exprTy = typeFor(checker.resolver, expr) {
8174 8265
                if isLinear(exprTy) {
8175 -
                    throw emitError(&mut *checker.resolver, expr, ErrorKind::LinearDiscard);
8266 +
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
8176 8267
                }
8177 8268
            }
8178 8269
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8179 8270
        }
8180 8271
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
8182 8273
            let mut isUndefined = false;
8183 8274
            if let case ast::NodeValue::Undef = binding.value.value {
8184 8275
                set isUndefined = true;
8185 8276
            }
8186 8277
            if isUndefined {
8187 -
                if let bindingTy = typeFor(&mut *checker.resolver, binding.ident);
8278 +
                if let bindingTy = typeFor(checker.resolver, binding.ident);
8188 8279
                    isLinear(bindingTy)
8189 8280
                {
8190 8281
                    throw emitError(
8191 -
                        &mut *checker.resolver,
8282 +
                        checker.resolver,
8192 8283
                        binding.value,
8193 8284
                        ErrorKind::LinearUndefined,
8194 8285
                    );
8195 8286
                }
8196 8287
            }
8197 8288
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
8198 8289
            try addLinearBinding(checker, env, node);
8199 8290
            if isUndefined {
8200 -
                markLinearBindingUnavailable(&mut *checker.resolver, env, node);
8291 +
                markLinearBindingUnavailable(checker.resolver, env, node);
8201 8292
            }
8202 8293
        }
8203 8294
        case ast::NodeValue::Assign(assign) => {
8204 8295
            try checkPatternLoan(checker, assign.left);
8205 8296
            let mut target: ?u32 = nil;
8206 8297
            let mut targetLinear = false;
8207 -
            if let leftTy = typeFor(&mut *checker.resolver, assign.left) {
8298 +
            if let leftTy = typeFor(checker.resolver, assign.left) {
8208 8299
                if isMoveOnly(leftTy) {
8209 8300
                    set targetLinear = isLinear(leftTy);
8210 8301
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
8211 -
                        if let sym = symbolFor(&mut *checker.resolver, assign.left) {
8302 +
                        if let sym = symbolFor(checker.resolver, assign.left) {
8212 8303
                            set target = findLinearBinding(env, sym);
8213 8304
                        }
8214 8305
                    }
8215 -
                    if target == nil {
8306 +
                    if targetLinear and target == nil {
8216 8307
                        throw emitError(
8217 -
                            &mut *checker.resolver,
8308 +
                            checker.resolver,
8218 8309
                            assign.left,
8219 8310
                            ErrorKind::LinearOverwrite,
8220 8311
                        );
8221 8312
                    }
8222 8313
                }
8224 8315
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
8225 8316
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
8226 8317
            if let index = target {
8227 8318
                if targetLinear and linearBindingAvailable(env, index) {
8228 8319
                    throw emitError(
8229 -
                        &mut *checker.resolver,
8320 +
                        checker.resolver,
8230 8321
                        assign.left,
8231 8322
                        ErrorKind::LinearOverwrite,
8232 8323
                    );
8233 8324
                }
8234 8325
                set env.available |= (1 as u64) << (index as u64);
8240 8331
                try checkPatternLoan(checker, addr.target);
8241 8332
            }
8242 8333
            try checkLinearNode(checker, env, addr.target, LinearUse::Borrow);
8243 8334
        }
8244 8335
        case ast::NodeValue::Deref(target) => {
8245 -
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8336 +
            if let resultTy = typeFor(checker.resolver, node) {
8246 8337
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8247 -
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8338 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8248 8339
                }
8249 8340
            }
8250 8341
            try checkLinearNode(checker, env, target, LinearUse::Observe);
8251 8342
        }
8252 8343
        case ast::NodeValue::FieldAccess(access) => {
8253 -
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8344 +
            if let resultTy = typeFor(checker.resolver, node) {
8254 8345
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8255 -
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8346 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8256 8347
                }
8257 8348
            }
8258 8349
            try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
8259 8350
        }
8260 8351
        case ast::NodeValue::ScopeAccess(_) => {}
8261 8352
        case ast::NodeValue::Subscript { container, index } => {
8262 -
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8353 +
            if let resultTy = typeFor(checker.resolver, node) {
8263 8354
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8264 -
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8355 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8265 8356
                }
8266 8357
            }
8267 8358
            try checkLinearNode(checker, env, container, LinearUse::Observe);
8268 8359
            try checkLinearNode(checker, env, index, LinearUse::Consume);
8269 8360
        }
8278 8369
            for item in items {
8279 8370
                try checkLinearNode(checker, env, item, LinearUse::Consume);
8280 8371
            }
8281 8372
        }
8282 8373
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
8283 -
            if let itemTy = typeFor(&mut *checker.resolver, repeat.item) {
8374 +
            if let itemTy = typeFor(checker.resolver, repeat.item) {
8284 8375
                if not isCopy(itemTy) {
8285 8376
                    throw emitError(
8286 -
                        &mut *checker.resolver,
8377 +
                        checker.resolver,
8287 8378
                        repeat.item,
8288 8379
                        ErrorKind::LinearDiscard,
8289 8380
                    );
8290 8381
                }
8291 8382
            }
8292 8383
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
8293 8384
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
8294 8385
        }
8295 8386
        case ast::NodeValue::BinOp(op) => {
8296 -
            try checkLinearNode(checker, env, op.left, LinearUse::Consume);
8297 -
            try checkLinearNode(checker, env, op.right, LinearUse::Consume);
8387 +
            let mut operandUse = LinearUse::Consume;
8388 +
            match op.op {
8389 +
                case ast::BinaryOp::Eq, ast::BinaryOp::Ne,
8390 +
                     ast::BinaryOp::Lt, ast::BinaryOp::Gt,
8391 +
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
8392 +
                    set operandUse = LinearUse::Observe,
8393 +
                else => {}
8394 +
            }
8395 +
            try checkLinearNode(checker, env, op.left, operandUse);
8396 +
            try checkLinearNode(checker, env, op.right, operandUse);
8298 8397
        }
8299 8398
        case ast::NodeValue::UnOp(op) => {
8300 8399
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
8301 8400
        }
8302 8401
        case ast::NodeValue::As(expr) => {
8303 -
            try checkLinearNode(checker, env, expr.value, LinearUse::Consume);
8402 +
            let mut castUse = usage;
8403 +
            if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) {
8404 +
                set castUse = LinearUse::Observe;
8405 +
            }
8406 +
            try checkLinearNode(checker, env, expr.value, castUse);
8304 8407
        }
8305 8408
        case ast::NodeValue::Range(range) => {
8306 8409
            if let start = range.start {
8307 8410
                try checkLinearNode(checker, env, start, LinearUse::Consume);
8308 8411
            }
8323 8426
        }
8324 8427
        case ast::NodeValue::IfLet(conditional) => {
8325 8428
            try checkLinearIfLet(checker, env, node, conditional);
8326 8429
        }
8327 8430
        case ast::NodeValue::LetElse(binding) => {
8328 -
            if let subjectTy = typeFor(&mut *checker.resolver, binding.pattern.scrutinee);
8431 +
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
8329 8432
                isLinear(subjectTy)
8330 8433
            {
8331 8434
                throw emitError(
8332 -
                    &mut *checker.resolver,
8435 +
                    checker.resolver,
8333 8436
                    binding.pattern.scrutinee,
8334 8437
                    ErrorKind::LinearPartialMove,
8335 8438
                );
8336 8439
            }
8337 8440
            try checkLinearNode(
8338 8441
                checker,
8339 8442
                env,
8340 8443
                binding.pattern.scrutinee,
8341 -
                LinearUse::Consume,
8444 +
                patternSubjectUse(checker.resolver, binding.pattern.scrutinee),
8342 8445
            );
8343 8446
            let base = *env;
8344 8447
            let mut guardedEnv = base;
8345 8448
            if let guard = binding.pattern.guard {
8346 8449
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
8424 8527
                );
8425 8528
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8426 8529
            }
8427 8530
        }
8428 8531
        case ast::NodeValue::WhileLet(whileStmt) => {
8429 -
            if let subjectTy = typeFor(&mut *checker.resolver, whileStmt.pattern.scrutinee);
8532 +
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8430 8533
                isLinear(subjectTy)
8431 8534
            {
8432 8535
                throw emitError(
8433 -
                    &mut *checker.resolver,
8536 +
                    checker.resolver,
8434 8537
                    whileStmt.pattern.scrutinee,
8435 8538
                    ErrorKind::LinearPartialMove,
8436 8539
                );
8437 8540
            }
8438 8541
            let base = *env;
8440 8543
            let mut bodyEnv = base;
8441 8544
            try checkLinearNode(
8442 8545
                checker,
8443 8546
                &mut bodyEnv,
8444 8547
                whileStmt.pattern.scrutinee,
8445 -
                LinearUse::Consume,
8548 +
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
8446 8549
            );
8447 8550
            let mut conditionExit = bodyEnv;
8448 8551
            let start = bodyEnv.len;
8449 8552
            let loanStart = checker.loanLen;
8450 8553
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
8480 8583
                );
8481 8584
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8482 8585
            }
8483 8586
        }
8484 8587
        case ast::NodeValue::For(forStmt) => {
8485 -
            if let iterableTy = typeFor(&mut *checker.resolver, forStmt.iterable) {
8588 +
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8486 8589
                if isLinear(iterableTy) {
8487 8590
                    throw emitError(
8488 -
                        &mut *checker.resolver,
8591 +
                        checker.resolver,
8489 8592
                        forStmt.iterable,
8490 8593
                        ErrorKind::LinearPartialMove,
8491 8594
                    );
8492 8595
                }
8493 8596
            }
8576 8679
8577 8680
/// Check exact-use ownership for one resolved function.
8578 8681
unsafe fn checkLinearFn(
8579 8682
    self: &mut Resolver,
8580 8683
    receiver: ?*ast::Node,
8581 -
    params: *mut [*ast::Node],
8684 +
    params: *[*ast::Node],
8582 8685
    body: *ast::Node,
8583 8686
) throws (ResolveError) {
8584 8687
    let mut checker = LinearChecker {
8585 8688
        resolver: self as *unsafe mut Resolver,
8586 8689
        loans: undefined,
8639 8742
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
8640 8743
        if not success(&diags) {
8641 8744
            return diags;
8642 8745
        }
8643 8746
    }
8644 -
    return Diagnostics { errors: self.errors };
8747 +
    return diagnostics(self);
8645 8748
}
8646 8749
8647 8750
/// Resolve a package.
8648 8751
unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
8649 8752
    let rootId = rootEntry.id;
8658 8761
        else panic "resolvePackage: expected block for module root";
8659 8762
8660 8763
    // Module graph analysis phase: bind all module name symbols and scopes.
8661 8764
    try resolveModuleGraph(self, &block) catch {
8662 8765
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8663 -
        return Diagnostics { errors: self.errors };
8766 +
        return diagnostics(self);
8664 8767
    };
8665 8768
8666 8769
    // Declaration phase: bind all names and analyze top-level declarations.
8667 8770
    try resolveModuleDecls(self, &block) catch {
8668 8771
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8669 8772
    };
8670 8773
    if self.errors.len > 0 {
8671 -
        return Diagnostics { errors: self.errors };
8774 +
        return diagnostics(self);
8672 8775
    }
8673 8776
8674 8777
    // Definition phase: analyze function bodies and sub-module definitions.
8675 8778
    try resolveModuleDefs(self, &block) catch {
8676 8779
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8677 8780
    };
8678 8781
    setNodeType(self, node, Type::Void);
8679 8782
8680 -
    return Diagnostics { errors: self.errors };
8783 +
    return diagnostics(self);
8681 8784
}
lib/std/lang/resolver/printer.rad +10 -7
42 42
        io::print("mut ");
43 43
    }
44 44
}
45 45
46 46
/// Print a resolved type in a textual form.
47 -
fn printType(ty: super::Type) {
47 +
unsafe fn printType(ty: super::Type) {
48 48
    printTypeBody(ty, false);
49 49
}
50 50
51 51
/// Print just the type name without detailed structure info.
52 -
fn printTypeName(ty: super::Type) {
52 +
unsafe fn printTypeName(ty: super::Type) {
53 53
    printTypeBody(ty, true);
54 54
}
55 55
56 56
/// Print a resolved type, optionally abbreviated for function signatures.
57 -
fn printTypeBody(ty: super::Type, brief: bool) {
57 +
unsafe fn printTypeBody(ty: super::Type, brief: bool) {
58 58
    match ty {
59 59
        case super::Type::Unknown => {
60 60
            io::print("<unknown>");
61 61
        }
62 62
        case super::Type::Undefined => {
172 172
        }
173 173
    }
174 174
}
175 175
176 176
/// Print detailed information about a nominal type (record or union).
177 -
fn printNominalType(info: *super::NominalType) {
177 +
unsafe fn printNominalType(info: *unsafe super::NominalType) {
178 178
    match *info {
179 179
        case super::NominalType::Placeholder(_) => {
180 180
            io::print("<placeholder>");
181 181
        }
182 182
        case super::NominalType::Record(recordType) => {
227 227
        }
228 228
    }
229 229
}
230 230
231 231
/// Print just the type kind for a nominal type, without detailed info.
232 -
fn printNominalTypeName(info: *super::NominalType) {
232 +
unsafe fn printNominalTypeName(info: *unsafe super::NominalType) {
233 233
    match *info {
234 234
        case super::NominalType::Placeholder(_) => {
235 235
            io::print("<placeholder>");
236 236
        }
237 237
        case super::NominalType::Record(_) => {
242 242
        }
243 243
    }
244 244
}
245 245
246 246
/// Print a single diagnostic entry.
247 -
unsafe fn printError(err: *super::Error, res: &super::Resolver) {
247 +
unsafe fn printError(err: &super::Error, res: &super::Resolver) {
248 248
    if let node = err.node {
249 249
        // Find the module containing this error.
250 -
        if let moduleEntry = module::get(&*res.moduleGraph, err.moduleId) {
250 +
        if let moduleEntry = module::get(res.moduleGraph, err.moduleId) {
251 251
            // Get the source text if available.
252 252
            if let source = moduleEntry.source {
253 253
                // Convert offset to location.
254 254
                if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) {
255 255
                    // Print: filename:line:col: error: message
369 369
            io::print("expected numeric type");
370 370
        }
371 371
        case super::ErrorKind::ExpectedPointer => {
372 372
            io::print("expected pointer type");
373 373
        }
374 +
        case super::ErrorKind::InvalidSliceAllocator => {
375 +
            io::print("expected allocator with func callback and matching opaque ctx pointer");
376 +
        }
374 377
        case super::ErrorKind::ExpectedRecord => {
375 378
            io::print("expected record type");
376 379
        }
377 380
        case super::ErrorKind::ExpectedIndexable => {
378 381
            io::print("expected array or slice");
lib/std/lang/resolver/tests.rad +138 -12
75 75
        strings::intern(&mut STRING_POOL, LITERALS[i]);
76 76
    }
77 77
    // TODO: Use local static for this.
78 78
    // Reset the module graph for each test.
79 79
    set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]);
80 -
    set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut STRING_POOL, &mut MODULE_ARENA);
80 +
    set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA);
81 81
    let config = super::Config { buildTest: true };
82 82
    let res = super::resolver(testStorage(), config);
83 83
84 84
    return res;
85 85
}
88 88
unsafe fn resolveStatements(
89 89
    self: &mut super::Resolver, block: ast::Block, arena: &mut ast::NodeArena
90 90
) -> TestResult throws (super::ResolveError) {
91 91
    let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements);
92 92
    let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch {
93 -
        return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, root: module.modBody };
93 +
        return TestResult { diagnostics: super::diagnostics(self), root: module.modBody };
94 94
    };
95 95
    return TestResult { diagnostics, root: module.fnBody };
96 96
}
97 97
98 98
/// Parse and analyze an expression string for testing.
112 112
113 113
/// Parse and analyze a module string for testing.
114 114
/// Use this for code with `fn`, `record`, `union`, etc. at the top level.
115 115
unsafe fn resolveProgramStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
116 116
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
117 -
    let stmt = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
117 +
    let stmt: *ast::Node = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
118 118
        panic "resolveProgramStr: parsing failed";
119 119
    };
120 120
    let diagnostics = try super::resolveModuleRoot(self, stmt) catch {
121 121
        throw testing::TestError::Failed;
122 122
    };
171 171
    arena: &mut ast::NodeArena
172 172
) -> u16 throws (testing::TestError) {
173 173
    let filePath = "<test>";
174 174
    let mut modId: u16 = undefined;
175 175
    if let parent = parentId {
176 -
        set modId = try module::registerChild(graph, parent, name, filePath) catch {
176 +
        set modId = try module::registerChild(graph, &mut STRING_POOL, parent, name, filePath) catch {
177 177
            throw testing::TestError::Failed;
178 178
        };
179 179
    } else {
180 -
        set modId = try module::registerRootWithName(graph, 0, name, filePath) catch {
180 +
        set modId = try module::registerRootWithName(graph, &mut STRING_POOL, 0, name, filePath) catch {
181 181
            throw testing::TestError::Failed;
182 182
        };
183 183
    }
184 184
    let root = try parser::parse(scanner::SourceLoc::String, code, arena, &mut STRING_POOL) catch {
185 185
        panic "registerModule: parsing failed";
205 205
fn expectNoErrors(r: &TestResult) throws (testing::TestError) {
206 206
    try testing::expect(super::success(&r.diagnostics));
207 207
}
208 208
209 209
/// Extract the first error from a test result, failing if none exists.
210 -
fn expectError(result: &TestResult) -> *super::Error throws (testing::TestError) {
210 +
fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) {
211 211
    let err = super::errorAt(&result.diagnostics.errors[..], 0)
212 212
        else throw testing::TestError::Failed;
213 213
    return err;
214 214
}
215 215
313 313
    }
314 314
    return *actual == expected;
315 315
}
316 316
317 317
/// Extract the first error and ensure it has the expected kind.
318 -
fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> *super::Error
318 +
fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error
319 319
    throws (testing::TestError)
320 320
{
321 321
    let err = try expectError(result);
322 322
    try testing::expect(errorKindMatches(&err.kind, kind));
323 323
    return err;
334 334
        throw testing::TestError::Failed;
335 335
    }
336 336
}
337 337
338 338
/// Verify that an error represents a specific type mismatch.
339 -
fn expectTypeMismatch(err: &super::Error, expected: super::Type, actual: super::Type)
339 +
fn expectTypeMismatch(err: super::Error, expected: super::Type, actual: super::Type)
340 340
    throws (testing::TestError)
341 341
{
342 342
    let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
343 343
        else throw testing::TestError::Failed;
344 344
    try testing::expect(mismatch.expected == expected);
374 374
    }
375 375
    return body.statements[index];
376 376
}
377 377
378 378
/// Retrieve a function body block by function name from the program scope.
379 -
fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block
379 +
unsafe fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block
380 380
    throws (testing::TestError)
381 381
{
382 382
    let scope = super::scopeFor(a, root)
383 383
        else throw testing::TestError::Failed;
384 384
    let sym = super::findSymbolInScope(scope, name)
398 398
    return blk;
399 399
}
400 400
401 401
/// Get the payload type of a union variant, if it has one.
402 402
/// For single-field unlabeled variants like `Variant(i32)`, unwraps to return the inner type.
403 -
fn getUnionVariantPayload(nominalTy: *super::NominalType, variantName: *[u8]) -> super::Type {
403 +
unsafe fn getUnionVariantPayload(nominalTy: *unsafe super::NominalType, variantName: *[u8]) -> super::Type {
404 404
    let case super::NominalType::Union(unionType) = *nominalTy
405 405
        else panic "getUnionVariantPayload: not a union";
406 406
    for i in 0..unionType.variants.len {
407 407
        if mem::eq(unionType.variants[i].name, variantName) {
408 408
            let payloadType = unionType.variants[i].valueType;
417 417
    }
418 418
    panic "getUnionVariantPayload: variant not found";
419 419
}
420 420
421 421
/// Get a nominal type by name, in the scope of the given block node.
422 -
fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *super::NominalType
422 +
unsafe fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType
423 423
    throws (testing::TestError)
424 424
{
425 425
    let scope = super::scopeFor(a, blk)
426 426
        else throw testing::TestError::Failed;
427 427
    let sym = super::findSymbolInScope(scope, name)
4922 4922
}
4923 4923
4924 4924
/// Both mutable and immutable methods on a mutable trait object should work.
4925 4925
@test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) {
4926 4926
    let mut a = testResolver();
4927 -
    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(); }";
4927 +
    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(); }";
4928 4928
    let result = try resolveProgramStr(&mut a, program);
4929 4929
    try expectNoErrors(&result);
4930 4930
}
4931 4931
4932 4932
/// Instance method body type must match the trait return type.
6033 6033
/// Unsafe contexts permit pattern access through raw pointers.
6034 6034
@test unsafe fn testRawPointerPatternsAllowed() throws (testing::TestError) {
6035 6035
    try expectAnalyzeOk("union U: Copy { A(u8), B } unsafe fn run(p: *unsafe U) { match p { case U::A(n) => { *n; } else => {} } if let case U::A(n) = p { *n; } while let case U::A(n) = p { *n; break; } }");
6036 6036
    try expectAnalyzeOk("record R: Copy { n: u8 } unsafe fn run(p: *unsafe R) { let case R { n } = p else return; }");
6037 6037
}
6038 +
6039 +
/// Slice append requires the allocator layout and callback signature.
6040 +
@test unsafe fn testSliceAppendAllocatorRejected() throws (testing::TestError) {
6041 +
    let programs = &[
6042 +
        "fn run(s: *mut [u8]) { s.append(1, 0); }",
6043 +
        "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6044 +
        "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6045 +
        "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6046 +
        "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6047 +
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
6048 +
        "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); }",
6049 +
    ];
6050 +
    for program in programs {
6051 +
        let mut a = testResolver();
6052 +
        let result = try resolveProgramStr(&mut a, program);
6053 +
        try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator);
6054 +
    }
6055 +
}
6056 +
6057 +
/// Slice allocators can use raw context pointers.
6058 +
@test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) {
6059 +
    try expectAnalyzeOk("record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }");
6060 +
}
6061 +
6062 +
/// A live pattern reference protects its payload from writes through copied pointers.
6063 +
@test unsafe fn testPatternPayloadPointerAliasesRejected() throws (testing::TestError) {
6064 +
    let programs = &[
6065 +
        "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; match p { case U::A(n) => { set *q = U::B; *n; } else => {} } }",
6066 +
        "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, } }",
6067 +
        "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; } }",
6068 +
        "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; } }",
6069 +
    ];
6070 +
    for program in programs {
6071 +
        let mut a = testResolver();
6072 +
        let result = try resolveProgramStr(&mut a, program);
6073 +
        let _ = try expectError(&result);
6074 +
    }
6075 +
}
6076 +
6077 +
/// Every access to a moved safe mutable pointer is rejected.
6078 +
@test unsafe fn testMutablePointerMovesRejected() throws (testing::TestError) {
6079 +
    let programs = &[
6080 +
        "fn run(p: *mut u8) { let q = p; *p; }",
6081 +
        "fn run(p: *mut u8) { let q = p; set *p = 1; }",
6082 +
        "fn run(p: *mut u8) { let q = p; let r = &*p; }",
6083 +
        "fn read(p: &u8) {} fn run(p: *mut u8) { let q = p; read(p); }",
6084 +
        "fn run(p: &mut u8) { let q = p; *p; }",
6085 +
        "fn run(p: *mut [u8]) { let q = p; p.len; }",
6086 +
        "fn run(p: &mut [u8]) { let q = p; p[0]; }",
6087 +
        "record R { value: u8 } fn run(p: *mut R) { let q = p; p.value; }",
6088 +
        "trait T { fn (&T) read(); } fn run(p: *mut opaque T) { let q = p; p.read(); }",
6089 +
        "fn run(p: *mut u8, flag: bool) { if flag { let q = p; } *p; }",
6090 +
        "fn run(p: *mut u8) { loop { let q = p; } }",
6091 +
    ];
6092 +
    for program in programs {
6093 +
        let mut a = testResolver();
6094 +
        let result = try resolveProgramStr(&mut a, program);
6095 +
        let _ = try expectError(&result);
6096 +
    }
6097 +
}
6098 +
6099 +
/// Copy composites cannot contain safe mutable owners or their containers.
6100 +
@test unsafe fn testCopyMutablePointerFieldsRejected() throws (testing::TestError) {
6101 +
    let programs = &[
6102 +
        "record R: Copy { p: *mut u8 }",
6103 +
        "union U: Copy { A(*mut u8), B }",
6104 +
        "record R: Copy { p: ?*mut u8 }",
6105 +
        "record R: Copy { p: [*mut u8; 2] }",
6106 +
        "record R: Copy { p: *mut [u8] }",
6107 +
        "trait T {} record R: Copy { p: *mut opaque T }",
6108 +
        "record Inner { p: *mut u8 } record Outer: Copy { inner: Inner }",
6109 +
    ];
6110 +
    for program in programs {
6111 +
        let mut a = testResolver();
6112 +
        let result = try resolveProgramStr(&mut a, program);
6113 +
        try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
6114 +
    }
6115 +
}
6116 +
6117 +
/// Moves transfer safe mutable pointers, and calls can borrow them temporarily.
6118 +
@test unsafe fn testMutablePointerMovesAllowed() throws (testing::TestError) {
6119 +
    try expectAnalyzeOk("fn run(p: *mut u8) -> u8 { let q = p; set *q = 1; return *q; }");
6120 +
    try expectAnalyzeOk("fn setValue(p: &mut u8) { set *p = 1; } fn run(p: *mut u8) { setValue(p); setValue(p); *p; }");
6121 +
    try expectAnalyzeOk("fn run(p: *u8) { let q = p; *p; *q; }");
6122 +
    try expectAnalyzeOk("fn run(p: *mut u8, q: *mut u8) { unsafe { p == q; p as u64; } set *p = 1; set *q = 2; }");
6123 +
    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; }");
6124 +
    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; }");
6125 +
}
6126 +
6127 +
/// Function signature inspection uses immutable descriptors in safe code.
6128 +
fn expectImmutableFunctionSignatures(res: &super::Resolver, root: *ast::Node) throws (testing::TestError) {
6129 +
    let first = try typeOf(res, try getBlockStmt(root, 0));
6130 +
    let equivalent = try typeOf(res, try getBlockStmt(root, 1));
6131 +
    let throwing = try typeOf(res, try getBlockStmt(root, 2));
6132 +
    let unsafeCall = try typeOf(res, try getBlockStmt(root, 3));
6133 +
    try testing::expect(super::typesEqual(first, equivalent));
6134 +
    try testing::expect(not super::typesEqual(first, throwing));
6135 +
    try testing::expect(not super::typesEqual(first, unsafeCall));
6136 +
}
6137 +
6138 +
/// Local analysis state does not change function signature identity.
6139 +
@test unsafe fn testImmutableFunctionSignatures() throws (testing::TestError) {
6140 +
    let mut res = testResolver();
6141 +
    let result = try resolveProgramStr(&mut res,
6142 +
        "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; }");
6143 +
    try expectNoErrors(&result);
6144 +
    try expectImmutableFunctionSignatures(&res, result.root);
6145 +
}
6146 +
6147 +
/// A diagnostic snapshot keeps its contents when the resolver buffer changes.
6148 +
@test unsafe fn testDiagnosticSnapshotOwnsItsErrors() throws (testing::TestError) {
6149 +
    let mut res = testResolver();
6150 +
    let result = try resolveProgramStr(&mut res, "fn run() { missing; }");
6151 +
    let snapshot = result.diagnostics;
6152 +
    let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
6153 +
    set res.errors.entries[0] = super::Error {
6154 +
        kind: super::ErrorKind::Internal, node: nil, moduleId: 0,
6155 +
    };
6156 +
    let later = super::diagnostics(&mut res);
6157 +
    try testing::expect(snapshot.errors.len == 1);
6158 +
    try testing::expect(later.errors.len == 1);
6159 +
    let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed;
6160 +
    try testing::expect(first.kind == original.kind);
6161 +
    try testing::expect(later.errors[0].kind == super::ErrorKind::Internal);
6162 +
    try testing::expect(super::errorAt(snapshot.errors, 1) == nil);
6163 +
}
lib/std/lang/scanner.rad +6 -6
190 190
    source: *[u8],
191 191
    /// Offset of current token into buffer.
192 192
    token: u32,
193 193
    /// Offset of current character being scanned.
194 194
    cursor: u32,
195 -
    /// Interned string pool.
196 -
    pool: *mut strings::Pool,
195 +
    /// Interned string pool. It must remain valid while scanning.
196 +
    pool: *unsafe mut strings::Pool,
197 197
}
198 198
199 199
/// Individual token with kind, source text, and position.
200 200
///
201 201
/// Represents a single lexical element extracted from source,
220 220
    /// Column number.
221 221
    col: u16,
222 222
}
223 223
224 224
/// Create a new scanner object.
225 -
export fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *mut strings::Pool) -> Scanner {
225 +
export unsafe fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *unsafe mut strings::Pool) -> Scanner {
226 226
    // Intern built-in functions and attributes.
227 227
    strings::intern(pool, "@sizeOf");
228 228
    strings::intern(pool, "@alignOf");
229 229
    strings::intern(pool, "@sliceOf");
230 230
    strings::intern(pool, "@default");
399 399
    }
400 400
    return TokenKind::Ident;
401 401
}
402 402
403 403
/// Scan an identifier, keyword, or label.
404 -
fn scanIdentifier(s: &mut Scanner) -> Token {
404 +
unsafe fn scanIdentifier(s: &mut Scanner) -> Token {
405 405
    while let ch = current(s); char::isAlpha(ch) or ch == '_' or char::isDigit(ch) {
406 406
        advance(s);
407 407
    }
408 408
    let ident = &s.source[s.token..s.cursor];
409 409
    let kind = keywordOrIdent(ident);
413 413
        return Token { kind, source: strings::intern(s.pool, ident), offset: s.token };
414 414
    }
415 415
    return tok(s, kind);
416 416
}
417 417
418 -
/// Scan the next token.
419 -
export fn next(s: &mut Scanner) -> Token {
418 +
/// Scan the next token. The retained string pool must be valid and writable.
419 +
export unsafe fn next(s: &mut Scanner) -> Token {
420 420
    skipWhitespace(s);  // Skip any whitespace between tokens.
421 421
    set s.token = s.cursor; // Token starts at current position.
422 422
423 423
    if isEof(s) {
424 424
        return tok(s, TokenKind::Eof);
lib/std/lang/scanner/tests.rad +20 -20
10 10
    unsafe {
11 11
        return super::scanner(super::SourceLoc::File("test.r"), source, &mut TEST_STRING_POOL);
12 12
    }
13 13
}
14 14
15 -
@test fn testScanTokens() throws (testing::TestError) {
15 +
@test unsafe fn testScanTokens() throws (testing::TestError) {
16 16
    let mut s = testScanner(
17 17
        "'x' < fnord fnord: 0 => >> mod and nil"
18 18
    );
19 19
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Char);
20 20
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Lt);
28 28
    try testing::expect(super::next(&mut s).kind == super::TokenKind::And);
29 29
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Nil);
30 30
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
31 31
}
32 32
33 -
@test fn testScanChars() throws (testing::TestError) {
33 +
@test unsafe fn testScanChars() throws (testing::TestError) {
34 34
    let mut s = testScanner(
35 35
        "'\\0'"
36 36
    );
37 37
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Char);
38 38
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
39 39
}
40 40
41 -
@test fn testScanWhitespace() throws (testing::TestError) {
41 +
@test unsafe fn testScanWhitespace() throws (testing::TestError) {
42 42
    let mut s = testScanner(
43 43
        " X\n\nY //\n Z//1 \n"
44 44
    );
45 45
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
46 46
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
47 47
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
48 48
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
49 49
}
50 50
51 -
@test fn testScanIdentsAndKeywords() throws (testing::TestError) {
51 +
@test unsafe fn testScanIdentsAndKeywords() throws (testing::TestError) {
52 52
    let mut s = testScanner(
53 53
        "m mo mod modo"
54 54
    );
55 55
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
56 56
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
57 57
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Mod);
58 58
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
59 59
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
60 60
}
61 61
62 -
@test fn testScanIdentifiers() throws (testing::TestError) {
62 +
@test unsafe fn testScanIdentifiers() throws (testing::TestError) {
63 63
    let mut s = testScanner(
64 64
        "fnord::yikes no1 4j"
65 65
    );
66 66
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
67 67
    try testing::expect(super::next(&mut s).kind == super::TokenKind::ColonColon);
70 70
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Number);
71 71
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
72 72
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
73 73
}
74 74
75 -
@test fn testScanAtIdentifiers() throws (testing::TestError) {
75 +
@test unsafe fn testScanAtIdentifiers() throws (testing::TestError) {
76 76
    let mut s = testScanner(
77 77
        "@default @"
78 78
    );
79 79
    try testing::expect(super::next(&mut s).kind == super::TokenKind::AtIdent);
80 80
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Invalid);
81 81
}
82 82
83 -
@test fn testScanStrings() throws (testing::TestError) {
83 +
@test unsafe fn testScanStrings() throws (testing::TestError) {
84 84
    let mut s = testScanner(
85 85
        "\"Hello World!\" \"\\\"\""
86 86
    );
87 87
    let mut t: super::Token = super::next(&mut s);
88 88
    try testing::expect(t.kind == super::TokenKind::String);
93 93
    try testing::expect(t.kind == super::TokenKind::String);
94 94
    try testing::expect(t.source.len == 4);
95 95
    try testing::expect(mem::eq(t.source, "\"\\\"\""));
96 96
}
97 97
98 -
@test fn testGetLocation() throws (testing::TestError) {
98 +
@test unsafe fn testGetLocation() throws (testing::TestError) {
99 99
    let mut s = testScanner(
100 100
        "abc\n  def\n    ghi"
101 101
    );
102 102
    let mut tok: super::Token = super::next(&mut s);
103 103
    try testing::expect(tok.kind == super::TokenKind::Ident);
129 129
    } else {
130 130
        try testing::expect(false);
131 131
    }
132 132
}
133 133
134 -
@test fn testScanEmptyInput() throws (testing::TestError) {
134 +
@test unsafe fn testScanEmptyInput() throws (testing::TestError) {
135 135
    let mut s = testScanner("");
136 136
    let tok: super::Token = super::next(&mut s);
137 137
138 138
    try testing::expect(tok.kind == super::TokenKind::Eof);
139 139
    try testing::expect(tok.source.len == 0);
140 140
    try testing::expect(tok.offset == 0);
141 141
}
142 142
143 -
@test fn testScanSingleCharTokens() throws (testing::TestError) {
143 +
@test unsafe fn testScanSingleCharTokens() throws (testing::TestError) {
144 144
    let mut s = testScanner(
145 145
        "(){}[] ,;+-*~|&"
146 146
    );
147 147
    try testing::expect(super::next(&mut s).kind == super::TokenKind::LParen);
148 148
    try testing::expect(super::next(&mut s).kind == super::TokenKind::RParen);
159 159
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Pipe);
160 160
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Amp);
161 161
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
162 162
}
163 163
164 -
@test fn testScanDoubleCharTokens() throws (testing::TestError) {
164 +
@test unsafe fn testScanDoubleCharTokens() throws (testing::TestError) {
165 165
    let mut s = testScanner(
166 166
        "== <> <= >= < > >> << -> =>"
167 167
    );
168 168
    try testing::expect(super::next(&mut s).kind == super::TokenKind::EqualEqual);
169 169
    try testing::expect(super::next(&mut s).kind == super::TokenKind::LtGt);
176 176
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Arrow);
177 177
    try testing::expect(super::next(&mut s).kind == super::TokenKind::FatArrow);
178 178
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof);
179 179
}
180 180
181 -
@test fn testScanIdentifierLengths() throws (testing::TestError) {
181 +
@test unsafe fn testScanIdentifierLengths() throws (testing::TestError) {
182 182
    let mut s = testScanner(
183 183
        "sho xyz981 lal_lel"
184 184
    );
185 185
    let mut tok: super::Token = super::next(&mut s);
186 186
    try testing::expect(tok.kind == super::TokenKind::Ident);
193 193
    set tok = super::next(&mut s);
194 194
    try testing::expect(tok.kind == super::TokenKind::Ident);
195 195
    try testing::expect(tok.source.len == 7);
196 196
}
197 197
198 -
@test fn testScanNumbers() throws (testing::TestError) {
198 +
@test unsafe fn testScanNumbers() throws (testing::TestError) {
199 199
    let mut s = testScanner(
200 200
        "2 -2 +2 value-2 value+2 value--2 value+-2 value-+2 f(-2)"
201 201
    );
202 202
    let expected: [super::TokenKind; 29] = [
203 203
        super::TokenKind::Number,
218 218
    for expectedKind in expected {
219 219
        try testing::expect(super::next(&mut s).kind == expectedKind);
220 220
    }
221 221
}
222 222
223 -
@test fn testScanKeywords() throws (testing::TestError) {
223 +
@test unsafe fn testScanKeywords() throws (testing::TestError) {
224 224
    let mut s = testScanner("nil mod not static unsafe");
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);
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 245
}
246 246
247 -
@test fn testScanVoidAsIdent() throws (testing::TestError) {
247 +
@test unsafe fn testScanVoidAsIdent() throws (testing::TestError) {
248 248
    let mut s = testScanner("void");
249 249
    let tok: super::Token = super::next(&mut s);
250 250
    try testing::expect(tok.kind == super::TokenKind::Ident);
251 251
    try testing::expect(tok.source.len == 4);
252 252
}
253 253
254 -
@test fn testScanWhitespaceAndComments() throws (testing::TestError) {
254 +
@test unsafe fn testScanWhitespaceAndComments() throws (testing::TestError) {
255 255
    let mut s = testScanner(
256 256
        "  \t\n  // This is a comment..\n  42"
257 257
    );
258 258
    let tok: super::Token = super::next(&mut s);
259 259
    try testing::expect(tok.kind == super::TokenKind::Number);
260 260
    try testing::expect(tok.source.len == 2);
261 261
    try testing::expect(super::isEof(&s));
262 262
}
263 263
264 -
@test fn testScanInvalidCharacters() throws (testing::TestError) {
264 +
@test unsafe fn testScanInvalidCharacters() throws (testing::TestError) {
265 265
    let mut s = testScanner(
266 266
        "`#$"
267 267
    );
268 268
    let mut tok: super::Token = super::next(&mut s);
269 269
    try testing::expect(tok.kind == super::TokenKind::Invalid);
276 276
    set tok = super::next(&mut s);
277 277
    try testing::expect(tok.kind == super::TokenKind::Invalid);
278 278
    try testing::expect(tok.offset == 2);
279 279
}
280 280
281 -
@test fn testScanUnterminatedString() throws (testing::TestError) {
281 +
@test unsafe fn testScanUnterminatedString() throws (testing::TestError) {
282 282
    let mut s = testScanner("\"Hello World!");
283 283
    let tok: super::Token = super::next(&mut s);
284 284
285 285
    try testing::expect(tok.kind == super::TokenKind::Invalid);
286 286
    try testing::expect(tok.source == "unterminated string");
287 287
    try testing::expect(tok.offset == 0);
288 288
}
289 289
290 -
@test fn testScanUnderminatedStringEscape() throws (testing::TestError) {
290 +
@test unsafe fn testScanUnderminatedStringEscape() throws (testing::TestError) {
291 291
    let mut s = testScanner("\"\\");
292 292
    let tok: super::Token = super::next(&mut s);
293 293
294 294
    try testing::expect(tok.kind == super::TokenKind::Invalid);
295 295
}
296 296
297 -
@test fn testScanFunctionDefinition() throws (testing::TestError) {
297 +
@test unsafe fn testScanFunctionDefinition() throws (testing::TestError) {
298 298
    let mut s = testScanner(
299 299
        "fn add(a: i32, b: i32) -> i32 { return a + b; }"
300 300
    );
301 301
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Fn);
302 302
    try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident);
lib/std/lang/sexpr.rad +1 -1
9 9
/// Output target for S-expression printing.
10 10
export union Output: Copy {
11 11
    /// Print to stdout.
12 12
    Stdout,
13 13
    /// Write to a buffer, tracking position.
14 -
    Buffer { buf: *mut [u8], pos: *unsafe mut u32 },
14 +
    Buffer { buf: *unsafe mut [u8], pos: *unsafe mut u32 },
15 15
}
16 16
17 17
/// An S-expression element.
18 18
export union Expr: Copy {
19 19
    /// An empty expression.
lib/std/lang/strings.rad +3 -3
30 30
    /// Not found, contains empty slot index.
31 31
    Empty(u32),
32 32
}
33 33
34 34
/// Look up a string, returning either the found entry or the empty slot index.
35 -
fn lookup(sp: *Pool, str: *[u8]) -> Lookup {
35 +
fn lookup(sp: &Pool, str: *[u8]) -> Lookup {
36 36
    let mut idx = dict::hash(str) & (TABLE_SIZE - 1);
37 37
38 38
    loop {
39 39
        let entry = sp.table[idx];
40 40
        if entry.len > 0 {
48 48
    }
49 49
}
50 50
51 51
/// Look up a string in the pool.
52 52
/// Returns the canonical interned pointer if the string exists.
53 -
export fn find(sp: *Pool, str: *[u8]) -> ?*[u8] {
53 +
export fn find(sp: &Pool, str: *[u8]) -> ?*[u8] {
54 54
    match lookup(sp, str) {
55 55
        case Lookup::Found(entry) => return entry,
56 56
        case Lookup::Empty(_) => return nil,
57 57
    }
58 58
}
59 59
60 60
/// Intern a string, returning a canonical pointer for equality comparison.
61 61
///
62 62
/// If the string content already exists in the pool, returns the existing pointer.
63 63
/// Otherwise, adds the string pointer to the pool and returns it.
64 -
export fn intern(sp: *mut Pool, str: *[u8]) -> *[u8] {
64 +
export fn intern(sp: &mut Pool, str: *[u8]) -> *[u8] {
65 65
    match lookup(sp, str) {
66 66
        case Lookup::Found(entry) => return entry,
67 67
        case Lookup::Empty(idx) => {
68 68
            assert sp.count < TABLE_SIZE / 2, "intern: string pool is full";
69 69
            set sp.table[idx] = str;
test/runner.rad +4 -4
171 171
}
172 172
173 173
/// Write a self-contained RV64 image containing text and data sections.
174 174
unsafe fn writeImage(
175 175
    code: *[u32],
176 -
    roData: *[u8],
176 +
    roData: &[u8],
177 177
    rwData: *[u8],
178 178
    path: *[u8]
179 179
) -> bool {
180 180
    let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len);
181 181
    let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32);
182 182
    let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32);
183 183
184 184
    let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420);
185 185
    if fd < 0 { return false; }
186 -
    let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes)
186 +
    let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, &codeBytes[..])
187 187
        and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData);
188 188
    let closed = unix::close(fd) == 0;
189 189
    return written and closed;
190 190
}
191 191
212 212
        io::printError(sourcePath);
213 213
        io::printError("\n");
214 214
        return false;
215 215
    };
216 216
217 -
    if not writeImage(program.text, program.data, &[], outputPath) {
217 +
    if not writeImage(program.text, &program.data[..], &[], outputPath) {
218 218
        io::printError("error: could not write output: ");
219 219
        io::printError(outputPath);
220 220
        io::printError("\n");
221 221
        return false;
222 222
    }
253 253
    let expected = &EXPECTED_BUF[..expectedLen];
254 254
    let source = &SOURCE_BUF[..sourceLen];
255 255
256 256
    // Parse source.
257 257
    let mut astArena = ast::nodeArena(&mut AST_ARENA_STORAGE[..]);
258 -
    let root = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch {
258 +
    let root: *ast::Node = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch {
259 259
        io::printLn("error: parsing failed");
260 260
        return false;
261 261
    };
262 262
263 263
    // Run resolver.
test/tests/edge.cases.6.rad +2 -2
22 22
    r: u32,
23 23
    s: u32,
24 24
    t: u32,
25 25
}
26 26
27 -
record Analyzer: Copy {
27 +
record Analyzer {
28 28
    pad0: u32,
29 29
    pad1: u32,
30 30
    entries: *mut [Entry],
31 31
    len: u32,
32 32
}
115 115
116 116
@default unsafe fn main() -> i32 {
117 117
    let target = &mut STORAGE[..];
118 118
    init(target);
119 119
    add();
120 -
    return checkHeader(target);
120 +
    return checkHeader(&STORAGE[..]);
121 121
}
test/tests/edge.cases.7.addr.bug.rad +1 -1
1 1
//! returns: 42
2 2
3 -
record PtrHolder: Copy {
3 +
record PtrHolder {
4 4
    ptr: *mut i32,
5 5
}
6 6
7 7
static target: i32 = 10;
8 8
unsafe static holder: PtrHolder = undefined;
test/tests/pointer.arithmetic.unsafe.rad +1 -1
3 3
/// Backing storage for pointer offsets.
4 4
static DATA: [u8; 3] = [11, 22, 33];
5 5
6 6
/// Read and write valid offsets in an unsafe function.
7 7
@default unsafe fn main() -> i32 {
8 -
    let p = &mut DATA[0];
8 +
    let p: *unsafe mut u8 = &mut DATA[0];
9 9
    let q = p + 1;
10 10
    if *q <> 22 { return 1; }
11 11
    if *(1 + q) <> 33 { return 2; }
12 12
    if *(q - 1) <> 11 { return 3; }
13 13
    set *q = 44;
test/tests/pointer.cast.unsafe.rad +2 -2
6 6
/// Reinterpret valid storage in an unsafe function.
7 7
@default unsafe fn main() -> i32 {
8 8
    let bytes = &mut DATA[..] as *mut [u8];
9 9
    if bytes[0] <> 42 { return 1; }
10 10
    set bytes[0] = 43;
11 -
    let word = bytes.ptr as *u64;
12 -
    if *word <> 43 { return 2; }
13 11
    let words = bytes as *[u64];
12 +
    let word = words.ptr as *u64;
13 +
    if *word <> 43 { return 2; }
14 14
    if words[0] <> 43 { return 3; }
15 15
    return 0;
16 16
}
test/tests/pointer.slice.store.rad +2 -2
7 7
    c: u32,
8 8
    d: u32,
9 9
    e: u32,
10 10
}
11 11
12 -
record Table: Copy {
12 +
record Table {
13 13
    entries: *mut [Entry],
14 14
    len: u32,
15 15
}
16 16
17 -
record PtrBox: Copy {
17 +
record PtrBox {
18 18
    ptr: *mut *mut [Entry],
19 19
}
20 20
21 21
unsafe static STORAGE: [Entry; 2] = undefined;
22 22
unsafe static TABLE: Table = undefined;
test/tests/slice.append.rad +9 -9
1 1
//! returns: 0
2 2
//! Test slice .append() method with inline allocator.
3 3
4 4
/// Simple bump allocator for testing.
5 -
record Arena: Copy {
5 +
record Arena {
6 6
    data: *mut [u8],
7 7
    offset: u32,
8 8
}
9 9
10 10
fn newArena(data: *mut [u8]) -> Arena {
23 23
    return base as *mut opaque;
24 24
}
25 25
26 26
/// Allocator record matching the compiler's expected layout.
27 27
record Allocator: Copy {
28 -
    func: unsafe fn(*mut opaque, u32, u32) -> *mut opaque,
29 -
    ctx: *mut opaque,
28 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
29 +
    ctx: *unsafe mut opaque,
30 30
}
31 31
32 -
unsafe fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque {
33 -
    let arena = ctx as *mut Arena;
34 -
    return arenaAlloc(arena, size, al);
32 +
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, al: u32) -> *mut opaque {
33 +
    let arena = ctx as *unsafe mut Arena;
34 +
    return arenaAlloc(&mut *arena, size, al);
35 35
}
36 36
37 -
fn arenaAllocator(arena: *mut Arena) -> Allocator {
37 +
unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator {
38 38
    return Allocator {
39 39
        func: arenaAllocFn,
40 -
        ctx: arena as *mut opaque,
40 +
        ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque,
41 41
    };
42 42
}
43 43
44 44
unsafe static BUF: [u8; 4096] = undefined;
45 45
104 104
    if nums[3] <> 40 {
105 105
        return 12;
106 106
    }
107 107
108 108
    // Append from empty (cap == 0 triggers growth with cap = 1).
109 -
    let mut empty = @sliceOf(ptr as *mut i32, 0, 0);
109 +
    let mut empty: *mut [i32] = &mut [];
110 110
    empty.append(99, a);
111 111
112 112
    if empty.len <> 1 {
113 113
        return 13;
114 114
    }
test/tests/slice.append.ril +293 -293
341 341
    ebreak;
342 342
    unreachable;
343 343
  @then58
344 344
    ret 12;
345 345
  @merge59
346 -
    reserve %167 16 8;
347 -
    store w64 %10 %167 0;
348 -
    store w32 0 %167 8;
349 -
    store w32 0 %167 12;
350 -
    load w32 %170 %167 8;
351 -
    load w32 %171 %167 12;
352 -
    br.ult w32 %170 %171 @append.store62 @append.grow63;
346 +
    reserve %157 16 8;
347 +
    store w64 0 %157 0;
348 +
    store w32 0 %157 8;
349 +
    store w32 0 %157 12;
350 +
    load w32 %160 %157 8;
351 +
    load w32 %161 %157 12;
352 +
    br.ult w32 %160 %161 @append.store62 @append.grow63;
353 353
  @guard#pass60
354 354
    load w64 %153 %11 0;
355 355
    mul w64 %154 3 4;
356 356
    add w64 %155 %153 %154;
357 357
    sload w32 %156 %155 0;
358 358
    br.ne w32 %156 40 @then58 @merge59;
359 359
  @guard#trap61
360 360
    ebreak;
361 361
    unreachable;
362 362
  @append.store62
363 -
    load w64 %185 %167 0;
364 -
    mul w64 %186 %170 4;
365 -
    add w64 %187 %185 %186;
366 -
    store w32 99 %187 0;
367 -
    add w32 %188 %170 1;
368 -
    store w32 %188 %167 8;
369 -
    load w32 %191 %167 8;
370 -
    br.ne w32 %191 1 @then67 @merge68;
363 +
    load w64 %175 %157 0;
364 +
    mul w64 %176 %160 4;
365 +
    add w64 %177 %175 %176;
366 +
    store w32 99 %177 0;
367 +
    add w32 %178 %160 1;
368 +
    store w32 %178 %157 8;
369 +
    load w32 %181 %157 8;
370 +
    br.ne w32 %181 1 @then67 @merge68;
371 371
  @append.grow63
372 -
    shl w32 %172 %171 1;
373 -
    or w32 %173 %172 1;
374 -
    load w64 %174 %7 0;
375 -
    load w64 %175 %7 8;
376 -
    mul w32 %176 %173 4;
377 -
    call w64 %177 %174(%175, %176, 4);
378 -
    load w64 %178 %167 0;
379 -
    mul w32 %179 %170 4;
372 +
    shl w32 %162 %161 1;
373 +
    or w32 %163 %162 1;
374 +
    load w64 %164 %7 0;
375 +
    load w64 %165 %7 8;
376 +
    mul w32 %166 %163 4;
377 +
    call w64 %167 %164(%165, %166, 4);
378 +
    load w64 %168 %157 0;
379 +
    mul w32 %169 %160 4;
380 380
    jmp @append64(0);
381 -
  @append64(w32 %180)
382 -
    br.ult w32 %180 %179 @append65 @append66;
381 +
  @append64(w32 %170)
382 +
    br.ult w32 %170 %169 @append65 @append66;
383 383
  @append65
384 -
    add w64 %181 %178 %180;
385 -
    load w8 %182 %181 0;
386 -
    add w64 %183 %177 %180;
387 -
    store w8 %182 %183 0;
388 -
    add w32 %184 %180 1;
389 -
    jmp @append64(%184);
384 +
    add w64 %171 %168 %170;
385 +
    load w8 %172 %171 0;
386 +
    add w64 %173 %167 %170;
387 +
    store w8 %172 %173 0;
388 +
    add w32 %174 %170 1;
389 +
    jmp @append64(%174);
390 390
  @append66
391 -
    store w64 %177 %167 0;
392 -
    store w32 %173 %167 12;
391 +
    store w64 %167 %157 0;
392 +
    store w32 %163 %157 12;
393 393
    jmp @append.store62;
394 394
  @then67
395 395
    ret 13;
396 396
  @merge68
397 -
    load w32 %192 %167 12;
398 -
    br.ne w32 %192 1 @then69 @merge70;
397 +
    load w32 %182 %157 12;
398 +
    br.ne w32 %182 1 @then69 @merge70;
399 399
  @then69
400 400
    ret 14;
401 401
  @merge70
402 -
    load w32 %193 %167 8;
403 -
    br.ult w32 0 %193 @guard#pass73 @guard#trap74;
402 +
    load w32 %183 %157 8;
403 +
    br.ult w32 0 %183 @guard#pass73 @guard#trap74;
404 404
  @then71
405 405
    ret 15;
406 406
  @merge72
407 -
    load w32 %198 %11 8;
408 -
    load w64 %199 %11 0;
407 +
    load w32 %188 %11 8;
408 +
    load w64 %189 %11 0;
409 409
    jmp @loop75(0, 0);
410 410
  @guard#pass73
411 -
    load w64 %194 %167 0;
412 -
    sload w32 %195 %194 0;
413 -
    br.ne w32 %195 99 @then71 @merge72;
411 +
    load w64 %184 %157 0;
412 +
    sload w32 %185 %184 0;
413 +
    br.ne w32 %185 99 @then71 @merge72;
414 414
  @guard#trap74
415 415
    ebreak;
416 416
    unreachable;
417 -
  @loop75(w32 %200, w32 %204)
418 -
    br.slt w32 %200 %198 @body76 @merge77;
417 +
  @loop75(w32 %190, w32 %194)
418 +
    br.slt w32 %190 %188 @body76 @merge77;
419 419
  @body76
420 -
    mul w64 %201 %200 4;
421 -
    add w64 %202 %199 %201;
422 -
    sload w32 %203 %202 0;
423 -
    add w32 %205 %204 %203;
424 -
    add w32 %206 %200 1;
425 -
    jmp @loop75(%206, %205);
420 +
    mul w64 %191 %190 4;
421 +
    add w64 %192 %189 %191;
422 +
    sload w32 %193 %192 0;
423 +
    add w32 %195 %194 %193;
424 +
    add w32 %196 %190 1;
425 +
    jmp @loop75(%196, %195);
426 426
  @merge77
427 -
    br.ne w32 %204 150 @then78 @merge79;
427 +
    br.ne w32 %194 150 @then78 @merge79;
428 428
  @then78
429 429
    ret 16;
430 430
  @merge79
431 -
    copy %207 $main$nominal$arena;
432 -
    call w64 %208 $arenaAlloc(%207, 2, 1);
433 -
    reserve %209 16 8;
434 -
    store w64 %208 %209 0;
435 -
    store w32 0 %209 8;
436 -
    store w32 2 %209 12;
437 -
    load w32 %213 %209 8;
438 -
    load w32 %214 %209 12;
439 -
    br.ult w32 %213 %214 @append.store80 @append.grow81;
431 +
    copy %197 $main$nominal$arena;
432 +
    call w64 %198 $arenaAlloc(%197, 2, 1);
433 +
    reserve %199 16 8;
434 +
    store w64 %198 %199 0;
435 +
    store w32 0 %199 8;
436 +
    store w32 2 %199 12;
437 +
    load w32 %203 %199 8;
438 +
    load w32 %204 %199 12;
439 +
    br.ult w32 %203 %204 @append.store80 @append.grow81;
440 440
  @append.store80
441 -
    load w64 %228 %209 0;
442 -
    add w64 %229 %228 %213;
443 -
    store w8 171 %229 0;
444 -
    add w32 %230 %213 1;
445 -
    store w32 %230 %209 8;
446 -
    load w32 %235 %209 8;
447 -
    load w32 %236 %209 12;
448 -
    br.ult w32 %235 %236 @append.store85 @append.grow86;
441 +
    load w64 %218 %199 0;
442 +
    add w64 %219 %218 %203;
443 +
    store w8 171 %219 0;
444 +
    add w32 %220 %203 1;
445 +
    store w32 %220 %199 8;
446 +
    load w32 %225 %199 8;
447 +
    load w32 %226 %199 12;
448 +
    br.ult w32 %225 %226 @append.store85 @append.grow86;
449 449
  @append.grow81
450 -
    shl w32 %215 %214 1;
451 -
    or w32 %216 %215 1;
452 -
    load w64 %217 %7 0;
453 -
    load w64 %218 %7 8;
454 -
    mul w32 %219 %216 1;
455 -
    call w64 %220 %217(%218, %219, 1);
456 -
    load w64 %221 %209 0;
457 -
    mul w32 %222 %213 1;
450 +
    shl w32 %205 %204 1;
451 +
    or w32 %206 %205 1;
452 +
    load w64 %207 %7 0;
453 +
    load w64 %208 %7 8;
454 +
    mul w32 %209 %206 1;
455 +
    call w64 %210 %207(%208, %209, 1);
456 +
    load w64 %211 %199 0;
457 +
    mul w32 %212 %203 1;
458 458
    jmp @append82(0);
459 -
  @append82(w32 %223)
460 -
    br.ult w32 %223 %222 @append83 @append84;
459 +
  @append82(w32 %213)
460 +
    br.ult w32 %213 %212 @append83 @append84;
461 461
  @append83
462 -
    add w64 %224 %221 %223;
463 -
    load w8 %225 %224 0;
464 -
    add w64 %226 %220 %223;
465 -
    store w8 %225 %226 0;
466 -
    add w32 %227 %223 1;
467 -
    jmp @append82(%227);
462 +
    add w64 %214 %211 %213;
463 +
    load w8 %215 %214 0;
464 +
    add w64 %216 %210 %213;
465 +
    store w8 %215 %216 0;
466 +
    add w32 %217 %213 1;
467 +
    jmp @append82(%217);
468 468
  @append84
469 -
    store w64 %220 %209 0;
470 -
    store w32 %216 %209 12;
469 +
    store w64 %210 %199 0;
470 +
    store w32 %206 %199 12;
471 471
    jmp @append.store80;
472 472
  @append.store85
473 -
    load w64 %250 %209 0;
474 -
    add w64 %251 %250 %235;
475 -
    store w8 205 %251 0;
476 -
    add w32 %252 %235 1;
477 -
    store w32 %252 %209 8;
478 -
    load w32 %255 %209 8;
479 -
    br.ne w32 %255 2 @then90 @merge91;
473 +
    load w64 %240 %199 0;
474 +
    add w64 %241 %240 %225;
475 +
    store w8 205 %241 0;
476 +
    add w32 %242 %225 1;
477 +
    store w32 %242 %199 8;
478 +
    load w32 %245 %199 8;
479 +
    br.ne w32 %245 2 @then90 @merge91;
480 480
  @append.grow86
481 -
    shl w32 %237 %236 1;
482 -
    or w32 %238 %237 1;
483 -
    load w64 %239 %7 0;
484 -
    load w64 %240 %7 8;
485 -
    mul w32 %241 %238 1;
486 -
    call w64 %242 %239(%240, %241, 1);
487 -
    load w64 %243 %209 0;
488 -
    mul w32 %244 %235 1;
481 +
    shl w32 %227 %226 1;
482 +
    or w32 %228 %227 1;
483 +
    load w64 %229 %7 0;
484 +
    load w64 %230 %7 8;
485 +
    mul w32 %231 %228 1;
486 +
    call w64 %232 %229(%230, %231, 1);
487 +
    load w64 %233 %199 0;
488 +
    mul w32 %234 %225 1;
489 489
    jmp @append87(0);
490 -
  @append87(w32 %245)
491 -
    br.ult w32 %245 %244 @append88 @append89;
490 +
  @append87(w32 %235)
491 +
    br.ult w32 %235 %234 @append88 @append89;
492 492
  @append88
493 -
    add w64 %246 %243 %245;
494 -
    load w8 %247 %246 0;
495 -
    add w64 %248 %242 %245;
496 -
    store w8 %247 %248 0;
497 -
    add w32 %249 %245 1;
498 -
    jmp @append87(%249);
493 +
    add w64 %236 %233 %235;
494 +
    load w8 %237 %236 0;
495 +
    add w64 %238 %232 %235;
496 +
    store w8 %237 %238 0;
497 +
    add w32 %239 %235 1;
498 +
    jmp @append87(%239);
499 499
  @append89
500 -
    store w64 %242 %209 0;
501 -
    store w32 %238 %209 12;
500 +
    store w64 %232 %199 0;
501 +
    store w32 %228 %199 12;
502 502
    jmp @append.store85;
503 503
  @then90
504 504
    ret 17;
505 505
  @merge91
506 -
    load w32 %256 %209 8;
507 -
    br.ult w32 0 %256 @guard#pass94 @guard#trap95;
506 +
    load w32 %246 %199 8;
507 +
    br.ult w32 0 %246 @guard#pass94 @guard#trap95;
508 508
  @then92
509 509
    ret 18;
510 510
  @merge93
511 -
    load w32 %259 %209 8;
512 -
    br.ult w32 1 %259 @guard#pass98 @guard#trap99;
511 +
    load w32 %249 %199 8;
512 +
    br.ult w32 1 %249 @guard#pass98 @guard#trap99;
513 513
  @guard#pass94
514 -
    load w64 %257 %209 0;
515 -
    load w8 %258 %257 0;
516 -
    br.ne w8 %258 171 @then92 @merge93;
514 +
    load w64 %247 %199 0;
515 +
    load w8 %248 %247 0;
516 +
    br.ne w8 %248 171 @then92 @merge93;
517 517
  @guard#trap95
518 518
    ebreak;
519 519
    unreachable;
520 520
  @then96
521 521
    ret 19;
522 522
  @merge97
523 -
    load w32 %265 %209 8;
524 -
    load w32 %266 %209 12;
525 -
    br.ult w32 %265 %266 @append.store100 @append.grow101;
523 +
    load w32 %255 %199 8;
524 +
    load w32 %256 %199 12;
525 +
    br.ult w32 %255 %256 @append.store100 @append.grow101;
526 526
  @guard#pass98
527 -
    load w64 %260 %209 0;
528 -
    add w64 %261 %260 1;
529 -
    load w8 %262 %261 0;
530 -
    br.ne w8 %262 205 @then96 @merge97;
527 +
    load w64 %250 %199 0;
528 +
    add w64 %251 %250 1;
529 +
    load w8 %252 %251 0;
530 +
    br.ne w8 %252 205 @then96 @merge97;
531 531
  @guard#trap99
532 532
    ebreak;
533 533
    unreachable;
534 534
  @append.store100
535 -
    load w64 %280 %209 0;
536 -
    add w64 %281 %280 %265;
537 -
    store w8 239 %281 0;
538 -
    add w32 %282 %265 1;
539 -
    store w32 %282 %209 8;
540 -
    load w32 %285 %209 8;
541 -
    br.ne w32 %285 3 @then105 @merge106;
535 +
    load w64 %270 %199 0;
536 +
    add w64 %271 %270 %255;
537 +
    store w8 239 %271 0;
538 +
    add w32 %272 %255 1;
539 +
    store w32 %272 %199 8;
540 +
    load w32 %275 %199 8;
541 +
    br.ne w32 %275 3 @then105 @merge106;
542 542
  @append.grow101
543 -
    shl w32 %267 %266 1;
544 -
    or w32 %268 %267 1;
545 -
    load w64 %269 %7 0;
546 -
    load w64 %270 %7 8;
547 -
    mul w32 %271 %268 1;
548 -
    call w64 %272 %269(%270, %271, 1);
549 -
    load w64 %273 %209 0;
550 -
    mul w32 %274 %265 1;
543 +
    shl w32 %257 %256 1;
544 +
    or w32 %258 %257 1;
545 +
    load w64 %259 %7 0;
546 +
    load w64 %260 %7 8;
547 +
    mul w32 %261 %258 1;
548 +
    call w64 %262 %259(%260, %261, 1);
549 +
    load w64 %263 %199 0;
550 +
    mul w32 %264 %255 1;
551 551
    jmp @append102(0);
552 -
  @append102(w32 %275)
553 -
    br.ult w32 %275 %274 @append103 @append104;
552 +
  @append102(w32 %265)
553 +
    br.ult w32 %265 %264 @append103 @append104;
554 554
  @append103
555 -
    add w64 %276 %273 %275;
556 -
    load w8 %277 %276 0;
557 -
    add w64 %278 %272 %275;
558 -
    store w8 %277 %278 0;
559 -
    add w32 %279 %275 1;
560 -
    jmp @append102(%279);
555 +
    add w64 %266 %263 %265;
556 +
    load w8 %267 %266 0;
557 +
    add w64 %268 %262 %265;
558 +
    store w8 %267 %268 0;
559 +
    add w32 %269 %265 1;
560 +
    jmp @append102(%269);
561 561
  @append104
562 -
    store w64 %272 %209 0;
563 -
    store w32 %268 %209 12;
562 +
    store w64 %262 %199 0;
563 +
    store w32 %258 %199 12;
564 564
    jmp @append.store100;
565 565
  @then105
566 566
    ret 20;
567 567
  @merge106
568 -
    load w32 %286 %209 12;
569 -
    br.ne w32 %286 5 @then107 @merge108;
568 +
    load w32 %276 %199 12;
569 +
    br.ne w32 %276 5 @then107 @merge108;
570 570
  @then107
571 571
    ret 21;
572 572
  @merge108
573 -
    load w32 %287 %209 8;
574 -
    br.ult w32 2 %287 @guard#pass111 @guard#trap112;
573 +
    load w32 %277 %199 8;
574 +
    br.ult w32 2 %277 @guard#pass111 @guard#trap112;
575 575
  @then109
576 576
    ret 22;
577 577
  @merge110
578 -
    load w32 %291 %209 8;
579 -
    br.ult w32 0 %291 @guard#pass115 @guard#trap116;
578 +
    load w32 %281 %199 8;
579 +
    br.ult w32 0 %281 @guard#pass115 @guard#trap116;
580 580
  @guard#pass111
581 -
    load w64 %288 %209 0;
582 -
    add w64 %289 %288 2;
583 -
    load w8 %290 %289 0;
584 -
    br.ne w8 %290 239 @then109 @merge110;
581 +
    load w64 %278 %199 0;
582 +
    add w64 %279 %278 2;
583 +
    load w8 %280 %279 0;
584 +
    br.ne w8 %280 239 @then109 @merge110;
585 585
  @guard#trap112
586 586
    ebreak;
587 587
    unreachable;
588 588
  @then113
589 589
    ret 23;
590 590
  @merge114
591 -
    copy %294 $main$nominal$arena;
592 -
    mul w32 %295 4 2;
593 -
    call w64 %296 $arenaAlloc(%294, %295, 4);
594 -
    reserve %297 16 8;
595 -
    store w64 %296 %297 0;
596 -
    store w32 0 %297 8;
597 -
    store w32 2 %297 12;
598 -
    load w32 %300 %297 8;
599 -
    load w32 %301 %297 12;
600 -
    br.ult w32 %300 %301 @append.store117 @append.grow118;
591 +
    copy %284 $main$nominal$arena;
592 +
    mul w32 %285 4 2;
593 +
    call w64 %286 $arenaAlloc(%284, %285, 4);
594 +
    reserve %287 16 8;
595 +
    store w64 %286 %287 0;
596 +
    store w32 0 %287 8;
597 +
    store w32 2 %287 12;
598 +
    load w32 %290 %287 8;
599 +
    load w32 %291 %287 12;
600 +
    br.ult w32 %290 %291 @append.store117 @append.grow118;
601 601
  @guard#pass115
602 -
    load w64 %292 %209 0;
603 -
    load w8 %293 %292 0;
604 -
    br.ne w8 %293 171 @then113 @merge114;
602 +
    load w64 %282 %199 0;
603 +
    load w8 %283 %282 0;
604 +
    br.ne w8 %283 171 @then113 @merge114;
605 605
  @guard#trap116
606 606
    ebreak;
607 607
    unreachable;
608 608
  @append.store117
609 -
    load w64 %315 %297 0;
610 -
    mul w64 %316 %300 4;
611 -
    add w64 %317 %315 %316;
612 -
    store w32 42 %317 0;
613 -
    add w32 %318 %300 1;
614 -
    store w32 %318 %297 8;
615 -
    blit %297 %297 16;
616 -
    load w32 %321 %297 8;
617 -
    br.eq w32 %321 1 @assert.ok123 @assert.fail122;
609 +
    load w64 %305 %287 0;
610 +
    mul w64 %306 %290 4;
611 +
    add w64 %307 %305 %306;
612 +
    store w32 42 %307 0;
613 +
    add w32 %308 %290 1;
614 +
    store w32 %308 %287 8;
615 +
    blit %287 %287 16;
616 +
    load w32 %311 %287 8;
617 +
    br.eq w32 %311 1 @assert.ok123 @assert.fail122;
618 618
  @append.grow118
619 -
    shl w32 %302 %301 1;
620 -
    or w32 %303 %302 1;
621 -
    load w64 %304 %7 0;
622 -
    load w64 %305 %7 8;
623 -
    mul w32 %306 %303 4;
624 -
    call w64 %307 %304(%305, %306, 4);
625 -
    load w64 %308 %297 0;
626 -
    mul w32 %309 %300 4;
619 +
    shl w32 %292 %291 1;
620 +
    or w32 %293 %292 1;
621 +
    load w64 %294 %7 0;
622 +
    load w64 %295 %7 8;
623 +
    mul w32 %296 %293 4;
624 +
    call w64 %297 %294(%295, %296, 4);
625 +
    load w64 %298 %287 0;
626 +
    mul w32 %299 %290 4;
627 627
    jmp @append119(0);
628 -
  @append119(w32 %310)
629 -
    br.ult w32 %310 %309 @append120 @append121;
628 +
  @append119(w32 %300)
629 +
    br.ult w32 %300 %299 @append120 @append121;
630 630
  @append120
631 -
    add w64 %311 %308 %310;
632 -
    load w8 %312 %311 0;
633 -
    add w64 %313 %307 %310;
634 -
    store w8 %312 %313 0;
635 -
    add w32 %314 %310 1;
636 -
    jmp @append119(%314);
631 +
    add w64 %301 %298 %300;
632 +
    load w8 %302 %301 0;
633 +
    add w64 %303 %297 %300;
634 +
    store w8 %302 %303 0;
635 +
    add w32 %304 %300 1;
636 +
    jmp @append119(%304);
637 637
  @append121
638 -
    store w64 %307 %297 0;
639 -
    store w32 %303 %297 12;
638 +
    store w64 %297 %287 0;
639 +
    store w32 %293 %287 12;
640 640
    jmp @append.store117;
641 641
  @assert.fail122
642 642
    unreachable;
643 643
  @assert.ok123
644 -
    load w32 %322 %297 8;
645 -
    br.ult w32 0 %322 @guard#pass126 @guard#trap127;
644 +
    load w32 %312 %287 8;
645 +
    br.ult w32 0 %312 @guard#pass126 @guard#trap127;
646 646
  @assert.fail124
647 647
    unreachable;
648 648
  @assert.ok125
649 -
    load w32 %327 %297 8;
650 -
    load w32 %328 %297 12;
651 -
    br.ult w32 %327 %328 @append.store128 @append.grow129;
649 +
    load w32 %317 %287 8;
650 +
    load w32 %318 %287 12;
651 +
    br.ult w32 %317 %318 @append.store128 @append.grow129;
652 652
  @guard#pass126
653 -
    load w64 %323 %297 0;
654 -
    sload w32 %324 %323 0;
655 -
    br.eq w32 %324 42 @assert.ok125 @assert.fail124;
653 +
    load w64 %313 %287 0;
654 +
    sload w32 %314 %313 0;
655 +
    br.eq w32 %314 42 @assert.ok125 @assert.fail124;
656 656
  @guard#trap127
657 657
    ebreak;
658 658
    unreachable;
659 659
  @append.store128
660 -
    load w64 %342 %297 0;
661 -
    mul w64 %343 %327 4;
662 -
    add w64 %344 %342 %343;
663 -
    store w32 43 %344 0;
664 -
    add w32 %345 %327 1;
665 -
    store w32 %345 %297 8;
666 -
    blit %297 %297 16;
667 -
    load w32 %348 %297 8;
668 -
    br.eq w32 %348 2 @assert.ok134 @assert.fail133;
660 +
    load w64 %332 %287 0;
661 +
    mul w64 %333 %317 4;
662 +
    add w64 %334 %332 %333;
663 +
    store w32 43 %334 0;
664 +
    add w32 %335 %317 1;
665 +
    store w32 %335 %287 8;
666 +
    blit %287 %287 16;
667 +
    load w32 %338 %287 8;
668 +
    br.eq w32 %338 2 @assert.ok134 @assert.fail133;
669 669
  @append.grow129
670 -
    shl w32 %329 %328 1;
671 -
    or w32 %330 %329 1;
672 -
    load w64 %331 %7 0;
673 -
    load w64 %332 %7 8;
674 -
    mul w32 %333 %330 4;
675 -
    call w64 %334 %331(%332, %333, 4);
676 -
    load w64 %335 %297 0;
677 -
    mul w32 %336 %327 4;
670 +
    shl w32 %319 %318 1;
671 +
    or w32 %320 %319 1;
672 +
    load w64 %321 %7 0;
673 +
    load w64 %322 %7 8;
674 +
    mul w32 %323 %320 4;
675 +
    call w64 %324 %321(%322, %323, 4);
676 +
    load w64 %325 %287 0;
677 +
    mul w32 %326 %317 4;
678 678
    jmp @append130(0);
679 -
  @append130(w32 %337)
680 -
    br.ult w32 %337 %336 @append131 @append132;
679 +
  @append130(w32 %327)
680 +
    br.ult w32 %327 %326 @append131 @append132;
681 681
  @append131
682 -
    add w64 %338 %335 %337;
683 -
    load w8 %339 %338 0;
684 -
    add w64 %340 %334 %337;
685 -
    store w8 %339 %340 0;
686 -
    add w32 %341 %337 1;
687 -
    jmp @append130(%341);
682 +
    add w64 %328 %325 %327;
683 +
    load w8 %329 %328 0;
684 +
    add w64 %330 %324 %327;
685 +
    store w8 %329 %330 0;
686 +
    add w32 %331 %327 1;
687 +
    jmp @append130(%331);
688 688
  @append132
689 -
    store w64 %334 %297 0;
690 -
    store w32 %330 %297 12;
689 +
    store w64 %324 %287 0;
690 +
    store w32 %320 %287 12;
691 691
    jmp @append.store128;
692 692
  @assert.fail133
693 693
    unreachable;
694 694
  @assert.ok134
695 -
    load w32 %349 %297 8;
696 -
    br.ult w32 0 %349 @guard#pass135 @guard#trap136;
695 +
    load w32 %339 %287 8;
696 +
    br.ult w32 0 %339 @guard#pass135 @guard#trap136;
697 697
  @guard#pass135
698 -
    load w64 %350 %297 0;
699 -
    load w32 %353 %297 8;
700 -
    load w32 %354 %297 12;
701 -
    br.ult w32 %353 %354 @append.store137 @append.grow138;
698 +
    load w64 %340 %287 0;
699 +
    load w32 %343 %287 8;
700 +
    load w32 %344 %287 12;
701 +
    br.ult w32 %343 %344 @append.store137 @append.grow138;
702 702
  @guard#trap136
703 703
    ebreak;
704 704
    unreachable;
705 705
  @append.store137
706 -
    load w64 %368 %297 0;
707 -
    mul w64 %369 %353 4;
708 -
    add w64 %370 %368 %369;
709 -
    store w32 44 %370 0;
710 -
    add w32 %371 %353 1;
711 -
    store w32 %371 %297 8;
712 -
    blit %297 %297 16;
713 -
    load w32 %374 %297 8;
714 -
    br.eq w32 %374 3 @assert.ok143 @assert.fail142;
706 +
    load w64 %358 %287 0;
707 +
    mul w64 %359 %343 4;
708 +
    add w64 %360 %358 %359;
709 +
    store w32 44 %360 0;
710 +
    add w32 %361 %343 1;
711 +
    store w32 %361 %287 8;
712 +
    blit %287 %287 16;
713 +
    load w32 %364 %287 8;
714 +
    br.eq w32 %364 3 @assert.ok143 @assert.fail142;
715 715
  @append.grow138
716 -
    shl w32 %355 %354 1;
717 -
    or w32 %356 %355 1;
718 -
    load w64 %357 %7 0;
719 -
    load w64 %358 %7 8;
720 -
    mul w32 %359 %356 4;
721 -
    call w64 %360 %357(%358, %359, 4);
722 -
    load w64 %361 %297 0;
723 -
    mul w32 %362 %353 4;
716 +
    shl w32 %345 %344 1;
717 +
    or w32 %346 %345 1;
718 +
    load w64 %347 %7 0;
719 +
    load w64 %348 %7 8;
720 +
    mul w32 %349 %346 4;
721 +
    call w64 %350 %347(%348, %349, 4);
722 +
    load w64 %351 %287 0;
723 +
    mul w32 %352 %343 4;
724 724
    jmp @append139(0);
725 -
  @append139(w32 %363)
726 -
    br.ult w32 %363 %362 @append140 @append141;
725 +
  @append139(w32 %353)
726 +
    br.ult w32 %353 %352 @append140 @append141;
727 727
  @append140
728 -
    add w64 %364 %361 %363;
729 -
    load w8 %365 %364 0;
730 -
    add w64 %366 %360 %363;
731 -
    store w8 %365 %366 0;
732 -
    add w32 %367 %363 1;
733 -
    jmp @append139(%367);
728 +
    add w64 %354 %351 %353;
729 +
    load w8 %355 %354 0;
730 +
    add w64 %356 %350 %353;
731 +
    store w8 %355 %356 0;
732 +
    add w32 %357 %353 1;
733 +
    jmp @append139(%357);
734 734
  @append141
735 -
    store w64 %360 %297 0;
736 -
    store w32 %356 %297 12;
735 +
    store w64 %350 %287 0;
736 +
    store w32 %346 %287 12;
737 737
    jmp @append.store137;
738 738
  @assert.fail142
739 739
    unreachable;
740 740
  @assert.ok143
741 -
    load w32 %375 %297 12;
742 -
    br.eq w32 %375 5 @assert.ok145 @assert.fail144;
741 +
    load w32 %365 %287 12;
742 +
    br.eq w32 %365 5 @assert.ok145 @assert.fail144;
743 743
  @assert.fail144
744 744
    unreachable;
745 745
  @assert.ok145
746 -
    load w32 %376 %297 8;
747 -
    br.ult w32 0 %376 @guard#pass146 @guard#trap147;
746 +
    load w32 %366 %287 8;
747 +
    br.ult w32 0 %366 @guard#pass146 @guard#trap147;
748 748
  @guard#pass146
749 -
    load w64 %377 %297 0;
750 -
    br.ne w64 %350 %377 @assert.ok149 @assert.fail148;
749 +
    load w64 %367 %287 0;
750 +
    br.ne w64 %340 %367 @assert.ok149 @assert.fail148;
751 751
  @guard#trap147
752 752
    ebreak;
753 753
    unreachable;
754 754
  @assert.fail148
755 755
    unreachable;
756 756
  @assert.ok149
757 -
    load w32 %380 %297 8;
758 -
    br.ult w32 0 %380 @guard#pass152 @guard#trap153;
757 +
    load w32 %370 %287 8;
758 +
    br.ult w32 0 %370 @guard#pass152 @guard#trap153;
759 759
  @assert.fail150
760 760
    unreachable;
761 761
  @assert.ok151
762 -
    load w32 %383 %297 8;
763 -
    br.ult w32 1 %383 @guard#pass156 @guard#trap157;
762 +
    load w32 %373 %287 8;
763 +
    br.ult w32 1 %373 @guard#pass156 @guard#trap157;
764 764
  @guard#pass152
765 -
    load w64 %381 %297 0;
766 -
    sload w32 %382 %381 0;
767 -
    br.eq w32 %382 42 @assert.ok151 @assert.fail150;
765 +
    load w64 %371 %287 0;
766 +
    sload w32 %372 %371 0;
767 +
    br.eq w32 %372 42 @assert.ok151 @assert.fail150;
768 768
  @guard#trap153
769 769
    ebreak;
770 770
    unreachable;
771 771
  @assert.fail154
772 772
    unreachable;
773 773
  @assert.ok155
774 -
    load w32 %388 %297 8;
775 -
    br.ult w32 2 %388 @guard#pass160 @guard#trap161;
774 +
    load w32 %378 %287 8;
775 +
    br.ult w32 2 %378 @guard#pass160 @guard#trap161;
776 776
  @guard#pass156
777 -
    load w64 %384 %297 0;
778 -
    mul w64 %385 1 4;
779 -
    add w64 %386 %384 %385;
780 -
    sload w32 %387 %386 0;
781 -
    br.eq w32 %387 43 @assert.ok155 @assert.fail154;
777 +
    load w64 %374 %287 0;
778 +
    mul w64 %375 1 4;
779 +
    add w64 %376 %374 %375;
780 +
    sload w32 %377 %376 0;
781 +
    br.eq w32 %377 43 @assert.ok155 @assert.fail154;
782 782
  @guard#trap157
783 783
    ebreak;
784 784
    unreachable;
785 785
  @assert.fail158
786 786
    unreachable;
787 787
  @assert.ok159
788 788
    ret 0;
789 789
  @guard#pass160
790 -
    load w64 %389 %297 0;
791 -
    mul w64 %390 2 4;
792 -
    add w64 %391 %389 %390;
793 -
    sload w32 %392 %391 0;
794 -
    br.eq w32 %392 44 @assert.ok159 @assert.fail158;
790 +
    load w64 %379 %287 0;
791 +
    mul w64 %380 2 4;
792 +
    add w64 %381 %379 %380;
793 +
    sload w32 %382 %381 0;
794 +
    br.eq w32 %382 44 @assert.ok159 @assert.fail158;
795 795
  @guard#trap161
796 796
    ebreak;
797 797
    unreachable;
798 798
}
test/tests/static.slice.offset.rad +1 -1
4 4
record Entry: Copy {
5 5
    a: i32,
6 6
    b: i32,
7 7
}
8 8
9 -
record Table: Copy {
9 +
record Table {
10 10
    scratch: *mut [Entry],
11 11
    entries: *mut [Entry],
12 12
    len: u32,
13 13
}
14 14
test/tests/trait.dispatch.rad +4 -4
1 1
record Acc: Copy {
2 2
    n: i32,
3 3
}
4 4
5 5
trait Ops {
6 -
    fn (*Ops) get() -> i32;
7 -
    fn (*mut Ops) put(n: i32);
6 +
    fn (&Ops) get() -> i32;
7 +
    fn (&mut Ops) put(n: i32);
8 8
}
9 9
10 10
instance Ops for Acc {
11 -
    fn (a: *Acc) get() -> i32 {
11 +
    fn (a: &Acc) get() -> i32 {
12 12
        return a.n;
13 13
    }
14 14
15 -
    fn (a: *mut Acc) put(n: i32) {
15 +
    fn (a: &mut Acc) put(n: i32) {
16 16
        set a.n = n;
17 17
    }
18 18
}
19 19
20 20
fn dispatch(o: *mut opaque Ops) -> i32 {