Restrict safe pointers to permanent storage

a9d3b2cff3482048ab6e8780e099708d68c5732f2435654c8485e6b571a13d65
Use borrows for call-scoped stack access and explicit unsafe pointers
for retained storage. Migrate compiler, library, and test callers to the
storage rules.

Scope unsafe operations to individual functions and preserve call
requirements in unsafe function types. Refresh the compiler seed and
bootstrap inputs.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent 55e5d92e
Makefile +3 -2
36 36
endif
37 37
38 38
# Compiler build
39 39
40 40
SEED      := seed/radiance.rv64
41 -
SEED_OPTS := $(STD) -pkg radiance -mod compiler/radiance.rad -entry radiance
41 +
COMPILER_SRC := compiler/radiance.rad compiler/radiance/codegen.rad
42 +
SEED_OPTS := $(STD) -pkg radiance $(patsubst %,-mod %,$(COMPILER_SRC)) -entry radiance
42 43
43 -
$(RAD_BIN): $(STD_LIB) compiler/radiance.rad | $(BIN_DIR)
44 +
$(RAD_BIN): $(STD_LIB) $(COMPILER_SRC) | $(BIN_DIR)
44 45
	@echo "radiance $(SEED) => $@"
45 46
	@$(EMU) $(EMU_FLAGS) -run $(SEED) $(SEED_OPTS) -o $@
46 47
47 48
$(BIN_DIR):
48 49
	@mkdir -p $@
compiler/radiance.rad +126 -140
1 1
//! Radiance compiler front-end.
2 +
mod codegen;
3 +
2 4
use std::mem;
3 5
use std::fmt;
4 6
use std::io;
5 7
use std::lang::alloc;
6 8
use std::lang::ast;
90 92
static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined;
91 93
92 94
/// Assembly source file extension.
93 95
constant ASM_SOURCE_EXT: *[u8] = ".ras";
94 96
/// Symbol name exported for startup code to call the semantic entry function.
95 -
constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default";
97 +
export constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default";
96 98
97 99
/// Usage string.
98 100
constant USAGE: *[u8] =
99 101
    "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> -o <output>\n";
100 102
167 169
record RootModule: Copy {
168 170
    entry: *module::ModuleEntry,
169 171
    ast: *mut ast::Node,
170 172
}
171 173
172 -
/// State carried by the streaming lowerer/codegen callback.
173 -
record CodegenSinkContext: Copy {
174 -
    /// RV64 generator receiving lowered functions.
175 -
    generator: *mut rv64::Generator,
176 -
    /// Arena holding the current function's lowered IL.
177 -
    fnArena: *mut alloc::Arena,
178 -
}
179 -
180 174
/// Entry handling for streamed code generation.
181 175
union CodegenEntryMode: Copy {
182 176
    /// Do not reserve an entry jump.
183 177
    None,
184 178
    /// Reserve and patch an entry jump to the `@default` function.
194 188
    /// How the generated program should handle entry.
195 189
    entryMode: CodegenEntryMode,
196 190
}
197 191
198 192
/// Print a driver error line.
199 -
fn error(msg: *[*[u8]]) -> Error {
193 +
fn error(msg: &[*[u8]]) -> Error {
200 194
    io::printError("radiance: ");
201 195
202 196
    for part, i in msg {
203 197
        io::printError(part);
204 198
        if i < msg.len - 1 {
208 202
    io::printError("\n");
209 203
    return Error::Other;
210 204
}
211 205
212 206
/// Print a log line for the given package.
213 -
fn pkgLog(pkg: *package::Package, msg: *[*[u8]]) {
207 +
fn pkgLog(pkg: &package::Package, msg: &[*[u8]]) {
214 208
    io::printError("radiance: ");
215 209
    io::printError(pkg.name);
216 210
    io::printError(": ");
217 211
218 212
    for part, i in msg {
244 238
        asmPathCount: 0,
245 239
    };
246 240
}
247 241
248 242
/// Register, load, and parse `path` within `pkg`.
249 -
fn processModule(
250 -
    pkg: *mut package::Package,
251 -
    graph: *mut module::ModuleGraph,
243 +
unsafe fn processModule(
244 +
    pkg: *unsafe mut package::Package,
245 +
    graph: &mut module::ModuleGraph,
252 246
    path: *[u8],
253 -
    nodeArena: *mut ast::NodeArena,
254 -
    sourceArena: *mut alloc::Arena
247 +
    nodeArena: &mut ast::NodeArena,
248 +
    sourceArena: &mut alloc::Arena
255 249
) throws (Error) {
256 -
    pkgLog(pkg, &["parsing", "(", path, ")", ".."]);
250 +
    pkgLog(&*pkg, &["parsing", "(", path, ")", ".."]);
257 251
258 -
    let moduleId = try package::registerModule(pkg, graph, path) catch {
252 +
    let moduleId = try package::registerModule(&mut *pkg, graph, path) catch {
259 253
        throw error(&["error registering module"]);
260 254
    };
261 255
    // Read file into remaining arena space.
262 256
    let buffer = alloc::remainingBuf(sourceArena);
263 257
    if buffer.len == 0 {
264 258
        throw error(&["fatal:", "source arena exhausted"]);
265 259
    }
266 -
    let source = unix::readFile(path, buffer) else {
260 +
    let sourceLen = unix::readFile(path, buffer) else {
267 261
        throw error(&["error reading file"]);
268 262
    };
263 +
    let source = &buffer[..sourceLen];
269 264
    if source.len == buffer.len {
270 265
        throw error(&["fatal:", "source arena too small, file truncated:", path]);
271 266
    }
272 267
    // Commit only what was read.
273 268
    alloc::commit(sourceArena, source.len);
282 277
        throw error(&["error setting source"]);
283 278
    };
284 279
}
285 280
286 281
/// Consume the next argument, or print an error and throw.
287 -
fn nextArg(args: *[*[u8]], idx: *mut u32, msg: *[*[u8]]) -> *[u8] throws (Error) {
282 +
fn nextArg(args: *[*[u8]], idx: &mut u32, msg: &[*[u8]]) -> *[u8] throws (Error) {
288 283
    set *idx += 1;
289 284
    if *idx >= args.len {
290 285
        throw error(msg);
291 286
    }
292 287
    return args[*idx];
293 288
}
294 289
295 290
/// Parse CLI arguments and return compilation context.
296 -
fn processCommand(
291 +
unsafe fn processCommand(
297 292
    args: *[*[u8]],
298 -
    arena: *mut ast::NodeArena
293 +
    arena: &mut ast::NodeArena
299 294
) -> CompileContext throws (Error) {
300 295
    let mut buildTest = false;
301 296
    let mut debugEnabled = false;
302 297
    let mut outputPath: ?*[u8] = nil;
303 298
    let mut dump = Dump::None;
327 322
        } else if mem::eq(arg, "-mod") {
328 323
            try nextArg(args, &mut idx, &["`-mod` requires a module path"]);
329 324
            let pkgIdx = currentPkgIdx else {
330 325
                throw error(&["`-mod` must follow a `-pkg` argument"]);
331 326
            };
332 -
            let input = &mut inputs[pkgIdx];
327 +
            let input: *unsafe mut PackageInput = &mut inputs[pkgIdx];
333 328
            if hasExtension(args[idx], ASM_SOURCE_EXT) {
334 329
                if input.asmPathCount >= MAX_ASM_MODULES {
335 330
                    throw error(&["too many assembly modules specified"]);
336 331
                }
337 332
                set input.asmPaths[input.asmPathCount] = args[idx];
346 341
        } else if mem::eq(arg, "-start") {
347 342
            try nextArg(args, &mut idx, &["`-start` requires an assembly path"]);
348 343
            let pkgIdx = currentPkgIdx else {
349 344
                throw error(&["`-start` must follow a `-pkg` argument"]);
350 345
            };
351 -
            let input = &mut inputs[pkgIdx];
346 +
            let input: *unsafe mut PackageInput = &mut inputs[pkgIdx];
352 347
            if input.startupPath <> nil {
353 348
                throw error(&["package", input.name, "has more than one startup file"]);
354 349
            }
355 350
            if not hasExtension(args[idx], ASM_SOURCE_EXT) {
356 351
                throw error(&["`-start` requires a `.ras` assembly file"]);
435 430
        debug: debugEnabled,
436 431
    };
437 432
    // Initialize and parse all packages.
438 433
    let mut sourceArena = alloc::new(&mut MODULE_SOURCES[..]);
439 434
    for i in 0..pkgCount {
440 -
        package::init(&mut ctx.packages[i], i as u16, ctx.inputs[i].name, &mut STRING_POOL);
435 +
        let name = ctx.inputs[i].name;
436 +
        package::init(&mut ctx.packages[i], i as u16, name, &mut STRING_POOL);
441 437
442 438
        for j in 0..ctx.inputs[i].radPathCount {
443 439
            let path = ctx.inputs[i].radPaths[j];
444 440
            try processModule(&mut ctx.packages[i], &mut ctx.graph, path, arena, &mut sourceArena);
445 441
        }
446 442
    }
447 443
    return ctx;
448 444
}
449 445
450 446
/// Get the entry package from the context.
451 -
fn getEntryPackage(ctx: *CompileContext) -> *package::Package throws (Error) {
447 +
unsafe fn getEntryPackage(ctx: *unsafe CompileContext) -> *unsafe package::Package throws (Error) {
452 448
    let entryIdx = ctx.entryPkgIdx else {
453 449
        throw error(&["no entry package specified"]);
454 450
    };
455 451
    return &ctx.packages[entryIdx];
456 452
}
457 453
458 454
/// Return the startup assembly path for the entry package, if one was supplied.
459 -
fn getEntryStartupPath(ctx: *CompileContext) -> ?*[u8] {
455 +
fn getEntryStartupPath(ctx: &CompileContext) -> ?*[u8] {
460 456
    let entryIdx = ctx.entryPkgIdx else {
461 457
        panic "getEntryStartupPath: no entry package";
462 458
    };
463 459
    return ctx.inputs[entryIdx].startupPath;
464 460
}
465 461
466 462
/// Get root module info from a package.
467 -
fn getRootModule(pkg: *package::Package, graph: *module::ModuleGraph) -> RootModule throws (Error) {
463 +
fn getRootModule(pkg: &package::Package, graph: &module::ModuleGraph) -> RootModule throws (Error) {
468 464
    let rootId = pkg.rootModuleId else {
469 465
        throw error(&["no root module found"]);
470 466
    };
471 467
    let rootEntry = module::get(graph, rootId) else {
472 468
        throw error(&["root module entry not found"]);
476 472
    };
477 473
    return RootModule { entry: rootEntry, ast: rootAst };
478 474
}
479 475
480 476
/// Dump the module graph.
481 -
fn dumpGraph(ctx: *CompileContext) {
477 +
unsafe fn dumpGraph(ctx: &CompileContext) {
482 478
    let mut arena = alloc::new(&mut MAIN_ARENA[..]);
483 479
    module::printer::printGraph(&ctx.graph, &mut arena);
484 480
}
485 481
486 482
/// Dump the parsed AST.
487 -
fn dumpAst(ctx: *CompileContext) throws (Error) {
483 +
unsafe fn dumpAst(ctx: *unsafe CompileContext) throws (Error) {
488 484
    let pkg = try getEntryPackage(ctx);
489 -
    let root = try getRootModule(pkg, &ctx.graph);
485 +
    let root = try getRootModule(&*pkg, &ctx.graph);
490 486
    let mut arena = alloc::new(&mut MAIN_ARENA[..]);
491 487
492 488
    ast::printer::printTree(root.ast, &mut arena);
493 489
}
494 490
495 491
/// Lower all packages into a single IL program.
496 492
/// Dependencies are lowered first, then the entry package.
497 -
fn lowerAllPackages(
498 -
    ctx: *mut CompileContext,
499 -
    res: *mut resolver::Resolver
493 +
unsafe fn lowerAllPackages(
494 +
    ctx: *unsafe mut CompileContext,
495 +
    res: *unsafe mut resolver::Resolver
500 496
) -> il::Program throws (Error) {
501 497
    let entryIdx = ctx.entryPkgIdx else {
502 498
        panic "lowerAllPackages: no entry package";
503 499
    };
504 500
    let entryPkg = &ctx.packages[entryIdx];
513 509
    // Finalize and return the unified program.
514 510
    return lower::finalize(&low);
515 511
}
516 512
517 513
/// Lower all packages into an existing lowerer.
518 -
fn lowerAllPackagesInto(
519 -
    ctx: *mut CompileContext,
520 -
    res: *mut resolver::Resolver,
521 -
    low: *mut lower::Lowerer
514 +
unsafe fn lowerAllPackagesInto(
515 +
    ctx: *unsafe mut CompileContext,
516 +
    res: *unsafe mut resolver::Resolver,
517 +
    low: &mut lower::Lowerer
522 518
) throws (Error) {
523 519
    let entryIdx = ctx.entryPkgIdx else {
524 520
        panic "lowerAllPackagesInto: no entry package";
525 521
    };
526 522
    // Lower all packages except entry.
527 523
    for i in 0..ctx.packageCount {
528 524
        if i <> entryIdx {
529 -
            try lowerPackage(ctx, res, low, &mut ctx.packages[i], false);
525 +
            try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[i], false);
530 526
        }
531 527
    }
532 528
    // Lower entry package.
533 -
    try lowerPackage(ctx, res, low, &mut ctx.packages[entryIdx], true);
529 +
    try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[entryIdx], true);
534 530
}
535 531
536 532
/// Lower all modules in a package into the lowerer accumulator.
537 -
fn lowerPackage(
538 -
    ctx: *CompileContext,
539 -
    res: *mut resolver::Resolver,
540 -
    low: *mut lower::Lowerer,
541 -
    pkg: *mut package::Package,
533 +
unsafe fn lowerPackage(
534 +
    ctx: *unsafe CompileContext,
535 +
    res: *unsafe mut resolver::Resolver,
536 +
    low: &mut lower::Lowerer,
537 +
    pkg: &mut package::Package,
542 538
    isEntry: bool
543 539
) throws (Error) {
544 540
    let rootId = pkg.rootModuleId else {
545 541
        throw error(&["no root module found"]);
546 542
    };
547 543
    // Set lowerer's package context for qualified name generation.
548 544
    // TODO: We shouldn't have to call this manually.
549 -
    lower::setPackage(low, &ctx.graph, pkg.name);
545 +
    lower::setPackage(&mut *low, &ctx.graph, pkg.name);
550 546
551 -
    try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg);
547 +
    try lowerModuleTreeInto(ctx, &mut *low, &ctx.graph, rootId, isEntry, pkg);
552 548
}
553 549
554 550
/// Recursively lower a module and all its children into the accumulator.
555 -
fn lowerModuleTreeInto(
556 -
    ctx: *CompileContext,
557 -
    low: *mut lower::Lowerer,
558 -
    graph: *module::ModuleGraph,
551 +
unsafe fn lowerModuleTreeInto(
552 +
    ctx: *unsafe CompileContext,
553 +
    low: &mut lower::Lowerer,
554 +
    graph: &module::ModuleGraph,
559 555
    modId: u16,
560 556
    isRoot: bool,
561 -
    pkg: *package::Package
557 +
    pkg: &package::Package
562 558
) throws (Error) {
563 559
    let entry = module::get(graph, modId) else {
564 560
        throw error(&["module entry not found"]);
565 561
    };
566 562
    let modAst = entry.ast else {
567 563
        throw error(&["module has no AST"]);
568 564
    };
569 -
    pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]);
565 +
    pkgLog(&*pkg, &["lowering", "(", entry.filePath, ")", ".."]);
570 566
571 -
    try lower::lowerModule(low, modId, modAst, isRoot) catch err {
567 +
    try lower::lowerModule(&mut *low, modId, modAst, isRoot) catch err {
572 568
        io::printError("radiance: ");
573 569
        io::printError("internal error during lowering: ");
574 570
        lower::printError(err);
575 571
        io::printError("\n");
576 572
577 573
        throw Error::Other;
578 574
    };
579 575
    // Recurse into children.
580 576
    for i in 0..entry.childrenLen {
581 577
        let childId = module::childAt(entry, i);
582 -
        try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg);
578 +
        try lowerModuleTreeInto(ctx, &mut *low, graph, childId, false, pkg);
583 579
    }
584 580
}
585 581
586 582
/// Build a scope access chain: a::b::c from a slice of identifiers.
587 -
fn synthScopeAccess(arena: *mut ast::NodeArena, path: *[*[u8]]) -> *ast::Node {
583 +
fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node {
588 584
    let mut result = ast::synthNode(
589 585
        arena,
590 586
        ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[0]))
591 587
    );
592 588
    for i in 1..path.len {
600 596
    }
601 597
    return result;
602 598
}
603 599
604 600
/// Check if a function declaration has the `@test` attribute and return its name if so.
605 -
fn getTestFnName(decl: *ast::FnDecl) -> ?*[u8] {
601 +
fn getTestFnName(decl: &ast::FnDecl) -> ?*[u8] {
606 602
    let attrs = decl.attrs else { return nil; };
607 603
    if not ast::attributesContains(&attrs, ast::Attribute::Test) {
608 604
        return nil;
609 605
    }
610 606
    let case ast::NodeValue::Ident(name) = decl.name.value
614 610
}
615 611
616 612
/// Scan a single module's AST for `@test` functions and append them to `tests`.
617 613
fn collectModuleTests(
618 614
    entry: *module::ModuleEntry,
619 -
    tests: *mut [TestDesc],
620 -
    testCount: *mut u32
615 +
    tests: &mut [TestDesc],
616 +
    testCount: &mut u32
621 617
) {
622 618
    let modAst = entry.ast else {
623 619
        return;
624 620
    };
625 621
    let case ast::NodeValue::Block(block) = modAst.value else {
640 636
        }
641 637
    }
642 638
}
643 639
644 640
/// Synthesize a `testing::test("mod", "name", mod::fn)` call for one test.
645 -
fn synthTestCall(arena: *mut ast::NodeArena, desc: *TestDesc) -> *ast::Node {
641 +
unsafe fn synthTestCall(arena: &mut ast::NodeArena, desc: &TestDesc) -> *ast::Node {
646 642
    let callee = synthScopeAccess(arena, &["testing", "test"]);
647 643
    let modStr = il::formatQualifiedName(
648 644
        &mut arena.arena,
649 645
        &desc.modPath[..desc.modPath.len - 1],
650 646
        desc.modPath[desc.modPath.len - 1]
682 678
///     ]);
683 679
/// }
684 680
/// ```
685 681
///
686 682
/// Uses `#`-prefixed names to avoid conflicts with user code.
687 -
fn generateTestRunner(
688 -
    ctx: *mut CompileContext,
689 -
    arena: *mut ast::NodeArena
683 +
unsafe fn generateTestRunner(
684 +
    ctx: *unsafe mut CompileContext,
685 +
    arena: &mut ast::NodeArena
690 686
) throws (Error) {
691 687
    let entryPkg = try getEntryPackage(ctx);
692 -
    let root = try getRootModule(entryPkg, &ctx.graph);
688 +
    let root = try getRootModule(&*entryPkg, &ctx.graph);
693 689
694 690
    // Collect all test functions across all modules.
695 691
    let mut tests: [TestDesc; MAX_TESTS] = undefined;
696 692
    let mut testCount: u32 = 0;
697 693
702 698
    }
703 699
    if testCount == 0 {
704 700
        throw error(&["fatal:", "no test functions found"]);
705 701
    }
706 702
    let mut countBuf: [u8; 10] = undefined;
707 -
    let countStr = fmt::formatU32(testCount, &mut countBuf[..]);
708 -
    pkgLog(entryPkg, &["found", countStr, "test(s)"]);
703 +
    let start = fmt::formatU32(testCount, &mut countBuf[..]);
704 +
    io::printError("radiance: ");
705 +
    io::printError(entryPkg.name);
706 +
    io::printError(": found ");
707 +
    io::printError(&countBuf[start..]);
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 713
    injectIntoBlock(root.ast, arena, fnDecl);
714 714
}
715 715
716 716
/// Synthesize the test entry point.
717 -
fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node {
717 +
unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[TestDesc]) -> *ast::Node {
718 718
    // Build array literal: `[testing::test(...), ...]`.
719 719
    let a = alloc::arenaAllocator(&mut arena.arena);
720 720
    let mut elements = ast::nodeSlice(arena, tests.len as u32);
721 721
    for i in 0..tests.len {
722 722
        elements.append(synthTestCall(arena, &tests[i]), a);
738 738
    // Build: `return testing::runAllTests(&[...]);`
739 739
    let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr });
740 740
    let bodyStmts = ast::nodeSlice(arena, 1).append(retStmt, a);
741 741
    let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts }));
742 742
743 -
    // Build: `fn #testMain() -> i32`
743 +
    // Build: `unsafe fn #testMain() -> i32`
744 744
    let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain")));
745 745
    let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer {
746 746
        width: 4, sign: ast::Signedness::Signed,
747 747
    }));
748 748
    let fnSig = ast::FnSig {
749 749
        params: ast::nodeSlice(arena, 0),
750 750
        returnType,
751 751
        throwList: ast::nodeSlice(arena, 0),
752 752
    };
753 753
754 -
    // `@default` attribute.
754 +
    // Entry and function safety attributes.
755 755
    let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default));
756 -
    let attrList = ast::nodeSlice(arena, 1).append(attrNode, a);
756 +
    let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe));
757 +
    let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a);
757 758
    let fnAttrs = ast::Attributes { list: attrList };
758 759
759 760
    return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl {
760 761
        name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs,
761 762
    }));
762 763
}
763 764
764 765
/// Append a declaration to a block node's statement list.
765 -
fn injectIntoBlock(
766 +
unsafe fn injectIntoBlock(
766 767
    blockNode: *mut ast::Node,
767 -
    arena: *mut ast::NodeArena,
768 +
    arena: &mut ast::NodeArena,
768 769
    decl: *ast::Node
769 770
) {
770 771
    let case ast::NodeValue::Block(block) = blockNode.value else {
771 772
        panic "injectIntoBlock: expected Block node";
772 773
    };
773 774
    let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena));
774 775
    set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts });
775 776
}
776 777
777 778
/// Write a self-contained RV64 image containing text and data sections.
778 -
fn writeImage(
779 +
unsafe fn writeImage(
779 780
    code: *[u32],
780 781
    roData: *[u8],
781 782
    rwData: *[u8],
782 783
    path: *[u8]
783 784
) -> bool {
784 785
    let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len);
785 -
    let headerWords = &header[..];
786 -
    let headerBytes = @sliceOf(headerWords.ptr as *u8, headerWords.len * rv64::WORD_SIZE as u32);
786 +
    let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32);
787 787
    let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32);
788 788
789 -
    return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]);
789 +
    let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420);
790 +
    if fd < 0 { return false; }
791 +
    let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes)
792 +
        and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData);
793 +
    let closed = unix::close(fd) == 0;
794 +
    return written and closed;
790 795
}
791 796
792 797
/// Write a data section to a file at `basePath` + `ext`.
793 798
/// Empty data truncates any stale sidecar left by an earlier build.
794 799
fn writeDataWithExt(
811 816
/// Serialize debug entries and write the `.debug` file.
812 817
/// Resolves module IDs to file paths via the module graph.
813 818
/// Format per entry is `{pc: u32,  offset: u32, filePath: [u8], NULL}`.
814 819
fn writeDebugInfo(
815 820
    entries: *[types::DebugEntry],
816 -
    graph: *module::ModuleGraph,
821 +
    graph: &module::ModuleGraph,
817 822
    basePath: *[u8],
818 -
    arena: *mut alloc::Arena
823 +
    arena: &mut alloc::Arena
819 824
) throws (Error) {
820 825
    if entries.len == 0 {
821 826
        return;
822 827
    }
823 828
    // Use remaining arena space as serialization buffer.
838 843
    }
839 844
    try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT);
840 845
}
841 846
842 847
/// Run the resolver on the parsed modules.
843 -
fn runResolver(ctx: *mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) {
848 +
unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) {
844 849
    let mut mainArena = alloc::new(&mut MAIN_ARENA[..]);
845 850
    let entryPkg = try getEntryPackage(ctx);
846 851
847 -
    pkgLog(entryPkg, &["resolving", ".."]);
852 +
    pkgLog(&*entryPkg, &["resolving", ".."]);
848 853
849 854
    let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData);
850 855
    let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData));
851 856
    let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount);
852 857
    let storage = resolver::ResolverStorage {
860 865
    // Build the semantic package list consumed by the resolver.
861 866
    let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined;
862 867
    let mut resolverPackageCount: u32 = 0;
863 868
    for i in 0..ctx.packageCount {
864 869
        let pkg = &ctx.packages[i];
865 -
        let root = try getRootModule(pkg, &ctx.graph);
870 +
        let root = try getRootModule(&*pkg, &ctx.graph);
866 871
867 872
        set resolverPkgs[resolverPackageCount] = resolver::Pkg {
868 873
            rootEntry: root.entry,
869 874
            rootAst: root.ast,
870 875
        };
879 884
        throw Error::Other;
880 885
    };
881 886
    if not resolver::success(&diags) {
882 887
        resolver::printer::printDiagnostics(&diags, &res);
883 888
        let mut countBuf: [u8; 10] = undefined;
884 -
        let countStr = fmt::formatU32(diags.errors.len, &mut countBuf[..]);
885 -
        throw error(&["failed:", countStr, "errors"]);
889 +
        let start = fmt::formatU32(diags.errors.len, &mut countBuf[..]);
890 +
        io::printError("radiance: failed: ");
891 +
        io::printError(&countBuf[start..]);
892 +
        io::printError(" errors\n");
893 +
        throw Error::Other;
886 894
    }
887 895
    return res;
888 896
}
889 897
890 -
/// Emit one lowered function to machine code and reclaim its IL arena.
891 -
fn generateLoweredFn(ctxPtr: *mut opaque, func: *il::Fn, role: lower::FnRole) {
892 -
    let ctx = ctxPtr as *mut CodegenSinkContext;
893 -
894 -
    match role {
895 -
        case lower::FnRole::Default => {
896 -
            rv64::recordFunctionAlias(ctx.generator, DEFAULT_ENTRY_SYMBOL);
897 -
            match ctx.generator.entryPatch {
898 -
                case rv64::EntryPatch::Reserved(_) => {
899 -
                    set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name);
900 -
                }
901 -
                // No entry jump was reserved: startup assembly calls the
902 -
                // default function through `DEFAULT_ENTRY_SYMBOL` instead.
903 -
                case rv64::EntryPatch::None => {}
904 -
            }
905 -
        }
906 -
        else => {}
907 -
    }
908 -
    rv64::generateFunction(ctx.generator, func, ctx.fnArena);
909 -
    alloc::reset(ctx.fnArena);
910 -
}
911 -
912 898
/// Assemble one `.ras` input and merge it into the active code generator.
913 899
///
914 900
/// Text symbols are appended to `generator`. Data emitted by the assembler is
915 901
/// copied into `ASM_RO_DATA_BUF` at `*asmDataLen`, and `*asmDataLen` is advanced
916 902
/// so the next assembly module receives the correct rodata base address.
917 -
fn assembleAsmModule(
918 -
    generator: *mut rv64::Generator,
919 -
    pkg: *package::Package,
903 +
unsafe fn assembleAsmModule(
904 +
    generator: &mut rv64::Generator,
905 +
    pkg: &package::Package,
920 906
    path: *[u8],
921 -
    asmDataLen: *mut u32,
922 -
    arena: *mut alloc::Arena
907 +
    asmDataLen: &mut u32,
908 +
    arena: &mut alloc::Arena
923 909
) throws (Error) {
924 -
    pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]);
910 +
    pkgLog(&*pkg, &["asm:", "parsing", "(", path, ")", ".."]);
925 911
926 -
    let source = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else {
912 +
    let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else {
927 913
        throw error(&["error reading assembly file"]);
928 914
    };
915 +
    let source = &ASM_SOURCE_BUF[..sourceLen];
929 916
    if source.len == ASM_SOURCE_BUF.len {
930 917
        throw error(&["fatal:", "assembly source too large:", path]);
931 918
    }
932 919
    let program = try asm::assemble(
933 920
        asm::scanner::SourceKind::File { path },
948 935
949 936
    rv64::addAssembly(generator, program);
950 937
}
951 938
952 939
/// Assemble all inputs collected in the package inputs.
953 -
fn assembleAsmInputs(
954 -
    ctx: *CompileContext,
955 -
    generator: *mut rv64::Generator,
956 -
    asmDataLen: *mut u32,
957 -
    arena: *mut alloc::Arena
940 +
unsafe fn assembleAsmInputs(
941 +
    ctx: &CompileContext,
942 +
    generator: &mut rv64::Generator,
943 +
    asmDataLen: &mut u32,
944 +
    arena: &mut alloc::Arena
958 945
) -> *[u8] throws (Error) {
959 946
    for i in 0..ctx.packageCount {
960 -
        let input = &ctx.inputs[i];
961 -
        for j in 0..input.asmPathCount {
947 +
        for j in 0..ctx.inputs[i].asmPathCount {
962 948
            try assembleAsmModule(
963 949
                generator,
964 950
                &ctx.packages[i],
965 -
                input.asmPaths[j],
951 +
                ctx.inputs[i].asmPaths[j],
966 952
                asmDataLen,
967 953
                arena
968 954
            );
969 955
        }
970 956
    }
971 957
    return &ASM_RO_DATA_BUF[..*asmDataLen];
972 958
}
973 959
974 960
/// Lower all packages while streaming each lowered function into RV64 codegen.
975 -
fn lowerAndGenerateAllPackages(
976 -
    ctx: *mut CompileContext,
977 -
    res: *mut resolver::Resolver,
978 -
    fnArena: *mut alloc::Arena,
961 +
unsafe fn lowerAndGenerateAllPackages(
962 +
    ctx: *unsafe mut CompileContext,
963 +
    res: *unsafe mut resolver::Resolver,
964 +
    fnArena: &mut alloc::Arena,
979 965
    codegenOptions: CodegenOptions
980 966
) -> rv64::Program throws (Error) {
981 967
    let entryIdx = ctx.entryPkgIdx else {
982 968
        panic "lowerAndGenerateAllPackages: no entry package";
983 969
    };
984 970
    let entryPkg = &ctx.packages[entryIdx];
985 -
    let startupPath = getEntryStartupPath(ctx);
971 +
    let startupPath = getEntryStartupPath(&*ctx);
986 972
    let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest };
987 973
    let storage = rv64::Storage {
988 974
        dataSyms: &mut CODEGEN_DATA_SYMS[..],
989 975
        dataSymEntries: &mut CODEGEN_DATA_SYM_ENTRIES[..],
990 976
    };
997 983
    }
998 984
    let mut generator = rv64::beginProgram(
999 985
        rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug },
1000 986
        &mut res.arena
1001 987
    );
1002 -
    let mut codegenCtx = CodegenSinkContext {
988 +
    let mut codegenCtx = codegen::Context {
1003 989
        generator: &mut generator,
1004 -
        fnArena,
990 +
        fnArena: fnArena as *unsafe mut alloc::Arena,
1005 991
    };
1006 992
    let mut low = lower::lowerer(
1007 -
        res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena, options
993 +
        res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options
1008 994
    );
1009 995
    set low.output = lower::FnOutput::Stream(lower::FnSink {
1010 -
        ctx: &mut codegenCtx as *mut opaque,
1011 -
        emitFn: generateLoweredFn,
996 +
        ctx: &mut codegenCtx as *unsafe mut opaque,
997 +
        emitFn: codegen::emit,
1012 998
    });
1013 999
    let mut asmDataLen: u32 = 0;
1014 1000
    if let path = startupPath {
1015 -
        try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena);
1001 +
        try assembleAsmModule(&mut generator, &*entryPkg, path, &mut asmDataLen, &mut res.arena);
1016 1002
    }
1017 1003
    try lowerAllPackagesInto(ctx, res, &mut low);
1018 -
    let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena);
1004 +
    let asmData = try assembleAsmInputs(&*ctx, &mut generator, &mut asmDataLen, &mut res.arena);
1019 1005
1020 1006
    match generator.entryPatch {
1021 1007
        case rv64::EntryPatch::Reserved(targetName) => {
1022 1008
            if targetName == nil {
1023 1009
                throw error(&["fatal:", "no default function found"]);
1024 1010
            }
1025 1011
        }
1026 1012
        else => {}
1027 1013
    }
1028 1014
    if let path = codegenOptions.logPath {
1029 -
        pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1015 +
        pkgLog(&*entryPkg, &["generating code", "(", path, ")", ".."]);
1030 1016
    }
1031 1017
    return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]);
1032 1018
}
1033 1019
1034 1020
/// Lower, optionally dump, and optionally generate binary output.
1035 -
fn compile(
1036 -
    ctx: *mut CompileContext,
1037 -
    res: *mut resolver::Resolver,
1038 -
    fnArena: *mut alloc::Arena
1021 +
unsafe fn compile(
1022 +
    ctx: *unsafe mut CompileContext,
1023 +
    res: *unsafe mut resolver::Resolver,
1024 +
    fnArena: &mut alloc::Arena
1039 1025
) throws (Error) {
1040 1026
    let entryPkg = try getEntryPackage(ctx);
1041 1027
    let mut out = sexpr::Output::Stdout;
1042 1028
1043 1029
    if ctx.dump == Dump::Il {
1061 1047
    // Generate binary output if path specified.
1062 1048
    let outPath = ctx.outputPath else {
1063 1049
        try lowerAllPackages(ctx, res);
1064 1050
        return;
1065 1051
    };
1066 -
    let startupPath = getEntryStartupPath(ctx);
1052 +
    let startupPath = getEntryStartupPath(&*ctx);
1067 1053
    let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions {
1068 1054
        logPath: outPath,
1069 1055
        debug: ctx.debug,
1070 1056
        entryMode: CodegenEntryMode::None
1071 1057
            if startupPath <> nil
1083 1069
1084 1070
    // Write debug info file if enabled.
1085 1071
    if ctx.debug {
1086 1072
        try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena);
1087 1073
    }
1088 -
    pkgLog(entryPkg, &["ok", "(", outPath, ")"]);
1074 +
    pkgLog(&*entryPkg, &["ok", "(", outPath, ")"]);
1089 1075
}
1090 1076
1091 -
@default fn main(env: *sys::Env) -> i32 {
1077 +
@default unsafe fn main(env: *sys::Env) -> i32 {
1092 1078
    let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]);
1093 1079
    let mut ctx = try processCommand(env.args, &mut arena) catch {
1094 1080
        return 1;
1095 1081
    };
1096 1082
    match ctx.dump {
compiler/radiance/codegen.rad added +39 -0
1 +
//! Streaming code generation with caller-managed context storage.
2 +
3 +
use std::arch::rv64;
4 +
use std::lang::alloc;
5 +
use std::lang::il;
6 +
use std::lang::lower;
7 +
8 +
/// State carried by the streaming lowerer/codegen callback.
9 +
export record Context: Copy {
10 +
    /// Generator receiving functions. It must remain valid for every callback.
11 +
    generator: *unsafe mut rv64::Generator,
12 +
    /// Arena for function IL. It must remain valid for every callback.
13 +
    fnArena: *unsafe mut alloc::Arena,
14 +
}
15 +
16 +
/// Emit one lowered function to machine code and reclaim its IL arena.
17 +
/// The context must point to a valid `Context` with live generator and arena storage.
18 +
export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *il::Fn, role: lower::FnRole) {
19 +
    let ctx = ctxPtr as *unsafe mut Context;
20 +
21 +
    match role {
22 +
        case lower::FnRole::Default => {
23 +
            rv64::recordFunctionAlias(&mut *ctx.generator, super::DEFAULT_ENTRY_SYMBOL);
24 +
            match ctx.generator.entryPatch {
25 +
                case rv64::EntryPatch::Reserved(_) => {
26 +
                    set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name);
27 +
                }
28 +
                // Startup assembly calls the default function through its
29 +
                // default entry symbol when no entry jump is reserved.
30 +
                case rv64::EntryPatch::None => {}
31 +
            }
32 +
        }
33 +
        else => {}
34 +
    }
35 +
    let generator = ctx.generator;
36 +
    let arena = ctx.fnArena;
37 +
    rv64::generateFunction(&mut *generator, func, &mut *arena);
38 +
    alloc::reset(&mut *ctx.fnArena);
39 +
}
lib/std/arch/rv64.rad +10 -9
209 209
    /// Entry jump patching state.
210 210
    entryPatch: EntryPatch,
211 211
}
212 212
213 213
/// Begin RV64 code generation for a program's global state.
214 -
export fn beginProgram(
214 +
export unsafe fn beginProgram(
215 215
    options: ProgramOptions,
216 -
    arena: *mut alloc::Arena
216 +
    arena: &mut alloc::Arena
217 217
) -> Generator {
218 218
    let mut e = try! emit::emitter(arena, options.debug);
219 219
220 220
    // Emit placeholder entry jump when requested.
221 221
    // We'll patch this at the end once we know where the function is.
232 232
        entryPatch: options.entryPatch,
233 233
    };
234 234
}
235 235
236 236
/// Generate code for one IL function.
237 -
export fn generateFunction(
238 -
    generator: *mut Generator,
237 +
export unsafe fn generateFunction(
238 +
    generator: &mut Generator,
239 239
    func: *il::Fn,
240 -
    arena: *mut alloc::Arena
240 +
    arena: &mut alloc::Arena
241 241
) {
242 242
    if func.isExtern {
243 243
        return;
244 244
    }
245 245
    let checkpoint = alloc::save(arena);
251 251
    // Reclaim unused memory after instruction selection.
252 252
    alloc::restore(arena, checkpoint);
253 253
}
254 254
255 255
/// Record an alternate name for the next function emitted.
256 -
export fn recordFunctionAlias(generator: *mut Generator, name: *[u8]) {
257 -
    emit::recordFuncOffsetAt(&mut generator.e, name, generator.e.codeLen);
256 +
export fn recordFunctionAlias(generator: &mut Generator, name: *[u8]) {
257 +
    let codeLen = generator.e.codeLen;
258 +
    emit::recordFuncOffsetAt(&mut generator.e, name, codeLen);
258 259
}
259 260
260 261
/// Add the text section of an assembled program to the generator.
261 262
///
262 263
/// This function snapshots the generator's current code length as the base
267 268
/// separate assembly inputs may reuse the same local names.
268 269
///
269 270
/// Non-text symbols are ignored here because assembled data is not appended to
270 271
/// the generator's text stream. The driver merges assembled data into the RO data
271 272
/// prefix separately and passes that data to [`finishProgram`].
272 -
export fn addAssembly(generator: *mut Generator, program: asm::Program) {
273 +
export fn addAssembly(generator: &mut Generator, program: asm::Program) {
273 274
    let baseIndex = generator.e.codeLen;
274 275
275 276
    for symbol in program.symbols {
276 277
        if symbol.section == asm::Section::Text {
277 278
            let index = baseIndex + ((symbol.offset as u32) / INSTR_SIZE as u32);
297 298
    }
298 299
}
299 300
300 301
/// Finish RV64 code generation and return the emitted program.
301 302
export fn finishProgram(
302 -
    generator: *mut Generator,
303 +
    generator: &mut Generator,
303 304
    globalData: *[il::Data],
304 305
    storage: Storage,
305 306
    roDataPrefix: *[u8],
306 307
    roDataBuf: *mut [u8],
307 308
    rwDataBuf: *mut [u8]
lib/std/arch/rv64/asm.rad +4 -4
452 452
}
453 453
454 454
/// Parser and emission state.
455 455
export record Assembler: Copy {
456 456
    /// Allocation arena for temporary assembler state.
457 -
    arena: *mut alloc::Arena,
457 +
    arena: *unsafe mut alloc::Arena,
458 458
    /// Assembler lexical scanner.
459 459
    scan: scanner::Scanner,
460 460
    /// Output text buffer.
461 461
    text: *mut [u32],
462 462
    /// Output data buffer.
478 478
    /// Absolute runtime address of data-section offset zero.
479 479
    dataBase: u32,
480 480
}
481 481
482 482
/// Assemble source using `dataBase` as the runtime address of the data-section.
483 -
export fn assemble(
483 +
export unsafe fn assemble(
484 484
    sourceKind: scanner::SourceKind,
485 485
    source: *[u8],
486 486
    textBuf: *mut [u32],
487 487
    dataBuf: *mut [u8],
488 -
    arena: *mut alloc::Arena,
488 +
    arena: &mut alloc::Arena,
489 489
    pool: *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);
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 502
    let mut a = Assembler {
503 -
        arena,
503 +
        arena: arena as *unsafe mut alloc::Arena,
504 504
        scan: scanner::scanner(sourceKind, source, pool),
505 505
        text: @sliceOf(textBuf.ptr, 0, textBuf.len),
506 506
        data: @sliceOf(dataBuf.ptr, 0, dataBuf.len),
507 507
        section: Section::Text,
508 508
        symbols: @sliceOf((symbols as *mut [Symbol]).ptr, 0, (symbols as *mut [Symbol]).len),
lib/std/arch/rv64/asm/emit.rad +20 -20
7 7
use std::collections::dict;
8 8
use std::lang::alloc;
9 9
use std::lang::gen;
10 10
11 11
/// Define a symbol at the current text or data offset.
12 -
export fn defineSymbol(a: *mut super::Assembler, name: *[u8]) {
12 +
export unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) {
13 13
    let idx = a.symbols.len;
14 14
    let offset: i32 = a.data.len as i32
15 15
        if a.section == super::Section::Data
16 16
        else a.text.len as i32 * rv64::INSTR_SIZE;
17 17
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(a.arena));
23 +
    }, alloc::arenaAllocator(&mut *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 -
export fn emitText(a: *mut super::Assembler, word: u32) throws (super::Error) {
29 -
    a.text.append(word, alloc::arenaAllocator(a.arena));
28 +
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
29 +
    a.text.append(word, alloc::arenaAllocator(&mut *a.arena));
30 30
}
31 31
32 32
/// Append `words` no-op instructions to the text section.
33 -
export fn emitTextPadding(a: *mut super::Assembler, words: u32) throws (super::Error) {
33 +
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
34 34
    for _ in 0..words {
35 35
        try emitText(a, encode::nop());
36 36
    }
37 37
}
38 38
39 39
/// Append one byte to the data section.
40 -
export fn emitByte(a: *mut super::Assembler, byte: u8) throws (super::Error) {
41 -
    a.data.append(byte, alloc::arenaAllocator(a.arena));
40 +
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
41 +
    a.data.append(byte, alloc::arenaAllocator(&mut *a.arena));
42 42
}
43 43
44 44
/// Emit a little-endian integer with `bytes` bytes.
45 -
fn emitDataInt(a: *mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
45 +
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
46 46
    for i in 0..bytes {
47 47
        try emitByte(a, ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8);
48 48
    }
49 49
}
50 50
51 51
/// Patch a little-endian integer with `bytes` bytes.
52 -
fn patchDataInt(a: *mut super::Assembler, offset: u32, bits: u64, bytes: u32) {
52 +
fn patchDataInt(a: &mut super::Assembler, offset: u32, bits: u64, bytes: u32) {
53 53
    for i in 0..bytes {
54 54
        set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8;
55 55
    }
56 56
}
57 57
58 58
/// Emit an integer data directive value.
59 -
export fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
59 +
export unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
60 60
    match width {
61 61
        case super::DataWidth::Word => try emitDataInt(a, value as u64, rv64::WORD_SIZE as u32),
62 62
        case super::DataWidth::Dword => try emitDataInt(a, value as u64, rv64::DWORD_SIZE as u32),
63 63
    }
64 64
}
65 65
66 66
/// Record a data-section symbol fixup and reserve its bytes.
67 -
export fn recordDataFixup(a: *mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) {
67 +
export unsafe fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) {
68 68
    let offset = a.data.len;
69 69
    match width {
70 70
        case super::DataWidth::Word => {
71 71
            recordFixup(a, target, super::FixupInfo::Word { offset });
72 72
            try emitDataInt(a, 0, rv64::WORD_SIZE as u32);
77 77
        }
78 78
    }
79 79
}
80 80
81 81
/// Record a pending symbol fixup.
82 -
fn recordFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
83 -
    a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena));
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));
84 84
}
85 85
86 86
/// Record a text fixup that must be resolved after all program text is known.
87 -
fn recordExternalFixup(a: *mut super::Assembler, fixup: super::Fixup) {
88 -
    a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena));
87 +
unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
88 +
    a.externalFixups.append(fixup, alloc::arenaAllocator(&mut *a.arena));
89 89
}
90 90
91 91
/// Record a text-section symbol fixup and reserve its instruction words.
92 -
export fn recordTextFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
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);
94 94
    try emitTextPadding(a, words);
95 95
}
96 96
97 97
/// Find a previously defined symbol by name.
98 -
fn findSymbol(a: *super::Assembler, name: *[u8]) -> ?super::Symbol {
98 +
fn findSymbol(a: &super::Assembler, name: *[u8]) -> ?super::Symbol {
99 99
    let idx = dict::get(&a.symbolMap, name)
100 100
        else return nil;
101 101
    return a.symbols[idx as u32];
102 102
}
103 103
104 104
/// Return the final address for a data symbol.
105 -
fn dataSymbolAddr(a: *super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) {
105 +
fn dataSymbolAddr(a: &super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) {
106 106
    if symbol.section <> super::Section::Data {
107 107
        throw super::Error::Invalid { offset: 0, message: "data address target must be in data section" };
108 108
    }
109 109
    return symbol.offset + (a.dataBase as i32);
110 110
}
111 111
112 112
/// Resolve final symbol references and patch all delayed output.
113 -
export fn finishProgram(a: *mut super::Assembler) throws (super::Error) {
113 +
export unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) {
114 114
    for i in 0..a.fixups.len {
115 115
        let fixup = a.fixups[i];
116 116
        let symbol = findSymbol(a, fixup.symbol) else {
117 117
            match fixup.info {
118 118
                case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => {
185 185
        case super::BranchOp::Bgt  => return encode::bgt(rs1, rs2, imm),
186 186
    }
187 187
}
188 188
189 189
/// Decode string literal escapes and emit the resulting data bytes.
190 -
export fn emitDecodedString(a: *mut super::Assembler, literal: *[u8]) throws (super::Error) {
190 +
export unsafe fn emitDecodedString(a: &mut super::Assembler, literal: *[u8]) throws (super::Error) {
191 191
    let raw = &literal[super::QUOTE_DELIM_LEN..literal.len - super::QUOTE_DELIM_LEN];
192 192
    let mut i: u32 = 0;
193 193
194 194
    while i < raw.len {
195 195
        if raw[i] == '\\' and i + 1 < raw.len {
lib/std/arch/rv64/asm/parser.rad +62 -62
19 19
    /// Signed byte offset preceding the base register.
20 20
    offset: i32,
21 21
}
22 22
23 23
/// Parse assembler source into the supplied assembler state.
24 -
export fn parseProgram(a: *mut super::Assembler) throws (super::Error) {
24 +
export unsafe fn parseProgram(a: &mut super::Assembler) throws (super::Error) {
25 25
    advance(a);
26 26
27 27
    while a.scan.current.kind <> scanner::TokenKind::Eof {
28 28
        try parseItem(a);
29 29
    }
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 +
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 +
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;
54 54
}
55 55
56 56
/// Create an error at the current token.
57 -
fn fail(a: *super::Assembler, message: *[u8]) -> super::Error {
57 +
fn fail(a: &super::Assembler, message: *[u8]) -> super::Error {
58 58
    return super::Error::Invalid { offset: a.scan.current.offset, message };
59 59
}
60 60
61 61
/// Create an error at `tok`.
62 62
fn failOnToken(tok: scanner::Token, message: *[u8]) -> super::Error {
63 63
    return super::Error::Invalid { offset: tok.offset, message };
64 64
}
65 65
66 66
/// Require that a data directive appears while assembling the data section.
67 -
fn expectDataSection(a: *super::Assembler, tok: scanner::Token) throws (super::Error) {
67 +
fn expectDataSection(a: &super::Assembler, tok: scanner::Token) throws (super::Error) {
68 68
    if a.section <> super::Section::Data {
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 +
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 +
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.
87 -
fn expectTerminator(a: *super::Assembler, message: *[u8]) throws (super::Error) {
87 +
fn expectTerminator(a: &super::Assembler, message: *[u8]) throws (super::Error) {
88 88
    if a.scan.current.kind <> scanner::TokenKind::Semicolon {
89 89
        throw fail(a, message);
90 90
    }
91 91
}
92 92
93 93
/// Require that `value` fits in i32.
94 -
fn expectI32Value(a: *super::Assembler, value: i64, message: *[u8]) -> i32 throws (super::Error) {
94 +
fn expectI32Value(a: &super::Assembler, value: i64, message: *[u8]) -> i32 throws (super::Error) {
95 95
    if value < -super::I32_MIN_MAGNITUDE or value > super::I32_MAX_VALUE {
96 96
        throw fail(a, message);
97 97
    }
98 98
    return value as i32;
99 99
}
100 100
101 101
/// Require that `value` fits in a signed 12-bit immediate field.
102 -
fn expectSmallImmValue(a: *super::Assembler, value: i64) -> i32 throws (super::Error) {
102 +
fn expectSmallImmValue(a: &super::Assembler, value: i64) -> i32 throws (super::Error) {
103 103
    if not encode::isSmallImm64(value) {
104 104
        throw fail(a, "immediate out of range");
105 105
    }
106 106
    return value as i32;
107 107
}
108 108
109 109
/// Define a label at the current text or data offset.
110 -
fn defineSymbol(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
110 +
unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
111 111
    if dict::get(&a.symbolMap, name) <> nil {
112 112
        throw failOnToken(tok, "duplicate label");
113 113
    }
114 114
    emit::defineSymbol(a, name);
115 115
}
116 116
117 117
/// Emit a parsed integer data value after applying source-level range checks.
118 -
fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
118 +
unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
119 119
    match width {
120 120
        case super::DataWidth::Word =>
121 121
            try emit::emitDataValue(a, (try expectI32Value(a, value, "word literal out of range")) as i64, width),
122 122
        case super::DataWidth::Dword =>
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 128
fn parseScopedName(
129 -
    a: *mut super::Assembler,
129 +
    a: &mut super::Assembler,
130 130
    kind: scanner::TokenKind,
131 131
    message: *[u8],
132 132
    trimPrefix: u32
133 133
) -> *[u8] throws (super::Error) {
134 134
    let first = try expectToken(a, kind, message);
141 141
    }
142 142
    return strings::intern(a.scan.pool, &a.scan.source[start..end]);
143 143
}
144 144
145 145
/// Parse a bare symbol name.
146 -
fn parseSymbolName(a: *mut super::Assembler) -> *[u8] throws (super::Error) {
146 +
fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
147 147
    return try parseScopedName(a, scanner::TokenKind::Ident, "expected symbol name", 0);
148 148
}
149 149
150 150
/// Return `true` when [`tok`] is any label token form.
151 151
fn isLabel(tok: scanner::TokenKind) -> bool {
152 152
    return tok == scanner::TokenKind::Label or tok == scanner::TokenKind::QuotedLabel;
153 153
}
154 154
155 155
/// Parse the contents of a quoted label token, decoding escapes as needed.
156 -
fn parseQuotedLabelName(a: *mut super::Assembler) -> *[u8] throws (super::Error) {
156 +
unsafe fn parseQuotedLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
157 157
    let tok = try expectToken(a, scanner::TokenKind::QuotedLabel, "expected label name");
158 158
    let rawStart = super::LABEL_SIGIL_LEN + super::QUOTE_DELIM_LEN;
159 159
    let raw = &tok.source[rawStart..tok.source.len - super::QUOTE_DELIM_LEN];
160 -
    let storage = try alloc::allocSlice(a.arena, 1, 1, raw.len) catch {
160 +
    let storage = try alloc::allocSlice(&mut *a.arena, 1, 1, raw.len) catch {
161 161
        panic "asm: out of memory allocating quoted label";
162 162
    } as *mut [u8];
163 163
    let len = fmt::unescapeString(raw, storage);
164 164
165 165
    return strings::intern(a.scan.pool, &storage[..len]);
166 166
}
167 167
168 168
/// Parse a label reference or definition name.
169 -
fn parseLabelName(a: *mut super::Assembler) -> *[u8] throws (super::Error) {
169 +
unsafe fn parseLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
170 170
    if a.scan.current.kind == scanner::TokenKind::QuotedLabel {
171 171
        return try parseQuotedLabelName(a);
172 172
    }
173 173
    return try parseScopedName(a, scanner::TokenKind::Label, "expected label name", super::LABEL_SIGIL_LEN);
174 174
}
175 175
176 176
/// Parse a directive name without its leading `.`.
177 -
fn parseDirectiveName(a: *mut super::Assembler) -> *[u8] throws (super::Error) {
177 +
fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) {
178 178
    let name = try expectToken(a, scanner::TokenKind::Directive, "expected directive name");
179 179
    return &name.source[super::DIRECTIVE_SIGIL_LEN..];
180 180
}
181 181
182 182
/// Parse one top-level assembler item.
183 -
fn parseItem(a: *mut super::Assembler) throws (super::Error) {
183 +
unsafe fn parseItem(a: &mut super::Assembler) throws (super::Error) {
184 184
    match a.scan.current.kind {
185 185
        case scanner::TokenKind::Ident => {
186 186
            let tok = a.scan.current;
187 187
            let name = try parseSymbolName(a);
188 188
            try parseInstruction(a, name, tok);
277 277
    };
278 278
    return super::CSRS[index].csr;
279 279
}
280 280
281 281
/// Parse an instruction after its mnemonic has already been consumed.
282 -
fn parseInstruction(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
282 +
unsafe fn parseInstruction(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
283 283
    if a.section <> super::Section::Text {
284 284
        throw failOnToken(tok, "instructions are only valid in the text section");
285 285
    }
286 286
    let form = lookupInstruction(name) else {
287 287
        throw failOnToken(tok, "unknown instruction");
316 316
        case super::InstructionEncoder::Upper { enc } => return try parseUpper(a, enc),
317 317
    }
318 318
}
319 319
320 320
/// Parse the `li` pseudo-instruction.
321 -
fn parseLi(a: *mut super::Assembler) throws (super::Error) {
321 +
unsafe fn parseLi(a: &mut super::Assembler) throws (super::Error) {
322 322
    let rd = try parseRegister(a);
323 323
    let value = try parseValue(a);
324 324
    if encode::isSmallImm64(value) {
325 325
        try emit::emitText(a, encode::addi(rd, rv64::ZERO, value as i32));
326 326
        return;
331 331
    try emit::emitText(a, encode::lui(rd, split.hi));
332 332
    try emit::emitText(a, encode::addi(rd, rd, split.lo));
333 333
}
334 334
335 335
/// Parse the `la` pseudo-instruction.
336 -
fn parseLa(a: *mut super::Assembler) throws (super::Error) {
336 +
unsafe fn parseLa(a: &mut super::Assembler) throws (super::Error) {
337 337
    let rd = try parseRegister(a);
338 338
    let target = try parseLabelName(a);
339 339
    let index = a.text.len;
340 340
341 341
    try emit::recordTextFixup(a, target, super::FixupInfo::Addr { rd, index }, 2);
342 342
}
343 343
344 344
/// Parse a CSR read-like instruction with destination register then CSR.
345 -
fn parseRdCsr(a: *mut super::Assembler, enc: fn(gen::Reg, u32) -> u32) throws (super::Error) {
345 +
unsafe fn parseRdCsr(a: &mut super::Assembler, enc: fn(gen::Reg, u32) -> u32) throws (super::Error) {
346 346
    let rd = try parseRegister(a);
347 347
    let csr = try parseCsr(a);
348 348
349 349
    try emit::emitText(a, enc(rd, csr));
350 350
}
351 351
352 352
/// Parse a CSR write-like instruction with CSR then source register.
353 -
fn parseCsrRs1(a: *mut super::Assembler, enc: fn(u32, gen::Reg) -> u32) throws (super::Error) {
353 +
unsafe fn parseCsrRs1(a: &mut super::Assembler, enc: fn(u32, gen::Reg) -> u32) throws (super::Error) {
354 354
    let csr = try parseCsr(a);
355 355
    let rs1 = try parseRegister(a);
356 356
357 357
    try emit::emitText(a, enc(csr, rs1));
358 358
}
359 359
360 360
/// Parse `csrrw`.
361 -
fn parseCsrrw(a: *mut super::Assembler) throws (super::Error) {
361 +
unsafe fn parseCsrrw(a: &mut super::Assembler) throws (super::Error) {
362 362
    let rd = try parseRegister(a);
363 363
    let csr = try parseCsr(a);
364 364
    let rs1 = try parseRegister(a);
365 365
366 366
    try emit::emitText(a, encode::csrrw(rd, csr, rs1));
367 367
}
368 368
369 369
/// Parse a CSR immediate instruction.
370 -
fn parseCsrsi(a: *mut super::Assembler) throws (super::Error) {
370 +
unsafe fn parseCsrsi(a: &mut super::Assembler) throws (super::Error) {
371 371
    let csr = try parseCsr(a);
372 372
    let imm = try parseValue(a);
373 373
    if imm < 0 or imm >= super::CSR_IMM_LIMIT {
374 374
        throw fail(a, "CSR immediate out of range");
375 375
    }
376 376
    try emit::emitText(a, encode::csrsi(csr, imm as u32));
377 377
}
378 378
379 379
/// Parse a two-register instruction.
380 -
fn parseRR(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg) -> u32) throws (super::Error) {
380 +
unsafe fn parseRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg) -> u32) throws (super::Error) {
381 381
    let rd = try parseRegister(a);
382 382
    let rs = try parseRegister(a);
383 383
384 384
    try emit::emitText(a, enc(rd, rs));
385 385
}
386 386
387 387
/// Parse a three-register instruction.
388 -
fn parseRRR(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32) throws (super::Error) {
388 +
unsafe fn parseRRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32) throws (super::Error) {
389 389
    let rd = try parseRegister(a);
390 390
    let rs1 = try parseRegister(a);
391 391
    let rs2 = try parseRegister(a);
392 392
393 393
    try emit::emitText(a, enc(rd, rs1, rs2));
394 394
}
395 395
396 396
/// Parse a register-register-immediate instruction.
397 -
fn parseRRI(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
397 +
unsafe fn parseRRI(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
398 398
    let rd = try parseRegister(a);
399 399
    let rs1 = try parseRegister(a);
400 400
    let imm = try parseSmallImm(a);
401 401
402 402
    try emit::emitText(a, enc(rd, rs1, imm));
403 403
}
404 404
405 405
/// Parse a shift-immediate instruction and enforce its RV64 shift bound.
406 -
fn parseShift(
407 -
    a: *mut super::Assembler,
406 +
unsafe fn parseShift(
407 +
    a: &mut super::Assembler,
408 408
    enc: fn(gen::Reg, gen::Reg, i32) -> u32,
409 409
    limit: i32,
410 410
    message: *[u8]
411 411
) throws (super::Error) {
412 412
    let rd = try parseRegister(a);
423 423
424 424
    try emit::emitText(a, enc(rd, rs1, shamt));
425 425
}
426 426
427 427
/// Parse a load instruction with a memory operand.
428 -
fn parseLoad(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
428 +
unsafe fn parseLoad(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
429 429
    let rd = try parseRegister(a);
430 430
    let memop = try parseMemory(a);
431 431
432 432
    try emit::emitText(a, enc(rd, memop.base, memop.offset));
433 433
}
434 434
435 435
/// Parse a store instruction with a memory operand.
436 -
fn parseStore(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
436 +
unsafe fn parseStore(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) {
437 437
    let rs2 = try parseRegister(a);
438 438
    let memop = try parseMemory(a);
439 439
440 440
    try emit::emitText(a, enc(rs2, memop.base, memop.offset));
441 441
}
442 442
443 443
/// Parse a two-register branch instruction.
444 -
fn parseBranch(a: *mut super::Assembler, op: super::BranchOp) throws (super::Error) {
444 +
unsafe fn parseBranch(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) {
445 445
    let rs1 = try parseRegister(a);
446 446
    let rs2 = try parseRegister(a);
447 447
448 448
    try parseBranchLabel(a, op, rs1, rs2);
449 449
}
450 450
451 451
/// Parse an optional label operand.
452 -
fn parseOptionalLabel(a: *mut super::Assembler) -> ?*[u8] throws (super::Error) {
452 +
unsafe fn parseOptionalLabel(a: &mut super::Assembler) -> ?*[u8] throws (super::Error) {
453 453
    if not isLabel(a.scan.current.kind) {
454 454
        return nil;
455 455
    }
456 456
    return try parseLabelName(a);
457 457
}
458 458
459 459
/// Parse a branch target as either a label fixup or immediate offset.
460 -
fn parseBranchLabel(a: *mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) {
460 +
unsafe fn parseBranchLabel(a: &mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) {
461 461
    let index = a.text.len;
462 462
    if let target = try parseOptionalLabel(a) {
463 463
        try emit::recordTextFixup(a, target, super::FixupInfo::Branch { op, rs1, rs2, index }, 1);
464 464
        return;
465 465
    }
466 466
    let imm = try parseBranchImm(a);
467 467
    try emit::emitText(a, emit::encodeBranch(op, rs1, rs2, imm));
468 468
}
469 469
470 470
/// Parse a branch-to-zero pseudo-instruction.
471 -
fn parseBranchZero(a: *mut super::Assembler, op: super::BranchOp) throws (super::Error) {
471 +
unsafe fn parseBranchZero(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) {
472 472
    let rs = try parseRegister(a);
473 473
    try parseBranchLabel(a, op, rs, rv64::ZERO);
474 474
}
475 475
476 476
/// Parse `jal` with an explicit destination register.
477 -
fn parseJal(a: *mut super::Assembler) throws (super::Error) {
477 +
unsafe fn parseJal(a: &mut super::Assembler) throws (super::Error) {
478 478
    let rd = try parseRegister(a);
479 479
    try parseJ(a, rd);
480 480
}
481 481
482 482
/// Parse a jump target for `jal` or a jump pseudo-instruction.
483 -
fn parseJ(a: *mut super::Assembler, rd: gen::Reg) throws (super::Error) {
483 +
unsafe fn parseJ(a: &mut super::Assembler, rd: gen::Reg) throws (super::Error) {
484 484
    let index = a.text.len;
485 485
    if let target = try parseOptionalLabel(a) {
486 486
        try emit::recordTextFixup(a, target, super::FixupInfo::Jal { rd, index }, 1);
487 487
        return;
488 488
    }
489 489
    let imm = try parseJumpImm(a);
490 490
    try emit::emitText(a, encode::jal(rd, imm));
491 491
}
492 492
493 493
/// Parse an upper-immediate instruction.
494 -
fn parseUpper(a: *mut super::Assembler, enc: fn(gen::Reg, i32) -> u32) throws (super::Error) {
494 +
unsafe fn parseUpper(a: &mut super::Assembler, enc: fn(gen::Reg, i32) -> u32) throws (super::Error) {
495 495
    let rd = try parseRegister(a);
496 496
    let imm64 = try parseValue(a);
497 497
    if imm64 < 0 or imm64 > super::UPPER_IMM_MAX_VALUE {
498 498
        throw fail(a, "upper immediate out of range");
499 499
    }
500 500
    try emit::emitText(a, enc(rd, imm64 as i32));
501 501
}
502 502
503 503
/// Parse a directive after its name has already been consumed.
504 -
fn parseDirective(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
504 +
unsafe fn parseDirective(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
505 505
    let directive = classifyDirective(name) else {
506 506
        throw failOnToken(tok, "unknown directive");
507 507
    };
508 508
    match directive {
509 509
        case super::DirectiveKind::Text => {
544 544
        }
545 545
    }
546 546
}
547 547
548 548
/// Parse a `.constant` directive.
549 -
fn parseConstantDirective(a: *mut super::Assembler) throws (super::Error) {
549 +
fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) {
550 550
    let name = try parseSymbolName(a);
551 551
    let value = try expectI32Value(a, try parseExpr(a), "constant out of range");
552 552
553 553
    dict::insert(&mut a.constMap, name, value);
554 554
}
555 555
556 556
/// Parse a `.export` directive.
557 -
fn parseExportDirective(a: *mut super::Assembler) throws (super::Error) {
557 +
unsafe fn parseExportDirective(a: &mut super::Assembler) throws (super::Error) {
558 558
    let name = try parseLabelName(a);
559 559
    dict::insert(&mut a.exportMap, name, 1);
560 560
    if let idx = dict::get(&a.symbolMap, name) {
561 561
        set a.symbols[idx as u32].isExported = true;
562 562
    }
563 563
}
564 564
565 565
/// Parse a `.space` directive.
566 -
fn parseSpaceDirective(a: *mut super::Assembler) throws (super::Error) {
566 +
unsafe fn parseSpaceDirective(a: &mut super::Assembler) throws (super::Error) {
567 567
    let count = try parseValue(a);
568 568
    if count < 0 {
569 569
        throw fail(a, "space size must be non-negative");
570 570
    }
571 571
    // The data section grows on demand; only reject sizes that cannot be
577 577
        try emit::emitByte(a, 0);
578 578
    }
579 579
}
580 580
581 581
/// Parse an `.align` directive for the current section.
582 -
fn parseAlignDirective(a: *mut super::Assembler) throws (super::Error) {
582 +
unsafe fn parseAlignDirective(a: &mut super::Assembler) throws (super::Error) {
583 583
    let amount64 = try parseValue(a);
584 584
    if amount64 <= 0 {
585 585
        throw fail(a, "alignment must be positive");
586 586
    }
587 587
    if amount64 > super::U32_MAX_VALUE {
613 613
        }
614 614
    }
615 615
}
616 616
617 617
/// Parse a `.byte` directive.
618 -
fn parseByteDirective(a: *mut super::Assembler) throws (super::Error) {
618 +
unsafe fn parseByteDirective(a: &mut super::Assembler) throws (super::Error) {
619 619
    loop {
620 620
        if a.scan.current.kind == scanner::TokenKind::Char {
621 621
            let ch = parseCharLiteral(a.scan.current) else {
622 622
                throw fail(a, "invalid char literal");
623 623
            };
635 635
        }
636 636
    }
637 637
}
638 638
639 639
/// Parse a fixed-width integer data directive.
640 -
fn parseIntDirective(a: *mut super::Assembler, width: super::DataWidth) throws (super::Error) {
640 +
unsafe fn parseIntDirective(a: &mut super::Assembler, width: super::DataWidth) throws (super::Error) {
641 641
    loop {
642 642
        if isLabel(a.scan.current.kind) {
643 643
            let target = try parseLabelName(a);
644 644
            try emit::recordDataFixup(a, target, width);
645 645
        } else if a.scan.current.kind == scanner::TokenKind::Char {
656 656
        }
657 657
    }
658 658
}
659 659
660 660
/// Parse a `.ascii` string literal list.
661 -
fn parseStringDirective(a: *mut super::Assembler) throws (super::Error) {
661 +
unsafe fn parseStringDirective(a: &mut super::Assembler) throws (super::Error) {
662 662
    loop {
663 663
        let literal = try expectToken(a, scanner::TokenKind::String, "expected string literal");
664 664
        try emit::emitDecodedString(a, literal.source);
665 665
        if not consume(a, scanner::TokenKind::Comma) {
666 666
            return;
667 667
        }
668 668
    }
669 669
}
670 670
671 671
/// Parse and resolve a register operand.
672 -
fn parseRegister(a: *mut super::Assembler) -> gen::Reg throws (super::Error) {
672 +
fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) {
673 673
    let tok = try expectToken(a, scanner::TokenKind::Register, "expected register");
674 674
    let reg = lookupRegister(&tok.source[1..]) else {
675 675
        throw super::Error::Invalid { offset: tok.offset, message: "unknown register" };
676 676
    };
677 677
    return reg;
678 678
}
679 679
680 680
/// Parse a simple signed immediate or constant value.
681 -
fn parseValue(a: *mut super::Assembler) -> i64 throws (super::Error) {
681 +
fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
682 682
    if consume(a, scanner::TokenKind::Minus) {
683 683
        return -(try parseValuePrimary(a));
684 684
    }
685 685
    return try parseValuePrimary(a);
686 686
}
687 687
688 688
/// Parse the primary form used by simple immediate values.
689 -
fn parseValuePrimary(a: *mut super::Assembler) -> i64 throws (super::Error) {
689 +
fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
690 690
    if a.scan.current.kind == scanner::TokenKind::Number {
691 691
        return try parseInteger(a);
692 692
    }
693 693
    if a.scan.current.kind == scanner::TokenKind::Ident {
694 694
        return try parseConstantValue(a);
695 695
    }
696 696
    throw fail(a, "expected number or constant");
697 697
}
698 698
699 699
/// Parse an additive constant expression.
700 -
fn parseExpr(a: *mut super::Assembler) -> i64 throws (super::Error) {
700 +
fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) {
701 701
    let mut value = try parseExprMul(a);
702 702
703 703
    while a.scan.current.kind == scanner::TokenKind::Plus or a.scan.current.kind == scanner::TokenKind::Minus {
704 704
        let op = a.scan.current.kind;
705 705
        advance(a);
713 713
    }
714 714
    return value;
715 715
}
716 716
717 717
/// Parse multiplicative expression operators.
718 -
fn parseExprMul(a: *mut super::Assembler) -> i64 throws (super::Error) {
718 +
fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) {
719 719
    let mut value = try parseExprUnary(a);
720 720
721 721
    while a.scan.current.kind == scanner::TokenKind::Star or a.scan.current.kind == scanner::TokenKind::Slash {
722 722
        let op = a.scan.current.kind;
723 723
        advance(a);
734 734
    }
735 735
    return value;
736 736
}
737 737
738 738
/// Parse unary expression operators.
739 -
fn parseExprUnary(a: *mut super::Assembler) -> i64 throws (super::Error) {
739 +
fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) {
740 740
    if consume(a, scanner::TokenKind::Minus) {
741 741
        return -(try parseExprUnary(a));
742 742
    }
743 743
    if consume(a, scanner::TokenKind::Plus) {
744 744
        return try parseExprUnary(a);
745 745
    }
746 746
    return try parseExprPrimary(a);
747 747
}
748 748
749 749
/// Parse expression atoms.
750 -
fn parseExprPrimary(a: *mut super::Assembler) -> i64 throws (super::Error) {
750 +
fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) {
751 751
    if consume(a, scanner::TokenKind::LParen) {
752 752
        let value = try parseExpr(a);
753 753
        try expect(a, scanner::TokenKind::RParen, "expected `)`");
754 754
        return value;
755 755
    }
761 761
    }
762 762
    throw fail(a, "expected expression");
763 763
}
764 764
765 765
/// Parse and resolve a named assembler constant.
766 -
fn parseConstantValue(a: *mut super::Assembler) -> i64 throws (super::Error) {
766 +
fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) {
767 767
    let name = try parseSymbolName(a);
768 768
    let value = dict::get(&a.constMap, name) else {
769 769
        throw super::Error::Invalid { offset: a.scan.previous.offset, message: "undefined constant" };
770 770
    };
771 771
    return value as i64;
772 772
}
773 773
774 774
/// Parse and resolve a CSR operand.
775 -
fn parseCsr(a: *mut super::Assembler) -> u32 throws (super::Error) {
775 +
fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) {
776 776
    let name = try parseSymbolName(a);
777 777
    let csr = lookupCsr(name) else {
778 778
        throw super::Error::Invalid { offset: a.scan.previous.offset, message: "unknown CSR" };
779 779
    };
780 780
    return csr;
781 781
}
782 782
783 783
/// Parse an offset(base) memory operand.
784 -
fn parseMemory(a: *mut super::Assembler) -> MemOperand throws (super::Error) {
784 +
fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) {
785 785
    let mut offset: i32 = 0;
786 786
    if a.scan.current.kind <> scanner::TokenKind::LParen {
787 787
        set offset = try expectSmallImmValue(a, try parseValue(a));
788 788
    }
789 789
    try expect(a, scanner::TokenKind::LParen, "expected `(`");
792 792
793 793
    return MemOperand { base, offset };
794 794
}
795 795
796 796
/// Parse an immediate value that fits in a signed 12-bit field.
797 -
fn parseSmallImm(a: *mut super::Assembler) -> i32 throws (super::Error) {
797 +
fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
798 798
    return try expectSmallImmValue(a, try parseValue(a));
799 799
}
800 800
801 801
/// Parse and validate a branch immediate.
802 -
fn parseBranchImm(a: *mut super::Assembler) -> i32 throws (super::Error) {
802 +
fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
803 803
    let value = try expectI32Value(a, try parseValue(a), "branch immediate out of range");
804 804
    if not encode::isBranchImm(value) {
805 805
        throw fail(a, "branch immediate out of range");
806 806
    }
807 807
    return value;
808 808
}
809 809
810 810
/// Parse and validate a jump immediate.
811 -
fn parseJumpImm(a: *mut super::Assembler) -> i32 throws (super::Error) {
811 +
fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) {
812 812
    let value = try expectI32Value(a, try parseValue(a), "jump immediate out of range");
813 813
    if not encode::isJumpImm(value) {
814 814
        throw fail(a, "jump immediate out of range");
815 815
    }
816 816
    return value;
817 817
}
818 818
819 819
/// Parse an integer token as an i64.
820 -
fn parseInteger(a: *mut super::Assembler) -> i64 throws (super::Error) {
820 +
fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) {
821 821
    let tok = try expectToken(a, scanner::TokenKind::Number, "expected number");
822 822
    let value = parseIntegerText(tok.source) else {
823 823
        throw failOnToken(tok, "invalid integer literal");
824 824
    };
825 825
    return value;
lib/std/arch/rv64/asm/scanner.rad +17 -17
95 95
export fn invalid(offset: u32, message: *[u8]) -> Token {
96 96
    return Token { kind: TokenKind::Invalid, source: message, offset };
97 97
}
98 98
99 99
/// Return `true` when the scanner has consumed all input.
100 -
export fn isEof(s: *Scanner) -> bool {
100 +
export fn isEof(s: &Scanner) -> bool {
101 101
    return s.cursor >= s.source.len;
102 102
}
103 103
104 104
/// Return the current character without advancing.
105 -
fn current(s: *Scanner) -> ?u8 {
105 +
fn current(s: &Scanner) -> ?u8 {
106 106
    if isEof(s) {
107 107
        return nil;
108 108
    }
109 109
    return s.source[s.cursor];
110 110
}
111 111
112 112
/// Return the next character without advancing.
113 -
fn peek(s: *Scanner) -> ?u8 {
113 +
fn peek(s: &Scanner) -> ?u8 {
114 114
    if s.cursor + 1 >= s.source.len {
115 115
        return nil;
116 116
    }
117 117
    return s.source[s.cursor + 1];
118 118
}
119 119
120 120
/// Advance the scanner cursor and return the consumed character.
121 -
fn advance(s: *mut Scanner) -> u8 {
121 +
fn advance(s: &mut Scanner) -> u8 {
122 122
    set s.cursor += 1;
123 123
    return s.source[s.cursor - 1];
124 124
}
125 125
126 126
/// Consume `expected` when it is present at the current cursor.
127 -
fn consume(s: *mut Scanner, expected: u8) -> bool {
127 +
fn consume(s: &mut Scanner, expected: u8) -> bool {
128 128
    if let ch = current(s); ch == expected {
129 129
        advance(s);
130 130
        return true;
131 131
    }
132 132
    return false;
133 133
}
134 134
135 135
/// Skip spaces, newlines, tabs, and `//` line comments.
136 -
fn skipWhitespace(s: *mut Scanner) {
136 +
fn skipWhitespace(s: &mut Scanner) {
137 137
    while let ch = current(s) {
138 138
        match ch {
139 139
            case ' ', '\n', '\r', '\t' => advance(s),
140 140
            case '/' => {
141 141
                if let nextCh = peek(s); nextCh == '/' {
150 150
        }
151 151
    }
152 152
}
153 153
154 154
/// Return the next assembler token.
155 -
export fn next(s: *mut Scanner) -> Token {
155 +
export 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);
191 191
        else => return invalid(s.token, "unexpected character"),
192 192
    }
193 193
}
194 194
195 195
/// Create a token spanning the current scanner range.
196 -
fn tok(s: *Scanner, kind: TokenKind) -> Token {
196 +
fn tok(s: &Scanner, kind: TokenKind) -> Token {
197 197
    return Token { kind, source: &s.source[s.token..s.cursor], offset: s.token };
198 198
}
199 199
200 200
/// Scan the identifier continuation characters that follow the current token start.
201 -
fn scanIdentifierBody(s: *mut Scanner) {
201 +
fn scanIdentifierBody(s: &mut Scanner) {
202 202
    while let ch = current(s); char::isAlpha(ch) or char::isDigit(ch) or ch == '_' {
203 203
        advance(s);
204 204
    }
205 205
}
206 206
207 207
/// Scan a signed number when `+` or `-` is followed by a digit, otherwise return the punctuation token.
208 -
fn scanSignedNumberOrToken(s: *mut Scanner, kind: TokenKind) -> Token {
208 +
fn scanSignedNumberOrToken(s: &mut Scanner, kind: TokenKind) -> Token {
209 209
    if let nextCh = current(s); char::isDigit(nextCh) {
210 210
        return scanNumber(s);
211 211
    }
212 212
    return tok(s, kind);
213 213
}
214 214
215 215
/// Scan a numeric literal.
216 -
fn scanNumber(s: *mut Scanner) -> Token {
216 +
fn scanNumber(s: &mut Scanner) -> Token {
217 217
    let first = s.source[s.cursor - 1];
218 218
    if first == '-' or first == '+' {
219 219
        advance(s);
220 220
    }
221 221
    if s.source[s.cursor - 1] == '0' {
235 235
    }
236 236
    return tok(s, TokenKind::Number);
237 237
}
238 238
239 239
/// Scan a printable token terminated by `delim`.
240 -
fn scanCharsUntil(s: *mut Scanner, delim: u8, kind: TokenKind) -> ?Token {
240 +
fn scanCharsUntil(s: &mut Scanner, delim: u8, kind: TokenKind) -> ?Token {
241 241
    while let ch = current(s); ch <> delim {
242 242
        if not char::isPrint(ch) {
243 243
            return invalid(s.token, "invalid character");
244 244
        }
245 245
        if consume(s, '\\') {
254 254
    }
255 255
    return tok(s, kind);
256 256
}
257 257
258 258
/// Scan a string literal.
259 -
fn scanString(s: *mut Scanner) -> Token {
259 +
fn scanString(s: &mut Scanner) -> Token {
260 260
    if let token = scanCharsUntil(s, '"', TokenKind::String) {
261 261
        return token;
262 262
    }
263 263
    return invalid(s.token, "unterminated string");
264 264
}
265 265
266 266
/// Scan a character literal.
267 -
fn scanChar(s: *mut Scanner) -> Token {
267 +
fn scanChar(s: &mut Scanner) -> Token {
268 268
    if let token = scanCharsUntil(s, '\'', TokenKind::Char) {
269 269
        return token;
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 +
fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token {
276 276
    scanIdentifierBody(s);
277 277
278 278
    return Token {
279 279
        kind,
280 280
        source: strings::intern(s.pool, &s.source[s.token..s.cursor]),
281 281
        offset: s.token,
282 282
    };
283 283
}
284 284
285 285
/// Scan a sigil-prefixed identifier-shaped token.
286 -
fn scanPrefixedToken(s: *mut Scanner, kind: TokenKind, message: *[u8]) -> Token {
286 +
fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token {
287 287
    let ch = current(s) else {
288 288
        return invalid(s.token, message);
289 289
    };
290 290
    if not char::isAlpha(ch) and ch <> '_' {
291 291
        return invalid(s.token, message);
298 298
        offset: s.token,
299 299
    };
300 300
}
301 301
302 302
/// Scan an assembler label token, accepting either `@name` or `@"quoted"` syntax.
303 -
fn scanLabelToken(s: *mut Scanner) -> Token {
303 +
fn scanLabelToken(s: &mut Scanner) -> Token {
304 304
    let ch = current(s) else {
305 305
        return invalid(s.token, "expected label after `@`");
306 306
    };
307 307
    if ch == '"' {
308 308
        advance(s);
lib/std/arch/rv64/asm/tests.rad +15 -15
16 16
static ASM_DATA_STORAGE: [u8; 1024] = undefined;
17 17
static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18 18
static PRINT_ARENA_STORAGE: [u8; 1024] = undefined;
19 19
static PRINT_BUFFER: [u8; 128] = undefined;
20 20
21 -
fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) {
21 +
unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) {
22 22
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
23 23
    return try super::assemble(
24 24
        scanner::SourceKind::String,
25 25
        source,
26 26
        &mut ASM_TEXT_STORAGE[..],
31 31
    ) catch {
32 32
        throw testing::TestError::Failed;
33 33
    };
34 34
}
35 35
36 -
fn expectAssembleFail(source: *[u8]) throws (testing::TestError) {
36 +
unsafe fn expectAssembleFail(source: *[u8]) throws (testing::TestError) {
37 37
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
38 38
    try super::assemble(
39 39
        scanner::SourceKind::String,
40 40
        source,
41 41
        &mut ASM_TEXT_STORAGE[..],
47 47
        return;
48 48
    };
49 49
    throw testing::TestError::Failed;
50 50
}
51 51
52 -
fn printInstrText(instr: u32) -> *[u8] {
52 +
unsafe fn printInstrText(instr: u32) -> *[u8] {
53 53
    let mut arena = alloc::new(&mut PRINT_ARENA_STORAGE[..]);
54 54
    let mut pos: u32 = 0;
55 55
    let mut out = sexpr::Output::Buffer { buf: &mut PRINT_BUFFER[..], pos: &mut pos };
56 56
    printer::printInstr(&mut out, &mut arena, instr);
57 57
    return &PRINT_BUFFER[..pos];
58 58
}
59 59
60 -
@test fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) {
60 +
@test unsafe fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) {
61 61
    let program = try assembleSource(
62 62
        ".text;\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n"
63 63
    );
64 64
    try testing::expect(program.text.len == 2);
65 65
    try testing::expect(program.text[0] == encode::addi(rv64::A0, rv64::ZERO, 42));
66 66
    try testing::expect(program.text[1] == encode::sd(rv64::A0, rv64::SP, 8));
67 67
}
68 68
69 -
@test fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) {
69 +
@test unsafe fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) {
70 70
    let program = try assembleSource(
71 71
        ".text;\nla %t0 @value;\n.data;\n.byte 0;\n@value\n.byte 1;\n"
72 72
    );
73 73
    try testing::expect(program.text.len == 2);
74 74
    try testing::expect(program.text[0] == encode::lui(rv64::T0, 0x10));
75 75
    try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 1));
76 76
}
77 77
78 -
@test fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) {
78 +
@test unsafe fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) {
79 79
    let program = try assembleSource(
80 80
        ".text;\nla %t0 @target;\n@target\nret;\n"
81 81
    );
82 82
    try testing::expect(program.text.len == 3);
83 83
    try testing::expect(program.text[0] == encode::auipc(rv64::T0, 0));
84 84
    try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 8));
85 85
}
86 86
87 -
@test fn testAssembleQuotedLabelNames() throws (testing::TestError) {
87 +
@test unsafe fn testAssembleQuotedLabelNames() throws (testing::TestError) {
88 88
    let program = try assembleSource(
89 89
        ".text;\nj @\"foo.bar.baz\";\n@\"foo.bar.baz\"\nret;\n"
90 90
    );
91 91
    try testing::expect(program.text.len == 2);
92 92
    try testing::expect(program.text[0] == encode::jal(rv64::ZERO, 4));
93 93
    try testing::expect(program.text[1] == encode::jalr(rv64::ZERO, rv64::RA, 0));
94 94
}
95 95
96 -
@test fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) {
96 +
@test unsafe fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) {
97 97
    let program = try assembleSource(
98 98
        ".text;\n.export @exported;\n@local\nret;\n@exported\nret;\n@late\n.export @late;\nret;\n"
99 99
    );
100 100
    try testing::expect(program.symbols.len == 3);
101 101
    try testing::expect(not program.symbols[0].isExported);
102 102
    try testing::expect(program.symbols[1].isExported);
103 103
    try testing::expect(program.symbols[2].isExported);
104 104
}
105 105
106 -
@test fn testAssembleExternalTextFixups() throws (testing::TestError) {
106 +
@test unsafe fn testAssembleExternalTextFixups() throws (testing::TestError) {
107 107
    let program = try assembleSource(
108 108
        ".text;\ntail @\"::default\";\nla %t0 @\"::default\";\n"
109 109
    );
110 110
    try testing::expect(program.externalFixups.len == 2);
111 111
122 122
    try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default"));
123 123
    try testing::expect(addrRd == rv64::T0);
124 124
    try testing::expect(addrIndex == 1);
125 125
}
126 126
127 -
@test fn testAssembleInvalidOperandsFail() throws (testing::TestError) {
127 +
@test unsafe fn testAssembleInvalidOperandsFail() throws (testing::TestError) {
128 128
    try expectAssembleFail(
129 129
        ".text;\nbeq %a0 %a1 @missing;\n"
130 130
    );
131 131
    try expectAssembleFail(
132 132
        ".text;\naddi a0 zero 1;\n"
143 143
    try expectAssembleFail(
144 144
        ".data;\n.dword @missing;\n"
145 145
    );
146 146
}
147 147
148 -
@test fn testAssembleInvalidSyntaxFails() throws (testing::TestError) {
148 +
@test unsafe fn testAssembleInvalidSyntaxFails() throws (testing::TestError) {
149 149
    try expectAssembleFail(
150 150
        ".text;\n@dup\n@dup\nret;\n"
151 151
    );
152 152
    try expectAssembleFail(
153 153
        ".text;\naddi %a0, %zero, 1\n"
161 161
    try expectAssembleFail(
162 162
        ".export @kernel::main, @data::sym;\n"
163 163
    );
164 164
}
165 165
166 -
@test fn testAssembleInvalidSectionsFail() throws (testing::TestError) {
166 +
@test unsafe fn testAssembleInvalidSectionsFail() throws (testing::TestError) {
167 167
    try expectAssembleFail(
168 168
        ".data;\n.dword @target;\n.text;\n@target\nret;\n"
169 169
    );
170 170
    try expectAssembleFail(
171 171
        ".data;\naddi %a0 %zero 1;\n"
185 185
    try expectAssembleFail(
186 186
        ".data;\n@value\n.byte 1;\n.text;\nj @value;\n"
187 187
    );
188 188
}
189 189
190 -
@test fn testAssembleInvalidDirectivesFail() throws (testing::TestError) {
190 +
@test unsafe fn testAssembleInvalidDirectivesFail() throws (testing::TestError) {
191 191
    try expectAssembleFail(
192 192
        ".data;\n.ascii 'x';\n"
193 193
    );
194 194
    try expectAssembleFail(
195 195
        ".data;\n.byte 1 + 2;\n"
212 212
    try expectAssembleFail(
213 213
        ".data;\n.align 4294967296;\n"
214 214
    );
215 215
}
216 216
217 -
@test fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) {
217 +
@test unsafe fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) {
218 218
    try expectAssembleFail(
219 219
        ".text;\nslli %a0 %a1 64;\n"
220 220
    );
221 221
    try expectAssembleFail(
222 222
        ".text;\nslli %a0 %a1 4294967296;\n"
227 227
    try expectAssembleFail(
228 228
        ".text;\ncsrsi mstatus 32;\n"
229 229
    );
230 230
}
231 231
232 -
@test fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) {
232 +
@test unsafe fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) {
233 233
    let text = printInstrText(encode::addi(rv64::A0, rv64::SP, 42));
234 234
    try testing::expect(mem::eq(text, "addi    %a0, %sp, 42"));
235 235
}
lib/std/arch/rv64/emit.rad +43 -40
180 180
    }
181 181
    return frame;
182 182
}
183 183
184 184
/// Create a new emitter.
185 -
export fn emitter(arena: *mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) {
185 +
export unsafe fn emitter(arena: &mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) {
186 186
    let code = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_INSTRS);
187 187
    let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING);
188 188
    let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING);
189 189
    let pendingJumps = try alloc::allocSlice(arena, @sizeOf(PendingJump), @alignOf(PendingJump), MAX_PENDING);
190 190
    let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING);
216 216
///////////////////////
217 217
// Emission Helpers  //
218 218
///////////////////////
219 219
220 220
/// Emit a single instruction.
221 -
export fn emit(e: *mut Emitter, instr: u32) {
221 +
export fn emit(e: &mut Emitter, instr: u32) {
222 222
    assert e.codeLen < e.code.len, "emit: code buffer full";
223 223
    set e.code[e.codeLen] = instr;
224 224
    set e.codeLen += 1;
225 225
}
226 226
227 227
/// Compute branch offset to a function by name.
228 -
export fn branchOffsetToFunc(e: *Emitter, srcIndex: u32, name: *[u8]) -> i32 {
228 +
export fn branchOffsetToFunc(e: &Emitter, srcIndex: u32, name: *[u8]) -> i32 {
229 229
    return labels::branchToFunc(&e.labels, srcIndex, name, super::INSTR_SIZE);
230 230
}
231 231
232 232
/// Patch an instruction at a given index.
233 -
export fn patch(e: *mut Emitter, index: u32, instr: u32) {
233 +
export fn patch(e: &mut Emitter, index: u32, instr: u32) {
234 234
    set e.code[index] = instr;
235 235
}
236 236
237 237
/// Record a block's address for branch resolution.
238 -
export fn recordBlock(e: *mut Emitter, blockIdx: u32) {
238 +
export fn recordBlock(e: &mut Emitter, blockIdx: u32) {
239 239
    assert e.codeLen <= MAX_CODE_LEN;
240 240
    labels::recordBlock(&mut e.labels, blockIdx, e.codeLen as i32 * super::INSTR_SIZE);
241 241
}
242 242
243 243
/// Record a function's code offset for call resolution.
244 -
export fn recordFuncOffset(e: *mut Emitter, name: *[u8]) {
245 -
    recordFuncOffsetAt(e, name, e.codeLen);
244 +
export fn recordFuncOffset(e: &mut Emitter, name: *[u8]) {
245 +
    let codeLen = e.codeLen;
246 +
    recordFuncOffsetAt(e, name, codeLen);
246 247
}
247 248
248 249
/// Record a function's code offset at `index` for call resolution.
249 -
export fn recordFuncOffsetAt(e: *mut Emitter, name: *[u8], index: u32) {
250 +
export fn recordFuncOffsetAt(e: &mut Emitter, name: *[u8], index: u32) {
250 251
    assert index <= MAX_CODE_LEN;
251 252
    dict::insert(&mut e.labels.funcs, name, index as i32 * super::INSTR_SIZE);
252 253
}
253 254
254 255
/// Record a function's start position for printing.
255 -
export fn recordFunc(e: *mut Emitter, name: *[u8]) {
256 -
    recordFuncAt(e, name, e.codeLen);
256 +
export fn recordFunc(e: &mut Emitter, name: *[u8]) {
257 +
    let codeLen = e.codeLen;
258 +
    recordFuncAt(e, name, codeLen);
257 259
}
258 260
259 261
/// Record a function's start position at `index` for printing.
260 -
export fn recordFuncAt(e: *mut Emitter, name: *[u8], index: u32) {
262 +
export fn recordFuncAt(e: &mut Emitter, name: *[u8], index: u32) {
261 263
    e.funcs.append(types::FuncAddr { name, index }, e.allocator);
262 264
}
263 265
264 266
/// Record a local branch needing later patching.
265 267
/// Unconditional jumps use a single slot (J-type, +-1MB range).
266 268
/// Conditional branches use two slots (B-type has only +-4KB range,
267 269
/// so large functions may need the inverted-branch + JAL fallback).
268 -
export fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) {
270 +
export fn recordBranch(e: &mut Emitter, targetBlock: u32, kind: BranchKind) {
269 271
    e.pendingBranches.append(PendingBranch {
270 272
        index: e.codeLen,
271 273
        target: targetBlock,
272 274
        kind: kind,
273 275
    }, e.allocator);
281 283
}
282 284
283 285
/// Record a function call needing later patching.
284 286
/// Emits placeholder instructions that will be patched later.
285 287
/// Uses two slots to support long-distance calls.
286 -
export fn recordCall(e: *mut Emitter, target: *[u8]) {
288 +
export fn recordCall(e: &mut Emitter, target: *[u8]) {
287 289
    e.pendingCalls.append(PendingCall {
288 290
        index: e.codeLen,
289 291
        target,
290 292
    }, e.allocator);
291 293
292 294
    emit(e, encode::nop()); // Placeholder for AUIPC.
293 295
    emit(e, encode::nop()); // Placeholder for JALR.
294 296
}
295 297
296 298
/// Record a jump emitted by assembly that needs whole-program patching.
297 -
export fn recordJumpAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
299 +
export fn recordJumpAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
298 300
    e.pendingJumps.append(PendingJump {
299 301
        index,
300 302
        target,
301 303
        rd,
302 304
    }, e.allocator);
303 305
}
304 306
305 307
/// Record a function address load needing later patching.
306 308
/// Emits placeholder instructions that will be patched to load the function's address.
307 309
/// Uses two slots to compute long-distance addresses.
308 -
export fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) {
309 -
    recordAddrLoadAt(e, target, rd, e.codeLen);
310 +
export fn recordAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) {
311 +
    let codeLen = e.codeLen;
312 +
    recordAddrLoadAt(e, target, rd, codeLen);
310 313
311 314
    emit(e, encode::nop()); // Placeholder for AUIPC.
312 315
    emit(e, encode::nop()); // Placeholder for ADDI.
313 316
}
314 317
315 318
/// Record a function address load already reserved by assembly.
316 -
export fn recordAddrLoadAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
319 +
export fn recordAddrLoadAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) {
317 320
    e.pendingAddrLoads.append(PendingAddrLoad {
318 321
        index,
319 322
        target,
320 323
        rd: rd,
321 324
        isData: false,
322 325
    }, e.allocator);
323 326
}
324 327
325 328
/// Record a data address load needing later patching.
326 329
/// Uses an absolute 32-bit load sequence matching the current data memory map.
327 -
export fn recordDataAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) {
330 +
export fn recordDataAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) {
328 331
    e.pendingAddrLoads.append(PendingAddrLoad {
329 332
        index: e.codeLen,
330 333
        target,
331 334
        rd: rd,
332 335
        isData: true,
340 343
///
341 344
/// Called after each function.
342 345
///
343 346
/// Uses two-instruction sequences: short branches use `branch` and `nop`,
344 347
/// long branches use inverted branch  and `jal` or `auipc` and `jalr`.
345 -
export fn patchLocalBranches(e: *mut Emitter) {
348 +
export fn patchLocalBranches(e: &mut Emitter) {
346 349
    for i in 0..e.pendingBranches.len {
347 350
        let p = e.pendingBranches[i];
348 351
        let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE);
349 352
        match p.kind {
350 353
            case BranchKind::Cond { op, rs1, rs2 } => {
397 400
    }
398 401
}
399 402
400 403
/// Patch all pending function calls.
401 404
/// Called after all functions have been generated.
402 -
export fn patchCalls(e: *mut Emitter) {
405 +
export fn patchCalls(e: &mut Emitter) {
403 406
    for i in 0..e.pendingCalls.len {
404 407
        let p = e.pendingCalls[i];
405 408
        let offset = branchOffsetToFunc(e, p.index, p.target);
406 409
        let s = splitImm(offset);
407 410
411 414
        patch(e, p.index + 1, encode::jalr(super::RA, super::SCRATCH1, s.lo));
412 415
    }
413 416
}
414 417
415 418
/// Patch all pending assembly jumps.
416 -
export fn patchJumps(e: *mut Emitter) {
419 +
export fn patchJumps(e: &mut Emitter) {
417 420
    for i in 0..e.pendingJumps.len {
418 421
        let p = e.pendingJumps[i];
419 422
        let offset = branchOffsetToFunc(e, p.index, p.target);
420 423
421 424
        assert encode::isJumpImm(offset), "patchJumps: jump offset too large";
423 426
    }
424 427
}
425 428
426 429
/// Patch all pending function and data address loads.
427 430
/// Called after all functions have been generated and data layout is known.
428 -
export fn patchAddrLoads(e: *mut Emitter, dataSymMap: *data::DataSymMap) {
431 +
export fn patchAddrLoads(e: &mut Emitter, dataSymMap: &data::DataSymMap) {
429 432
    for i in 0..e.pendingAddrLoads.len {
430 433
        let p = e.pendingAddrLoads[i];
431 434
        if p.isData {
432 435
            let addr = data::lookupAddr(dataSymMap, p.target) else {
433 436
                panic "patchAddrLoads: data symbol not found";
479 482
/// Adjust a large offset by loading *hi* bits into [`super::ADDR_SCRATCH`].
480 483
/// Returns adjusted base register and remaining offset.
481 484
///
482 485
/// When the offset fits a 12-bit signed immediate, returns it unchanged.
483 486
/// Otherwise uses [`super::ADDR_SCRATCH`] for the LUI+ADD decomposition.
484 -
fn adjustOffset(e: *mut Emitter, base: gen::Reg, offset: i32) -> AdjustedOffset {
487 +
fn adjustOffset(e: &mut Emitter, base: gen::Reg, offset: i32) -> AdjustedOffset {
485 488
    if offset >= super::MIN_IMM and offset <= super::MAX_IMM {
486 489
        return AdjustedOffset { base, offset };
487 490
    }
488 491
    let s = splitImm(offset);
489 492
    emit(e, encode::lui(super::ADDR_SCRATCH, s.hi));
495 498
/// Load an immediate value into a register.
496 499
/// Handles the full range of 64-bit immediates.
497 500
/// For values fitting in 12 bits, uses a single `ADDI`.
498 501
/// For values fitting in 32 bits, uses `LUI` + `ADDIW`.
499 502
/// For wider values, loads upper and lower halves then combines with shift and add.
500 -
export fn loadImm(e: *mut Emitter, rd: gen::Reg, imm: i64) {
503 +
export fn loadImm(e: &mut Emitter, rd: gen::Reg, imm: i64) {
501 504
    let immMin = super::MIN_IMM as i64;
502 505
    let immMax = super::MAX_IMM as i64;
503 506
504 507
    if imm >= immMin and imm <= immMax {
505 508
        emit(e, encode::addi(rd, super::ZERO, imm as i32));
543 546
        emit(e, encode::addi(rd, rd, chunkLo));
544 547
    }
545 548
}
546 549
547 550
/// Emit add-immediate, handling large immediates.
548 -
export fn emitAddImm(e: *mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) {
551 +
export fn emitAddImm(e: &mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) {
549 552
    if imm >= super::MIN_IMM and imm <= super::MAX_IMM {
550 553
        emit(e, encode::addi(rd, rs, imm));
551 554
    } else {
552 555
        loadImm(e, super::SCRATCH1, imm as i64);
553 556
        emit(e, encode::add(rd, rs, super::SCRATCH1));
557 560
////////////////////////
558 561
// Load/Store Helpers //
559 562
////////////////////////
560 563
561 564
/// Emit unsigned load with automatic offset adjustment.
562 -
export fn emitLoad(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
565 +
export fn emitLoad(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
563 566
    let adj = adjustOffset(e, base, offset);
564 567
    match typ {
565 568
        case il::Type::W8 => emit(e, encode::lbu(rd, adj.base, adj.offset)),
566 569
        case il::Type::W16 => emit(e, encode::lhu(rd, adj.base, adj.offset)),
567 570
        case il::Type::W32 => emit(e, encode::lwu(rd, adj.base, adj.offset)),
568 571
        case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)),
569 572
    }
570 573
}
571 574
572 575
/// Emit signed load with automatic offset adjustment.
573 -
export fn emitSload(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
576 +
export fn emitSload(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
574 577
    let adj = adjustOffset(e, base, offset);
575 578
    match typ {
576 579
        case il::Type::W8 => emit(e, encode::lb(rd, adj.base, adj.offset)),
577 580
        case il::Type::W16 => emit(e, encode::lh(rd, adj.base, adj.offset)),
578 581
        case il::Type::W32 => emit(e, encode::lw(rd, adj.base, adj.offset)),
579 582
        case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)),
580 583
    }
581 584
}
582 585
583 586
/// Emit store with automatic offset adjustment.
584 -
export fn emitStore(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
587 +
export fn emitStore(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
585 588
    let adj = adjustOffset(e, base, offset);
586 589
    match typ {
587 590
        case il::Type::W8 => emit(e, encode::sb(rs, adj.base, adj.offset)),
588 591
        case il::Type::W16 => emit(e, encode::sh(rs, adj.base, adj.offset)),
589 592
        case il::Type::W32 => emit(e, encode::sw(rs, adj.base, adj.offset)),
590 593
        case il::Type::W64 => emit(e, encode::sd(rs, adj.base, adj.offset)),
591 594
    }
592 595
}
593 596
594 597
/// Emit 64-bit load with automatic offset adjustment.
595 -
export fn emitLd(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
598 +
export fn emitLd(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
596 599
    let adj = adjustOffset(e, base, offset);
597 600
    emit(e, encode::ld(rd, adj.base, adj.offset));
598 601
}
599 602
600 603
/// Emit 64-bit store with automatic offset adjustment.
601 -
export fn emitSd(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
604 +
export fn emitSd(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
602 605
    let adj = adjustOffset(e, base, offset);
603 606
    emit(e, encode::sd(rs, adj.base, adj.offset));
604 607
}
605 608
606 609
/// Emit 32-bit load with automatic offset adjustment.
607 -
export fn emitLw(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
610 +
export fn emitLw(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
608 611
    let adj = adjustOffset(e, base, offset);
609 612
    emit(e, encode::lw(rd, adj.base, adj.offset));
610 613
}
611 614
612 615
/// Emit 32-bit store with automatic offset adjustment.
613 -
export fn emitSw(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
616 +
export fn emitSw(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
614 617
    let adj = adjustOffset(e, base, offset);
615 618
    emit(e, encode::sw(rs, adj.base, adj.offset));
616 619
}
617 620
618 621
/// Emit 8-bit load with automatic offset adjustment.
619 -
export fn emitLb(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
622 +
export fn emitLb(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
620 623
    let adj = adjustOffset(e, base, offset);
621 624
    emit(e, encode::lb(rd, adj.base, adj.offset));
622 625
}
623 626
624 627
/// Emit 8-bit store with automatic offset adjustment.
625 -
export fn emitSb(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
628 +
export fn emitSb(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
626 629
    let adj = adjustOffset(e, base, offset);
627 630
    emit(e, encode::sb(rs, adj.base, adj.offset));
628 631
}
629 632
630 633
//////////////////////////
631 634
// Prologue / Epilogue  //
632 635
//////////////////////////
633 636
634 637
/// Emit function prologue.
635 638
/// Allocate the frame and save registers. Save FP only for dynamic frames.
636 -
export fn emitPrologue(e: *mut Emitter, frame: *Frame) {
639 +
export fn emitPrologue(e: &mut Emitter, frame: &Frame) {
637 640
    // Fast path: leaf function with no locals.
638 641
    if frame.totalSize == 0 {
639 642
        return;
640 643
    }
641 644
    let totalSize = frame.totalSize;
663 666
        emitSd(e, sr.reg, super::SP, sr.offset);
664 667
    }
665 668
}
666 669
667 670
/// Emit a return: jump to epilogue, or emit `ret` directly for leaf functions.
668 -
export fn emitReturn(e: *mut Emitter, frame: *Frame) {
671 +
export fn emitReturn(e: &mut Emitter, frame: &Frame) {
669 672
    if frame.totalSize == 0 {
670 673
        // Leaf function: no frame to tear down, emit ret directly.
671 674
        emit(e, encode::ret());
672 675
        return;
673 676
    }
674 677
    recordBranch(e, frame.epilogueBlock, BranchKind::Jump);
675 678
}
676 679
677 680
/// Emit function epilogue.
678 681
/// Restore saved registers and release the frame. Restore FP only for dynamic frames.
679 -
export fn emitEpilogue(e: *mut Emitter, frame: *Frame) {
682 +
export fn emitEpilogue(e: &mut Emitter, frame: &Frame) {
680 683
    // Record epilogue block address for return jumps.
681 684
    recordBlock(e, frame.epilogueBlock);
682 685
683 686
    // Fast path: leaf function with no locals.
684 687
    if frame.totalSize == 0 {
713 716
//////////////////
714 717
// Code Access  //
715 718
//////////////////
716 719
717 720
/// Get emitted code as a slice.
718 -
export fn getCode(e: *Emitter) -> *[u32] {
721 +
export fn getCode(e: &Emitter) -> *[u32] {
719 722
    return &e.code[..e.codeLen];
720 723
}
721 724
722 725
/// Record a debug entry mapping the current PC to a source location.
723 726
/// Deduplicates consecutive entries with the same location.
724 -
export fn recordSrcLoc(e: *mut Emitter, loc: il::SrcLoc) {
727 +
export fn recordSrcLoc(e: &mut Emitter, loc: il::SrcLoc) {
725 728
    let pc = e.codeLen * super::INSTR_SIZE as u32;
726 729
727 730
    // Skip if this is the same location as the previous entry.
728 731
    if e.debugEntriesLen > 0 {
729 732
        let prev = &e.debugEntries[e.debugEntriesLen - 1];
739 742
    };
740 743
    set e.debugEntriesLen += 1;
741 744
}
742 745
743 746
/// Get debug entries as a slice.
744 -
export fn getDebugEntries(e: *Emitter) -> *[types::DebugEntry] {
747 +
export fn getDebugEntries(e: &Emitter) -> *[types::DebugEntry] {
745 748
    return &e.debugEntries[..e.debugEntriesLen];
746 749
}
lib/std/arch/rv64/isel.rad +155 -153
84 84
////////////////////
85 85
86 86
/// Instruction selector state.
87 87
export record Selector: Copy {
88 88
    /// Emitter for outputting instructions.
89 -
    e: *mut emit::Emitter,
89 +
    e: *unsafe mut emit::Emitter,
90 90
    /// Register allocation result.
91 -
    ralloc: *regalloc::AllocResult,
91 +
    ralloc: *unsafe regalloc::AllocResult,
92 92
    /// Total stack frame size.
93 93
    frameSize: i32,
94 94
    /// Running offset into the reserve region of the frame.
95 95
    /// Tracks current position within the pre-allocated reserve slots.
96 96
    reserveOffset: i32,
105 105
/////////////////////////
106 106
// Register Allocation //
107 107
/////////////////////////
108 108
109 109
/// Get the physical register for an already-allocated SSA register.
110 -
fn getReg(s: *Selector, ssa: il::Reg) -> gen::Reg {
110 +
unsafe fn getReg(s: &Selector, ssa: il::Reg) -> gen::Reg {
111 111
    let phys = s.ralloc.assignments[ssa.n] else {
112 112
        panic "getReg: spilled register has no physical assignment";
113 113
    };
114 114
    return phys;
115 115
}
116 116
117 117
/// Compute the offset for a spill slot.
118 118
/// When using FP (dynamic): offset from FP = `slot - totalSize`.
119 119
/// When using SP: offset from SP = `slot`.
120 -
fn spillOffset(s: *Selector, slot: i32) -> i32 {
120 +
fn spillOffset(s: &Selector, slot: i32) -> i32 {
121 121
    if s.isDynamic {
122 122
        return slot - s.frameSize;
123 123
    }
124 124
    return slot;
125 125
}
126 126
127 127
/// Get the base register for spill slot addressing (FP or SP).
128 -
fn spillBase(s: *Selector) -> gen::Reg {
128 +
fn spillBase(s: &Selector) -> gen::Reg {
129 129
    if s.isDynamic {
130 130
        return super::FP;
131 131
    }
132 132
    return super::SP;
133 133
}
134 134
135 135
/// Get the destination register for an SSA register.
136 136
/// If the register is spilled, records a pending spill and returns the scratch
137 137
/// register. The pending spill is auto-committed by [`selectBlock`] after each
138 138
/// instruction. If not spilled, returns the physical register.
139 -
fn getDstReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
139 +
unsafe fn getDstReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
140 140
    if let _ = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
141 141
        set s.pendingSpill = PendingSpill { ssa, rd: scratch };
142 142
        return scratch;
143 143
    }
144 144
    return getReg(s, ssa);
145 145
}
146 146
147 147
/// Get the source register for an SSA register.
148 148
/// If the register is spilled, loads the value from the spill slot into the
149 149
/// scratch register and returns it. Otherwise returns the physical register.
150 -
fn getSrcReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
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(s.e, scratch, spillBase(s), spillOffset(s, slot));
152 +
        emit::emitLd(&mut *s.e, scratch, spillBase(s), spillOffset(s, slot));
153 153
        return scratch;
154 154
    }
155 155
    return getReg(s, ssa);
156 156
}
157 157
158 158
/// Resolve an IL value to the physical register holding it.
159 159
/// For non-spilled register values, returns the physical register directly.
160 160
/// For immediates, symbols, and spilled registers, materializes into `scratch`.
161 -
fn resolveVal(s: *mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg {
161 +
unsafe fn resolveVal(s: &mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg {
162 162
    match val {
163 163
        case il::Val::Reg(r) => {
164 164
            return getSrcReg(s, r, scratch);
165 165
        },
166 166
        case il::Val::Imm(imm) => {
167 167
            if imm == 0 {
168 168
                return super::ZERO;
169 169
            }
170 -
            emit::loadImm(s.e, scratch, imm);
170 +
            emit::loadImm(&mut *s.e, scratch, imm);
171 171
            return scratch;
172 172
        },
173 173
        case il::Val::DataSym(name) => {
174 -
            emit::recordDataAddrLoad(s.e, name, scratch);
174 +
            emit::recordDataAddrLoad(&mut *s.e, name, scratch);
175 175
            return scratch;
176 176
        },
177 177
        case il::Val::FnAddr(name) => {
178 -
            emit::recordAddrLoad(s.e, name, scratch);
178 +
            emit::recordAddrLoad(&mut *s.e, name, scratch);
179 179
            return scratch;
180 180
        },
181 181
        case il::Val::Undef => {
182 182
            return scratch;
183 183
        }
184 184
    }
185 185
}
186 186
187 187
/// Load an IL value into a specific physical register.
188 188
/// Like [`resolveVal`], but ensures the value ends up in `rd`.
189 -
fn loadVal(s: *mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg {
189 +
unsafe fn loadVal(s: &mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg {
190 190
    let rs = resolveVal(s, rd, val);
191 191
    emitMv(s, rd, rs);
192 192
    return rd;
193 193
}
194 194
195 195
/// Emit a move instruction if source and destination differ.
196 -
fn emitMv(s: *mut Selector, rd: gen::Reg, rs: gen::Reg) {
196 +
unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) {
197 197
    if *rd <> *rs {
198 -
        emit::emit(s.e, encode::mv(rd, rs));
198 +
        emit::emit(&mut *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 -
fn emitZext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
203 +
fn emitZext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
204 204
    match typ {
205 205
        case il::Type::W8 => emit::emit(e, encode::andi(rd, rs, MASK_W8)),
206 206
        case il::Type::W16 => {
207 207
            emit::emit(e, encode::slli(rd, rs, SHIFT_W16));
208 208
            emit::emit(e, encode::srli(rd, rd, SHIFT_W16));
214 214
        case il::Type::W64 => {}
215 215
    }
216 216
}
217 217
218 218
/// Emit sign-extension from a sub-word type to the full register width.
219 -
fn emitSext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
219 +
fn emitSext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
220 220
    match typ {
221 221
        case il::Type::W8 => {
222 222
            emit::emit(e, encode::slli(rd, rs, SHIFT_W8));
223 223
            emit::emit(e, encode::srai(rd, rd, SHIFT_W8));
224 224
        },
233 233
    }
234 234
}
235 235
236 236
/// Resolve a divisor in its declared width, trap if it becomes zero, and
237 237
/// return the canonicalized register.
238 -
fn resolveAndTrapIfZero(
239 -
    s: *mut Selector,
238 +
unsafe fn resolveAndTrapIfZero(
239 +
    s: &mut Selector,
240 240
    b: il::Val,
241 241
    typ: il::Type,
242 242
    signed: bool
243 243
) -> gen::Reg {
244 244
    let mut divisor = b;
245 245
    if let case il::Val::Imm(imm) = b {
246 246
        set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed));
247 247
    }
248 248
    let rs2 = resolveVal(s, super::SCRATCH2, divisor);
249 249
    if not isExtendedImm(divisor, typ, signed) {
250 -
        emitCmpExt(s.e, rs2, rs2, typ, signed);
250 +
        emitCmpExt(&mut *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(s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2));
258 -
        emit::emit(s.e, encode::ebreak());
257 +
        emit::emit(&mut *s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2));
258 +
        emit::emit(&mut *s.e, encode::ebreak());
259 259
    }
260 260
    return rs2;
261 261
}
262 262
263 263
////////////////////////
296 296
    }
297 297
    return ReserveInfo { size: offset, isDynamic };
298 298
}
299 299
300 300
/// Select instructions for a function.
301 -
export fn selectFn(
302 -
    e: *mut emit::Emitter,
303 -
    ralloc: *regalloc::AllocResult,
301 +
export unsafe fn selectFn(
302 +
    e: &mut emit::Emitter,
303 +
    ralloc: &regalloc::AllocResult,
304 304
    func: *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.
316 316
        isLeaf,
317 317
        reserveInfo.isDynamic
318 318
    );
319 319
    // Synthetic block indices start after real blocks and the epilogue block.
320 320
    let mut s = Selector {
321 -
        e, ralloc, frameSize: frame.totalSize,
321 +
        e: e as *unsafe mut emit::Emitter,
322 +
        ralloc: ralloc as *unsafe regalloc::AllocResult,
323 +
        frameSize: frame.totalSize,
322 324
        reserveOffset: 0, pendingSpill: nil,
323 325
        nextSynthBlock: func.blocks.len + 1,
324 326
        isDynamic: frame.isDynamic,
325 327
    };
326 328
    // Record function name for printing.
327 -
    emit::recordFunc(s.e, func.name);
329 +
    emit::recordFunc(&mut *s.e, func.name);
328 330
    // Record function code offset for call patching.
329 -
    emit::recordFuncOffset(s.e, func.name);
331 +
    emit::recordFuncOffset(&mut *s.e, func.name);
330 332
    // Emit prologue.
331 -
    emit::emitPrologue(s.e, &frame);
333 +
    emit::emitPrologue(&mut *s.e, &frame);
332 334
333 335
    // Move function params from arg registers to assigned registers.
334 336
    // Cross-call params may have been assigned to callee-saved registers
335 337
    // instead of their natural arg registers. Spilled params are stored
336 338
    // directly to their spill slots.
339 341
            let param = funcParam.value;
340 342
            let argReg = super::ARG_REGS[i];
341 343
342 344
            if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
343 345
                // Spilled parameter: store arg register to spill slot.
344 -
                emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot));
346 +
                emit::emitSd(&mut *s.e, argReg, spillBase(&s), spillOffset(&s, slot));
345 347
            } else if let assigned = ralloc.assignments[param.n] {
346 348
                emitMv(&mut s, assigned, argReg);
347 349
            }
348 350
        }
349 351
    }
351 353
    // Emit each block.
352 354
    for i in 0..func.blocks.len {
353 355
        selectBlock(&mut s, i, &func.blocks[i], &frame, func);
354 356
    }
355 357
    // Emit epilogue.
356 -
    emit::emitEpilogue(s.e, &frame);
358 +
    emit::emitEpilogue(&mut *s.e, &frame);
357 359
    // Patch local branches now that all blocks are emitted.
358 -
    emit::patchLocalBranches(s.e);
360 +
    emit::patchLocalBranches(&mut *s.e);
359 361
}
360 362
361 363
/// Select instructions for a block.
362 -
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: *il::Block, frame: &emit::Frame, func: *il::Fn) {
363 365
    // Record block address for branch patching.
364 -
    emit::recordBlock(s.e, blockIdx);
366 +
    emit::recordBlock(&mut *s.e, blockIdx);
365 367
366 368
    // Block parameters are handled at jump sites (in `Jmp`/`Br`).
367 369
    // By the time we enter the block, the arguments have already been
368 370
    // moved to the parameter registers by the predecessor's terminator.
369 371
370 372
    // Process each instruction, auto-committing any pending spill after each.
371 373
    let hasLocs = block.locs.len > 0;
372 374
    for instr, i in block.instrs {
373 375
        // Record debug location before emitting machine instructions.
374 376
        if hasLocs {
375 -
            emit::recordSrcLoc(s.e, block.locs[i]);
377 +
            emit::recordSrcLoc(&mut *s.e, block.locs[i]);
376 378
        }
377 379
        set s.pendingSpill = nil;
378 380
        selectInstr(s, blockIdx, instr, frame, func);
379 381
380 382
        // Flush the pending spill store, if any.
381 383
        if let p = s.pendingSpill {
382 384
            if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) {
383 -
                emit::emitSd(s.e, p.rd, spillBase(s), spillOffset(s, slot));
385 +
                emit::emitSd(&mut *s.e, p.rd, spillBase(s), spillOffset(s, slot));
384 386
            }
385 387
            set s.pendingSpill = nil;
386 388
        }
387 389
    }
388 390
}
389 391
390 392
/// Select instructions for a single IL instruction.
391 -
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: *il::Fn) {
392 394
    match instr {
393 395
        case il::Instr::BinOp { op, typ, dst, a, b } => {
394 396
            let rd = getDstReg(s, dst, super::SCRATCH1);
395 397
            let rs1 = resolveVal(s, super::SCRATCH1, a);
396 398
            selectAluBinOp(s, op, typ, rd, rs1, b);
401 403
            selectAluUnOp(s, op, typ, rd, rs);
402 404
        },
403 405
        case il::Instr::Load { typ, dst, src, offset } => {
404 406
            let rd = getDstReg(s, dst, super::SCRATCH1);
405 407
            let base = getSrcReg(s, src, super::SCRATCH2);
406 -
            emit::emitLoad(s.e, rd, base, offset, typ);
408 +
            emit::emitLoad(&mut *s.e, rd, base, offset, typ);
407 409
        },
408 410
        case il::Instr::Sload { typ, dst, src, offset } => {
409 411
            let rd = getDstReg(s, dst, super::SCRATCH1);
410 412
            let base = getSrcReg(s, src, super::SCRATCH2);
411 -
            emit::emitSload(s.e, rd, base, offset, typ);
413 +
            emit::emitSload(&mut *s.e, rd, base, offset, typ);
412 414
        },
413 415
        case il::Instr::Store { typ, src, dst, offset } => {
414 416
            let base = getSrcReg(s, dst, super::SCRATCH2);
415 417
            let rs = resolveVal(s, super::SCRATCH1, src);
416 -
            emit::emitStore(s.e, rs, base, offset, typ);
418 +
            emit::emitStore(&mut *s.e, rs, base, offset, typ);
417 419
        },
418 420
        case il::Instr::Copy { dst, val } => {
419 421
            let rd = getDstReg(s, dst, super::SCRATCH1);
420 422
            let rs = resolveVal(s, super::SCRATCH1, val);
421 423
            emitMv(s, rd, rs);
428 430
                    let aligned: i32 = mem::alignUpI32(s.reserveOffset, alignment as i32);
429 431
                    let base = spillBase(s);
430 432
                    let offset = s.ralloc.spill.frameSize + aligned
431 433
                        - (s.frameSize if s.isDynamic else 0);
432 434
433 -
                    emit::emitAddImm(s.e, rd, base, offset);
435 +
                    emit::emitAddImm(&mut *s.e, rd, base, offset);
434 436
                    set s.reserveOffset = aligned + (sz as i32);
435 437
                },
436 438
                case il::Val::Reg(r) => {
437 439
                    // Dynamic-sized reserve: runtime SP adjustment.
438 440
                    let rd = getDstReg(s, dst, super::SCRATCH1);
439 441
                    let rs = getSrcReg(s, r, super::SCRATCH2);
440 442
441 -
                    emit::emit(s.e, encode::sub(super::SP, super::SP, rs));
443 +
                    emit::emit(&mut *s.e, encode::sub(super::SP, super::SP, rs));
442 444
443 445
                    if alignment > 1 {
444 446
                        let mask = 0 - alignment as i32;
445 447
                        assert encode::isSmallImm(mask);
446 448
447 -
                        emit::emit(s.e, encode::andi(super::SP, super::SP, mask));
449 +
                        emit::emit(&mut *s.e, encode::andi(super::SP, super::SP, mask));
448 450
                    }
449 -
                    emit::emit(s.e, encode::mv(rd, super::SP));
451 +
                    emit::emit(&mut *s.e, encode::mv(rd, super::SP));
450 452
                },
451 453
                else =>
452 454
                    panic "selectInstr: invalid reserve operand",
453 455
            }
454 456
        },
474 476
                    panic "selectInstr: blit dst not spilled";
475 477
                };
476 478
                let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else {
477 479
                    panic "selectInstr: blit src not spilled";
478 480
                };
479 -
                emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot));
481 +
                emit::emitLd(&mut *s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot));
480 482
                set srcReload = spillOffset(s, srcSlot);
481 483
            } else {
482 484
                set rdst = getSrcReg(s, dst, super::SCRATCH2);
483 485
                set rsrc = getSrcReg(s, src, super::SCRATCH2);
484 486
            }
491 493
            let canLoop = not bothSpilled
492 494
                and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2
493 495
                and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2;
494 496
495 497
            if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD {
496 -
                emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes);
498 +
                emit::emitAddImm(&mut *s.e, super::SCRATCH1, rsrc, dwordBytes);
497 499
498 500
                let loopStart = s.e.codeLen;
499 501
500 -
                emit::emitLd(s.e, super::SCRATCH2, rsrc, 0);
501 -
                emit::emitSd(s.e, super::SCRATCH2, rdst, 0);
502 -
                emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE));
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));
503 505
504 506
                if *rdst <> *rsrc {
505 -
                    emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE));
507 +
                    emit::emit(&mut *s.e, encode::addi(rdst, rdst, super::DWORD_SIZE));
506 508
                }
507 509
                let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE;
508 510
509 -
                emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff));
511 +
                emit::emit(&mut *s.e, encode::bne(rsrc, super::SCRATCH1, brOff));
510 512
                set remaining -= dwordBytes;
511 513
            }
512 514
513 515
            // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time.
514 516
            // Before each load/store pair, check whether the offset is
515 517
            // about to exceed the 12-bit signed immediate range. When
516 518
            // it does, advance the base registers by the accumulated
517 519
            // offset and reset to zero.
518 520
            while remaining >= super::DWORD_SIZE {
519 521
                if offset > super::MAX_IMM - super::DWORD_SIZE {
520 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
522 +
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
521 523
                    if *rdst <> *rsrc {
522 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
524 +
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
523 525
                    }
524 526
                    set offset = 0;
525 527
                }
526 528
                if let off = srcReload {
527 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
528 -
                    emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset);
529 +
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
530 +
                    emit::emitLd(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
529 531
                } else {
530 -
                    emit::emitLd(s.e, super::SCRATCH1, rsrc, offset);
532 +
                    emit::emitLd(&mut *s.e, super::SCRATCH1, rsrc, offset);
531 533
                }
532 -
                emit::emitSd(s.e, super::SCRATCH1, rdst, offset);
534 +
                emit::emitSd(&mut *s.e, super::SCRATCH1, rdst, offset);
533 535
                set offset += super::DWORD_SIZE;
534 536
                set remaining -= super::DWORD_SIZE;
535 537
            }
536 538
            if remaining >= super::WORD_SIZE {
537 539
                if offset > super::MAX_IMM - super::WORD_SIZE {
538 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
540 +
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
539 541
                    if *rdst <> *rsrc {
540 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
542 +
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
541 543
                    }
542 544
                    set offset = 0;
543 545
                }
544 546
                if let off = srcReload {
545 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
546 -
                    emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset);
547 +
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
548 +
                    emit::emitLw(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
547 549
                } else {
548 -
                    emit::emitLw(s.e, super::SCRATCH1, rsrc, offset);
550 +
                    emit::emitLw(&mut *s.e, super::SCRATCH1, rsrc, offset);
549 551
                }
550 -
                emit::emitSw(s.e, super::SCRATCH1, rdst, offset);
552 +
                emit::emitSw(&mut *s.e, super::SCRATCH1, rdst, offset);
551 553
                set offset += super::WORD_SIZE;
552 554
                set remaining -= super::WORD_SIZE;
553 555
            }
554 556
            while remaining > 0 {
555 557
                if offset > super::MAX_IMM - 1 {
556 -
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
558 +
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset);
557 559
                    if *rdst <> *rsrc {
558 -
                        emit::emitAddImm(s.e, rdst, rdst, offset);
560 +
                        emit::emitAddImm(&mut *s.e, rdst, rdst, offset);
559 561
                    }
560 562
                    set offset = 0;
561 563
                }
562 564
                if let off = srcReload {
563 -
                    emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off);
564 -
                    emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset);
565 +
                    emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off);
566 +
                    emit::emitLb(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset);
565 567
                } else {
566 -
                    emit::emitLb(s.e, super::SCRATCH1, rsrc, offset);
568 +
                    emit::emitLb(&mut *s.e, super::SCRATCH1, rsrc, offset);
567 569
                }
568 -
                emit::emitSb(s.e, super::SCRATCH1, rdst, offset);
570 +
                emit::emitSb(&mut *s.e, super::SCRATCH1, rdst, offset);
569 571
                set offset += 1;
570 572
                set remaining -= 1;
571 573
            }
572 574
            // Restore base registers if they were advanced (never happens
573 575
            // in the both-spilled case since size <= MAX_IMM).
574 576
            if not bothSpilled {
575 577
                let advanced = staticSize as i32 - offset;
576 578
                if advanced <> 0 {
577 -
                    emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced);
579 +
                    emit::emitAddImm(&mut *s.e, rsrc, rsrc, 0 - advanced);
578 580
                    if *rdst <> *rsrc {
579 -
                        emit::emitAddImm(s.e, rdst, rdst, 0 - advanced);
581 +
                        emit::emitAddImm(&mut *s.e, rdst, rdst, 0 - advanced);
580 582
                    }
581 583
                }
582 584
            }
583 585
        },
584 586
        case il::Instr::Zext { typ, dst, val } => {
585 587
            let rd = getDstReg(s, dst, super::SCRATCH1);
586 588
            let rs = resolveVal(s, super::SCRATCH1, val);
587 -
            emitZext(s.e, rd, rs, typ);
589 +
            emitZext(&mut *s.e, rd, rs, typ);
588 590
        },
589 591
        case il::Instr::Sext { typ, dst, val } => {
590 592
            let rd = getDstReg(s, dst, super::SCRATCH1);
591 593
            let rs = resolveVal(s, super::SCRATCH1, val);
592 -
            emitSext(s.e, rd, rs, typ);
594 +
            emitSext(&mut *s.e, rd, rs, typ);
593 595
        },
594 596
        case il::Instr::Ret { val } => {
595 597
            if let v = val {
596 598
                let rs = resolveVal(s, super::SCRATCH1, v);
597 599
                emitMv(s, super::A0, rs);
599 601
            // Skip the jump to epilogue if this RET is in the last block,
600 602
            // since the epilogue immediately follows.
601 603
            if frame.totalSize <> 0 and blockIdx + 1 == frame.epilogueBlock {
602 604
                // Epilogue is the next block; fallthrough is sufficient.
603 605
            } else {
604 -
                emit::emitReturn(s.e, frame);
606 +
                emit::emitReturn(&mut *s.e, frame);
605 607
            }
606 608
        },
607 609
        case il::Instr::Jmp { target, args } => {
608 610
            // Move arguments to target block's parameter registers.
609 611
            emitBlockArgs(s, func, target, args);
610 612
            // Skip branch if target is the next block (fallthrough).
611 613
            if target <> blockIdx + 1 {
612 -
                emit::recordBranch(s.e, target, emit::BranchKind::Jump);
614 +
                emit::recordBranch(&mut *s.e, target, emit::BranchKind::Jump);
613 615
            }
614 616
        },
615 617
        case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => {
616 618
            // Use zero register directly for immediate `0` operands.
617 619
            let aIsZero = isZeroImm(a);
634 636
            if let case il::CmpOp::Slt = op {
635 637
                set signed = true;
636 638
            }
637 639
            let useSext = cmpUsesSext(typ, signed);
638 640
            if not aIsZero and not isExtendedImm(a, typ, useSext) {
639 -
                emitCmpExt(s.e, rs1, rs1, typ, useSext);
641 +
                emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
640 642
            }
641 643
            if not bIsZero and not isExtendedImm(b, typ, useSext) {
642 -
                emitCmpExt(s.e, rs2, rs2, typ, useSext);
644 +
                emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
643 645
            }
644 646
            // Block-argument moves must only execute on the taken path.
645 647
            // When `thenArgs` is non-empty, invert the branch so that the
646 648
            // then-moves land on the fall-through (taken) side.
647 649
            //
650 652
            // conditional branch to skip to the *other* target and letting
651 653
            // execution fall through.
652 654
            if thenArgs.len > 0 and elseArgs.len > 0 {
653 655
                panic "selectInstr: both `then` and `else` have block arguments";
654 656
            } else if thenArgs.len > 0 {
655 -
                emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
657 +
                emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
656 658
                emitBlockArgs(s, func, thenTarget, thenArgs);
657 659
                // Skip trailing jump if then is the next block (fallthrough).
658 660
                if thenTarget <> blockIdx + 1 {
659 -
                    emit::recordBranch(s.e, thenTarget, emit::BranchKind::Jump);
661 +
                    emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Jump);
660 662
                }
661 663
            } else if thenTarget == blockIdx + 1 and elseArgs.len == 0 {
662 664
                // Then is the next block and no else args: invert the
663 665
                // condition to branch to else and fall through to then.
664 -
                emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
666 +
                emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
665 667
            } else {
666 -
                emit::recordBranch(s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 });
668 +
                emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 });
667 669
                emitBlockArgs(s, func, elseTarget, elseArgs);
668 670
                // Skip trailing jump if else is the next block (fallthrough).
669 671
                if elseTarget <> blockIdx + 1 {
670 -
                    emit::recordBranch(s.e, elseTarget, emit::BranchKind::Jump);
672 +
                    emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::Jump);
671 673
                }
672 674
            }
673 675
        },
674 676
        case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => {
675 677
            let rs1 = resolveVal(s, super::SCRATCH1, val);
676 678
            // When a case has block args, invert the branch to skip past
677 679
            // the arg moves.
678 680
            for c in cases {
679 -
                emit::loadImm(s.e, super::SCRATCH2, c.value);
681 +
                emit::loadImm(&mut *s.e, super::SCRATCH2, c.value);
680 682
681 683
                if c.args.len > 0 {
682 684
                    let skip = s.nextSynthBlock;
683 685
                    set s.nextSynthBlock = skip + 1;
684 686
685 -
                    emit::recordBranch(s.e, skip, emit::BranchKind::InvertedCond {
687 +
                    emit::recordBranch(&mut *s.e, skip, emit::BranchKind::InvertedCond {
686 688
                        op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2,
687 689
                    });
688 690
                    emitBlockArgs(s, func, c.target, c.args);
689 -
                    emit::recordBranch(s.e, c.target, emit::BranchKind::Jump);
690 -
                    emit::recordBlock(s.e, skip);
691 +
                    emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Jump);
692 +
                    emit::recordBlock(&mut *s.e, skip);
691 693
                } else {
692 -
                    emit::recordBranch(s.e, c.target, emit::BranchKind::Cond {
694 +
                    emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Cond {
693 695
                        op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2,
694 696
                    });
695 697
                }
696 698
            }
697 699
            // Fall through to default.
698 700
            emitBlockArgs(s, func, defaultTarget, defaultArgs);
699 -
            emit::recordBranch(s.e, defaultTarget, emit::BranchKind::Jump);
701 +
            emit::recordBranch(&mut *s.e, defaultTarget, emit::BranchKind::Jump);
700 702
        },
701 703
        case il::Instr::Unreachable => {
702 -
            emit::emit(s.e, encode::ebreak());
704 +
            emit::emit(&mut *s.e, encode::ebreak());
703 705
        },
704 706
        case il::Instr::Call { retTy, dst, func, args } => {
705 707
            // For indirect calls, save target to scratch register before arg
706 708
            // setup can clobber it.
707 709
            if let case il::Val::Reg(r) = func {
713 715
            emitParallelMoves(s, &super::ARG_REGS[..], args);
714 716
715 717
            // Emit call.
716 718
            match func {
717 719
                case il::Val::FnAddr(name) => {
718 -
                    emit::recordCall(s.e, name);
720 +
                    emit::recordCall(&mut *s.e, name);
719 721
                },
720 722
                case il::Val::Reg(_) => {
721 -
                    emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0));
723 +
                    emit::emit(&mut *s.e, encode::jalr(super::RA, super::SCRATCH2, 0));
722 724
                },
723 725
                else => {
724 726
                    panic "selectInstr: invalid call target";
725 727
                }
726 728
            }
736 738
            // support constant-evaluating struct/union values in them.
737 739
            let ecallDsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3];
738 740
            let ecallArgs: [il::Val; 5] = [num, a0, a1, a2, a3];
739 741
740 742
            emitParallelMoves(s, &ecallDsts[..], &ecallArgs[..]);
741 -
            emit::emit(s.e, encode::ecall());
743 +
            emit::emit(&mut *s.e, encode::ecall());
742 744
743 745
            // Result in A0.
744 746
            let ecallRd = getDstReg(s, dst, super::SCRATCH1);
745 747
            emitMv(s, ecallRd, super::A0);
746 748
        },
747 749
        case il::Instr::Ebreak => {
748 -
            emit::emit(s.e, encode::ebreak());
750 +
            emit::emit(&mut *s.e, encode::ebreak());
749 751
        },
750 752
        case il::Instr::MemoryFence => {
751 -
            emit::emit(s.e, encode::fence());
753 +
            emit::emit(&mut *s.e, encode::fence());
752 754
        },
753 755
    }
754 756
}
755 757
756 758
/// Choose the cheapest canonical representation that preserves the comparison.
760 762
    return signed or typ == il::Type::W32;
761 763
}
762 764
763 765
/// Extend a comparison operand to its selected canonical representation.
764 766
fn emitCmpExt(
765 -
    e: *mut emit::Emitter,
767 +
    e: &mut emit::Emitter,
766 768
    rd: gen::Reg,
767 769
    rs: gen::Reg,
768 770
    typ: il::Type,
769 771
    useSext: bool
770 772
) {
830 832
    return false;
831 833
}
832 834
833 835
/// Select a binary ALU operation, dispatching to the appropriate
834 836
/// instruction pattern based on the operation kind and type.
835 -
fn selectAluBinOp(s: *mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) {
837 +
unsafe fn selectAluBinOp(s: &mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) {
836 838
    match op {
837 839
        case il::BinOp::Add => {
838 840
            if typ == il::Type::W32 {
839 841
                // Inline W32 ADD with immediate optimization.
840 842
                if let case il::Val::Imm(imm) = b {
841 843
                    if encode::isSmallImm64(imm) {
842 -
                        emit::emit(s.e, encode::addiw(rd, rs1, imm as i32));
844 +
                        emit::emit(&mut *s.e, encode::addiw(rd, rs1, imm as i32));
843 845
                        return;
844 846
                    }
845 847
                }
846 848
                let rs2 = resolveVal(s, super::SCRATCH2, b);
847 -
                emit::emit(s.e, encode::addw(rd, rs1, rs2));
849 +
                emit::emit(&mut *s.e, encode::addw(rd, rs1, rs2));
848 850
            } else {
849 851
                selectBinOp(s, rd, rs1, b, BinOp::Add, super::SCRATCH2);
850 852
            }
851 853
        }
852 854
        case il::BinOp::Sub => {
853 855
            // Optimize subtraction by small immediate: use ADDI with negated value.
854 856
            if let case il::Val::Imm(imm) = b {
855 857
                let neg = -imm;
856 858
                if neg >= super::MIN_IMM as i64 and neg <= super::MAX_IMM as i64 {
857 -
                    emit::emit(s.e,
859 +
                    emit::emit(&mut *s.e,
858 860
                        encode::addiw(rd, rs1, neg as i32)
859 861
                            if typ == il::Type::W32 else
860 862
                        encode::addi(rd, rs1, neg as i32));
861 863
                    return;
862 864
                }
863 865
            }
864 866
            let rs2 = resolveVal(s, super::SCRATCH2, b);
865 867
866 -
            emit::emit(s.e,
868 +
            emit::emit(&mut *s.e,
867 869
                encode::subw(rd, rs1, rs2)
868 870
                    if typ == il::Type::W32 else
869 871
                encode::sub(rd, rs1, rs2));
870 872
        }
871 873
        case il::BinOp::Mul => {
872 874
            // Strength-reduce multiplication by known constants.
873 875
            if let case il::Val::Imm(imm) = b {
874 876
                if imm == 0 {
875 -
                    emit::emit(s.e, encode::mv(rd, super::ZERO));
877 +
                    emit::emit(&mut *s.e, encode::mv(rd, super::ZERO));
876 878
                    return;
877 879
                } else if imm == 1 {
878 880
                    emitMv(s, rd, rs1);
879 881
                    return;
880 882
                } else if imm == 2 {
881 -
                    emit::emit(s.e, encode::slli(rd, rs1, 1));
883 +
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 1));
882 884
                    return;
883 885
                } else if imm == 4 {
884 -
                    emit::emit(s.e, encode::slli(rd, rs1, 2));
886 +
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 2));
885 887
                    return;
886 888
                } else if imm == 8 {
887 -
                    emit::emit(s.e, encode::slli(rd, rs1, 3));
889 +
                    emit::emit(&mut *s.e, encode::slli(rd, rs1, 3));
888 890
                    return;
889 891
                }
890 892
            }
891 893
            let rs2 = resolveVal(s, super::SCRATCH2, b);
892 -
            emit::emit(s.e,
894 +
            emit::emit(&mut *s.e,
893 895
                encode::mulw(rd, rs1, rs2)
894 896
                    if typ == il::Type::W32 else
895 897
                encode::mul(rd, rs1, rs2));
896 898
        }
897 899
        case il::BinOp::Sdiv => {
898 900
            let rs2 = resolveAndTrapIfZero(s, b, typ, true);
899 -
            emit::emit(s.e,
901 +
            emit::emit(&mut *s.e,
900 902
                encode::divw(rd, rs1, rs2)
901 903
                    if typ == il::Type::W32 else
902 904
                encode::div(rd, rs1, rs2));
903 905
        }
904 906
        case il::BinOp::Udiv => {
905 907
            let rs2 = resolveAndTrapIfZero(s, b, typ, false);
906 -
            emit::emit(s.e,
908 +
            emit::emit(&mut *s.e,
907 909
                encode::divuw(rd, rs1, rs2)
908 910
                    if typ == il::Type::W32 else
909 911
                encode::divu(rd, rs1, rs2));
910 912
        }
911 913
        case il::BinOp::Srem => {
912 914
            let rs2 = resolveAndTrapIfZero(s, b, typ, true);
913 -
            emit::emit(s.e,
915 +
            emit::emit(&mut *s.e,
914 916
                encode::remw(rd, rs1, rs2)
915 917
                    if typ == il::Type::W32 else
916 918
                encode::rem(rd, rs1, rs2));
917 919
        }
918 920
        case il::BinOp::Urem => {
919 921
            let rs2 = resolveAndTrapIfZero(s, b, typ, false);
920 -
            emit::emit(s.e,
922 +
            emit::emit(&mut *s.e,
921 923
                encode::remuw(rd, rs1, rs2)
922 924
                    if typ == il::Type::W32 else
923 925
                encode::remu(rd, rs1, rs2));
924 926
        }
925 927
        case il::BinOp::And =>
935 937
        case il::BinOp::Ushr =>
936 938
            selectShift(s, rd, rs1, b, ShiftOp::Srl, typ, super::SCRATCH2),
937 939
        case il::BinOp::Eq, il::BinOp::Ne => {
938 940
            let rs2 = resolveVal(s, super::SCRATCH2, b);
939 941
            let useSext = cmpUsesSext(typ, false);
940 -
            emitCmpExt(s.e, rs1, rs1, typ, useSext);
942 +
            emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
941 943
            if not isExtendedImm(b, typ, useSext) {
942 -
                emitCmpExt(s.e, rs2, rs2, typ, useSext);
944 +
                emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
943 945
            }
944 -
            emit::emit(s.e, encode::xor(rd, rs1, rs2));
946 +
            emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2));
945 947
            if let case il::BinOp::Eq = op {
946 -
                emit::emit(s.e, encode::sltiu(rd, rd, 1));
948 +
                emit::emit(&mut *s.e, encode::sltiu(rd, rd, 1));
947 949
            } else {
948 -
                emit::emit(s.e, encode::sltu(rd, super::ZERO, rd));
950 +
                emit::emit(&mut *s.e, encode::sltu(rd, super::ZERO, rd));
949 951
            }
950 952
        }
951 953
        case il::BinOp::Slt =>
952 954
            selectCmp(s, typ, rd, rs1, b, CmpOp::Slt, false, super::SCRATCH2),
953 955
        case il::BinOp::Ult =>
958 960
            selectCmp(s, typ, rd, rs1, b, CmpOp::Ult, true, super::SCRATCH2),
959 961
    }
960 962
}
961 963
962 964
/// Select a unary ALU operation.
963 -
fn selectAluUnOp(s: *mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) {
965 +
unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) {
964 966
    match op {
965 967
        case il::UnOp::Neg => {
966 968
            if typ == il::Type::W32 {
967 -
                emit::emit(s.e, encode::subw(rd, super::ZERO, rs));
969 +
                emit::emit(&mut *s.e, encode::subw(rd, super::ZERO, rs));
968 970
            } else {
969 -
                emit::emit(s.e, encode::neg(rd, rs));
971 +
                emit::emit(&mut *s.e, encode::neg(rd, rs));
970 972
            }
971 973
        }
972 974
        case il::UnOp::Not =>
973 -
            emit::emit(s.e, encode::not_(rd, rs)),
975 +
            emit::emit(&mut *s.e, encode::not_(rd, rs)),
974 976
    }
975 977
}
976 978
977 979
/// Select binary operation with immediate optimization.
978 -
fn selectBinOp(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) {
980 +
unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) {
979 981
    // Try immediate optimization first.
980 982
    if let case il::Val::Imm(imm) = b {
981 983
        if encode::isSmallImm64(imm) {
982 984
            let simm = imm as i32;
983 985
            match op {
984 -
                case BinOp::Add => emit::emit(s.e, encode::addi(rd, rs1, simm)),
985 -
                case BinOp::And => emit::emit(s.e, encode::andi(rd, rs1, simm)),
986 -
                case BinOp::Or  => emit::emit(s.e, encode::ori(rd, rs1, simm)),
987 -
                case BinOp::Xor => emit::emit(s.e, encode::xori(rd, rs1, simm)),
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)),
988 990
            }
989 991
            return;
990 992
        }
991 993
    }
992 994
    // Fallback: load into register.
993 995
    let rs2 = resolveVal(s, scratch, b);
994 996
    match op {
995 -
        case BinOp::Add => emit::emit(s.e, encode::add(rd, rs1, rs2)),
996 -
        case BinOp::And => emit::emit(s.e, encode::and_(rd, rs1, rs2)),
997 -
        case BinOp::Or  => emit::emit(s.e, encode::or_(rd, rs1, rs2)),
998 -
        case BinOp::Xor => emit::emit(s.e, encode::xor(rd, rs1, rs2)),
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)),
999 1001
    }
1000 1002
}
1001 1003
1002 1004
/// Select shift operation with immediate optimization.
1003 1005
/// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits
1004 1006
/// and sign-extend the result.
1005 -
fn selectShift(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) {
1007 +
unsafe fn selectShift(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) {
1006 1008
    let isW32: bool = typ == il::Type::W32;
1007 1009
1008 1010
    // Try immediate optimization first.
1009 1011
    if let case il::Val::Imm(shamt) = b {
1010 1012
        // Keep immediate forms only for encodable shift amounts.
1011 1013
        // Otherwise fall back to register shifts, which naturally mask the count.
1012 1014
        if shamt >= 0 and ((isW32 and shamt < 32) or (not isW32 and shamt < 64)) {
1013 1015
            let sa = shamt as i32;
1014 1016
            if isW32 {
1015 1017
                match op {
1016 -
                    case ShiftOp::Sll => emit::emit(s.e, encode::slliw(rd, rs1, sa)),
1017 -
                    case ShiftOp::Srl => emit::emit(s.e, encode::srliw(rd, rs1, sa)),
1018 -
                    case ShiftOp::Sra => emit::emit(s.e, encode::sraiw(rd, rs1, sa)),
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)),
1019 1021
                }
1020 1022
            } else {
1021 1023
                match op {
1022 -
                    case ShiftOp::Sll => emit::emit(s.e, encode::slli(rd, rs1, sa)),
1023 -
                    case ShiftOp::Srl => emit::emit(s.e, encode::srli(rd, rs1, sa)),
1024 -
                    case ShiftOp::Sra => emit::emit(s.e, encode::srai(rd, rs1, sa)),
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)),
1025 1027
                }
1026 1028
            }
1027 1029
            return;
1028 1030
        }
1029 1031
    }
1030 1032
    // Fallback: load into register.
1031 1033
    let rs2 = resolveVal(s, scratch, b);
1032 1034
    if isW32 {
1033 1035
        match op {
1034 -
            case ShiftOp::Sll => emit::emit(s.e, encode::sllw(rd, rs1, rs2)),
1035 -
            case ShiftOp::Srl => emit::emit(s.e, encode::srlw(rd, rs1, rs2)),
1036 -
            case ShiftOp::Sra => emit::emit(s.e, encode::sraw(rd, rs1, rs2)),
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)),
1037 1039
        }
1038 1040
    } else {
1039 1041
        match op {
1040 -
            case ShiftOp::Sll => emit::emit(s.e, encode::sll(rd, rs1, rs2)),
1041 -
            case ShiftOp::Srl => emit::emit(s.e, encode::srl(rd, rs1, rs2)),
1042 -
            case ShiftOp::Sra => emit::emit(s.e, encode::sra(rd, rs1, rs2)),
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)),
1043 1045
        }
1044 1046
    }
1045 1047
}
1046 1048
1047 1049
/// Resolve parallel moves from IL values to physical destination registers.
1053 1055
/// 1. Identifies "ready" moves.
1054 1056
/// 2. Executes ready moves.
1055 1057
/// 3. Breaks cycles using scratch register.
1056 1058
///
1057 1059
/// Entries with `ZERO` destination are skipped, as they are handled by caller.
1058 -
fn emitParallelMoves(s: *mut Selector, dsts: *[gen::Reg], args: *[il::Val]) {
1060 +
unsafe fn emitParallelMoves(s: &mut Selector, dsts: &[gen::Reg], args: &[il::Val]) {
1059 1061
    let n: u32 = args.len;
1060 1062
    if n == 0 {
1061 1063
        return;
1062 1064
    }
1063 1065
    assert n <= MAX_BLOCK_ARGS, "emitParallelMoves: too many arguments";
1164 1166
/// Emit moves from block arguments to target block's parameter registers.
1165 1167
///
1166 1168
/// Handles spilled destinations directly, then delegates to [`emitParallelMoves`]
1167 1169
/// for the remaining register-to-register parallel move resolution. Edges that
1168 1170
/// would overwrite an unconsumed spill source are unsupported.
1169 -
fn emitBlockArgs(s: *mut Selector, func: *il::Fn, target: u32, args: *mut [il::Val]) {
1171 +
unsafe fn emitBlockArgs(s: &mut Selector, func: *il::Fn, target: u32, args: &[il::Val]) {
1170 1172
    if args.len == 0 {
1171 1173
        return;
1172 1174
    }
1173 1175
    let block = &func.blocks[target];
1174 1176
    assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch";
1220 1222
        if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, param) {
1221 1223
            if let case il::Val::Undef = arg {
1222 1224
                // Undefined values don't need any move.
1223 1225
            } else {
1224 1226
                let rs = resolveVal(s, super::SCRATCH1, arg);
1225 -
                emit::emitSd(s.e, rs, spillBase(s), spillOffset(s, slot));
1227 +
                emit::emitSd(&mut *s.e, rs, spillBase(s), spillOffset(s, slot));
1226 1228
            }
1227 1229
        } else {
1228 1230
            set dsts[i] = getReg(s, param);
1229 1231
        }
1230 1232
    }
1231 1233
    emitParallelMoves(s, &dsts[..], args);
1232 1234
}
1233 1235
1234 1236
/// Select a comparison with immediate optimization.
1235 -
fn selectCmp(
1236 -
    s: *mut Selector,
1237 +
unsafe fn selectCmp(
1238 +
    s: &mut Selector,
1237 1239
    typ: il::Type,
1238 1240
    rd: gen::Reg,
1239 1241
    rs1: gen::Reg,
1240 1242
    b: il::Val,
1241 1243
    op: CmpOp,
1245 1247
    let mut signed = false;
1246 1248
    if let case CmpOp::Slt = op {
1247 1249
        set signed = true;
1248 1250
    }
1249 1251
    let useSext = cmpUsesSext(typ, signed);
1250 -
    emitCmpExt(s.e, rs1, rs1, typ, useSext);
1252 +
    emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext);
1251 1253
1252 1254
    // Canonicalizing the immediate can expose an immediate instruction even
1253 1255
    // when the IL value used a different representation for the same width.
1254 1256
    let mut rhs = b;
1255 1257
    if let case il::Val::Imm(imm) = b {
1256 1258
        let canonical = canonicalCmpImm(imm, typ, useSext);
1257 1259
        set rhs = il::Val::Imm(canonical);
1258 1260
        if encode::isSmallImm64(canonical) {
1259 1261
            let simm = canonical as i32;
1260 1262
            match op {
1261 -
                case CmpOp::Slt => emit::emit(s.e, encode::slti(rd, rs1, simm)),
1262 -
                case CmpOp::Ult => emit::emit(s.e, encode::sltiu(rd, rs1, simm)),
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 1265
            }
1264 1266
            if invert {
1265 -
                emit::emit(s.e, encode::xori(rd, rd, 1));
1267 +
                emit::emit(&mut *s.e, encode::xori(rd, rd, 1));
1266 1268
            }
1267 1269
            return;
1268 1270
        }
1269 1271
    }
1270 1272
1271 1273
    let rs2 = resolveVal(s, scratch, rhs);
1272 1274
    if not isExtendedImm(rhs, typ, useSext) {
1273 -
        emitCmpExt(s.e, rs2, rs2, typ, useSext);
1275 +
        emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext);
1274 1276
    }
1275 1277
    match op {
1276 -
        case CmpOp::Slt => emit::emit(s.e, encode::slt(rd, rs1, rs2)),
1277 -
        case CmpOp::Ult => emit::emit(s.e, encode::sltu(rd, rs1, rs2)),
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 1280
    }
1279 1281
    if invert {
1280 -
        emit::emit(s.e, encode::xori(rd, rd, 1));
1282 +
        emit::emit(&mut *s.e, encode::xori(rd, rd, 1));
1281 1283
    }
1282 1284
}
lib/std/arch/rv64/printer.rad +24 -24
37 37
///////////////////////
38 38
// Output Helpers    //
39 39
///////////////////////
40 40
41 41
/// Write a string to output.
42 -
fn write(out: *mut sexpr::Output, s: *[u8]) {
42 +
unsafe fn write(out: &mut sexpr::Output, s: &[u8]) {
43 43
    sexpr::write(out, s);
44 44
}
45 45
46 46
/// Format `i32` into arena.
47 -
fn formatI32(a: *mut alloc::Arena, val: i32) -> *[u8] {
47 +
fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] {
48 48
    let mut digits: [u8; 12] = undefined;
49 -
    let text = fmt::formatI32(val, &mut digits[..]);
50 -
    let slice = try! alloc::allocSlice(a, 1, 1, text.len) as *mut [u8];
51 -
    try! mem::copy(slice, text);
49 +
    let start = fmt::formatI32(val, &mut digits[..]);
50 +
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
51 +
    try! mem::copy(slice, &digits[start..]);
52 52
53 53
    return slice;
54 54
}
55 55
56 56
/// Format `u32` into arena.
57 -
fn formatU32(a: *mut alloc::Arena, val: u32) -> *[u8] {
57 +
fn formatU32(a: &mut alloc::Arena, val: u32) -> *[u8] {
58 58
    let mut digits: [u8; 10] = undefined;
59 -
    let text = fmt::formatU32(val, &mut digits[..]);
60 -
    let slice = try! alloc::allocSlice(a, 1, 1, text.len) as *mut [u8];
61 -
    try! mem::copy(slice, text);
59 +
    let start = fmt::formatU32(val, &mut digits[..]);
60 +
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
61 +
    try! mem::copy(slice, &digits[start..]);
62 62
63 63
    return slice;
64 64
}
65 65
66 66
///////////////////////////////
69 69
70 70
/// Mnemonic column width for alignment.
71 71
constant MNEMONIC_WIDTH: u32 = 8;
72 72
73 73
/// Write text wrapped in parentheses.
74 -
fn writeParens(out: *mut sexpr::Output, s: *[u8]) {
74 +
unsafe fn writeParens(out: &mut sexpr::Output, s: &[u8]) {
75 75
    write(out, "(");
76 76
    write(out, s);
77 77
    write(out, ")");
78 78
}
79 79
80 80
/// Write strings separated by ", ".
81 -
fn writeDelim(out: *mut sexpr::Output, parts: *[*[u8]]) {
81 +
unsafe fn writeDelim(out: &mut sexpr::Output, parts: &[*[u8]]) {
82 82
    for part, i in parts {
83 83
        if i > 0 {
84 84
            write(out, ", ");
85 85
        }
86 86
        write(out, part);
87 87
    }
88 88
}
89 89
90 90
/// Write mnemonic with padding for alignment.
91 -
fn writeMnem(out: *mut sexpr::Output, m: *[u8]) {
91 +
unsafe fn writeMnem(out: &mut sexpr::Output, m: *[u8]) {
92 92
    write(out, m);
93 93
    let mut i = m.len;
94 94
    while i < MNEMONIC_WIDTH {
95 95
        write(out, " ");
96 96
        set i += 1;
100 100
////////////////////////////////
101 101
// Instruction Format Helpers //
102 102
////////////////////////////////
103 103
104 104
/// R-type: `op rd, rs1, rs2`.
105 -
fn fmtR(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) {
105 +
unsafe fn fmtR(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) {
106 106
    writeMnem(out, m);
107 107
    writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]);
108 108
}
109 109
110 110
/// I-type: `op rd, rs1, imm`.
111 -
fn fmtI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
111 +
unsafe fn fmtI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
112 112
    writeMnem(out, m);
113 113
    writeDelim(out, &[regNameR(rd), regNameR(rs1), formatI32(a, imm)]);
114 114
}
115 115
116 116
/// 2-reg: `op rd, rs`.
117 -
fn fmt2R(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
117 +
unsafe fn fmt2R(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
118 118
    writeMnem(out, m);
119 119
    writeDelim(out, &[regNameR(rd), regNameR(rs)]);
120 120
}
121 121
122 122
/// reg + imm: `op rd, imm`.
123 -
fn fmtRI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
123 +
unsafe fn fmtRI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
124 124
    writeMnem(out, m);
125 125
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
126 126
}
127 127
128 128
/// imm only: `op imm`.
129 -
fn fmtImm(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], imm: i32) {
129 +
unsafe fn fmtImm(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], imm: i32) {
130 130
    writeMnem(out, m);
131 131
    write(out, formatI32(a, imm));
132 132
}
133 133
134 134
/// 1-reg: `op rs`.
135 -
fn fmt1R(out: *mut sexpr::Output, m: *[u8], rs: gen::Reg) {
135 +
unsafe fn fmt1R(out: &mut sexpr::Output, m: *[u8], rs: gen::Reg) {
136 136
    writeMnem(out, m);
137 137
    write(out, regNameR(rs));
138 138
}
139 139
140 140
/// Load: `op rd, imm(rs1)`.
141 -
fn fmtLoad(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
141 +
unsafe fn fmtLoad(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
142 142
    writeMnem(out, m);
143 143
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
144 144
    writeParens(out, regNameR(rs1));
145 145
}
146 146
147 147
/// Store: `op rs2, imm(rs1)`.
148 -
fn fmtStore(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
148 +
unsafe fn fmtStore(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
149 149
    writeMnem(out, m);
150 150
    writeDelim(out, &[regNameR(rs2), formatI32(a, imm)]);
151 151
    writeParens(out, regNameR(rs1));
152 152
}
153 153
154 154
/// Branch: `op rs1, rs2, imm`.
155 -
fn fmtB(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
155 +
unsafe fn fmtB(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
156 156
    writeMnem(out, m);
157 157
    writeDelim(out, &[regNameR(rs1), regNameR(rs2), formatI32(a, imm)]);
158 158
}
159 159
160 160
/// Branch zero: `op rs1, imm`.
161 -
fn fmtBz(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
161 +
unsafe fn fmtBz(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
162 162
    writeMnem(out, m);
163 163
    writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]);
164 164
}
165 165
166 166
/// Print a single instruction to output buffer.
167 -
export fn printInstr(out: *mut sexpr::Output, a: *mut alloc::Arena, instr: u32) {
167 +
export unsafe fn printInstr(out: &mut sexpr::Output, a: &mut alloc::Arena, instr: u32) {
168 168
    let decoded = decode::decode(instr);
169 169
170 170
    match decoded {
171 171
        case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm),
172 172
        case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm),
304 304
        },
305 305
    }
306 306
}
307 307
308 308
/// Print code with labels to the given output.
309 -
export fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena) {
309 +
export unsafe fn printCodeTo(out: &mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: &mut alloc::Arena) {
310 310
    // Package header.
311 311
    write(out, "# package `");
312 312
    write(out, pkgName);
313 313
    write(out, "`\n\n");
314 314
lib/std/arch/rv64/tests.rad +1 -1
16 16
/// Helper to check encoding equals expected value.
17 17
fn expectEncoding(actual: u32, expected: u32) throws (testing::TestError) {
18 18
    try testing::expect(actual == expected);
19 19
}
20 20
21 -
@test fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) {
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 26
    let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2, 2);
lib/std/collections/dict.rad +2 -2
29 29
    }
30 30
    return Dict { entries, count: 0 };
31 31
}
32 32
33 33
/// Insert or update a key-value pair. Panics if the table exceeds 50% load.
34 -
export fn insert(m: *mut Dict, key: *[u8], value: i32) {
34 +
export fn insert(m: &mut Dict, key: *[u8], value: i32) {
35 35
    let mask = m.entries.len - 1;
36 36
    let mut idx = hash(key) & mask;
37 37
38 38
    loop {
39 39
        let entry = &m.entries[idx];
50 50
        set idx = (idx + 1) & mask;
51 51
    }
52 52
}
53 53
54 54
/// Look up a value by key. Returns `nil` if not found.
55 -
export fn get(m: *Dict, key: *[u8]) -> ?i32 {
55 +
export fn get(m: &Dict, key: *[u8]) -> ?i32 {
56 56
    let mask = m.entries.len - 1;
57 57
    let mut idx = hash(key) & mask;
58 58
59 59
    loop {
60 60
        let entry = &m.entries[idx];
lib/std/fmt.rad +31 -29
42 42
    magnitude: u64,
43 43
    /// Radix used by the literal.
44 44
    radix: Radix,
45 45
}
46 46
47 -
/// Format a u32 by writing it to the provided buffer.
48 -
export fn formatU32(val: u32, buffer: *mut [u8]) -> *[u8] {
47 +
/// Write a u32 at the end of the buffer and return its start offset.
48 +
export fn formatU32(val: u32, buffer: &mut [u8]) -> u32 {
49 49
    assert buffer.len >= U32_STR_LEN;
50 50
51 51
    let mut x: u32 = val;
52 52
    let mut i: u32 = buffer.len;
53 53
61 61
            set i -= 1;
62 62
            set buffer[i] = ('0' + (x % 10) as u8);
63 63
            set x /= 10;
64 64
        }
65 65
    }
66 -
    // Return the slice from the start of the written number to
67 -
    // the end of the buffer.
68 -
    return &buffer[i..];
66 +
    // Return the offset of the first written byte.
67 +
    return i;
69 68
}
70 69
71 -
/// Format a i32 by writing it to the provided buffer.
72 -
export fn formatI32(val: i32, buffer: *mut [u8]) -> *[u8] {
70 +
/// Write a i32 at the end of the buffer and return its start offset.
71 +
export fn formatI32(val: i32, buffer: &mut [u8]) -> u32 {
73 72
    assert buffer.len >= I32_STR_LEN;
74 73
75 74
    let neg: bool = val < 0;
76 75
    let mut x: u32 = -val as u32 if neg else val as u32;
77 76
    let mut i: u32 = buffer.len;
90 89
        if neg {
91 90
            set i -= 1;
92 91
            set buffer[i] = '-';
93 92
        }
94 93
    }
95 -
    return &buffer[i..];
94 +
    return i;
96 95
}
97 96
98 -
/// Format a u64 by writing it to the provided buffer.
99 -
export fn formatU64(val: u64, buffer: *mut [u8]) -> *[u8] {
97 +
/// Write a u64 at the end of the buffer and return its start offset.
98 +
export fn formatU64(val: u64, buffer: &mut [u8]) -> u32 {
100 99
    assert buffer.len >= U64_STR_LEN;
101 100
102 101
    let mut x: u64 = val;
103 102
    let mut i: u32 = buffer.len;
104 103
110 109
            set i -= 1;
111 110
            set buffer[i] = ('0' + (x % 10) as u8);
112 111
            set x /= 10;
113 112
        }
114 113
    }
115 -
    return &buffer[i..];
114 +
    return i;
116 115
}
117 116
118 -
/// Format a i64 by writing it to the provided buffer.
119 -
export fn formatI64(val: i64, buffer: *mut [u8]) -> *[u8] {
117 +
/// Write a i64 at the end of the buffer and return its start offset.
118 +
export fn formatI64(val: i64, buffer: &mut [u8]) -> u32 {
120 119
    assert buffer.len >= I64_STR_LEN;
121 120
122 121
    let neg: bool = val < 0;
123 122
    let mut x: u64 = -val as u64 if neg else val as u64;
124 123
    let mut i: u32 = buffer.len;
134 133
        if neg {
135 134
            set i -= 1;
136 135
            set buffer[i] = '-';
137 136
        }
138 137
    }
139 -
    return &buffer[i..];
138 +
    return i;
140 139
}
141 140
142 -
/// Format a i8 by writing it to the provided buffer.
143 -
export fn formatI8(val: i8, buffer: *mut [u8]) -> *[u8] {
141 +
/// Write a i8 at the end of the buffer and return its start offset.
142 +
export fn formatI8(val: i8, buffer: &mut [u8]) -> u32 {
144 143
    return formatI32(val as i32, buffer);
145 144
}
146 145
147 -
/// Format a i16 by writing it to the provided buffer.
148 -
export fn formatI16(val: i16, buffer: *mut [u8]) -> *[u8] {
146 +
/// Write a i16 at the end of the buffer and return its start offset.
147 +
export fn formatI16(val: i16, buffer: &mut [u8]) -> u32 {
149 148
    return formatI32(val as i32, buffer);
150 149
}
151 150
152 -
/// Format a u8 by writing it to the provided buffer.
153 -
export fn formatU8(val: u8, buffer: *mut [u8]) -> *[u8] {
151 +
/// Write a u8 at the end of the buffer and return its start offset.
152 +
export fn formatU8(val: u8, buffer: &mut [u8]) -> u32 {
154 153
    return formatU32(val as u32, buffer);
155 154
}
156 155
157 -
/// Format a u16 by writing it to the provided buffer.
158 -
export fn formatU16(val: u16, buffer: *mut [u8]) -> *[u8] {
156 +
/// Write a u16 at the end of the buffer and return its start offset.
157 +
export fn formatU16(val: u16, buffer: &mut [u8]) -> u32 {
159 158
    return formatU32(val as u32, buffer);
160 159
}
161 160
162 -
/// Format a bool by writing it to the provided buffer.
163 -
export fn formatBool(val: bool, buffer: *mut [u8]) -> *[u8] {
161 +
/// Write a bool at the end of the buffer and return its start offset.
162 +
export fn formatBool(val: bool, buffer: &mut [u8]) -> u32 {
163 +
    assert buffer.len >= BOOL_STR_LEN;
164 164
    if val {
165 -
        try! mem::copy(buffer, "true");
166 -
        return &buffer[..4];
165 +
        let start = buffer.len - 4;
166 +
        try! mem::copy(&mut buffer[start..], "true");
167 +
        return start;
167 168
    } else {
168 -
        try! mem::copy(buffer, "false");
169 -
        return &buffer[..5];
169 +
        let start = buffer.len - 5;
170 +
        try! mem::copy(&mut buffer[start..], "false");
171 +
        return start;
170 172
    }
171 173
}
172 174
173 175
/// Convert a single ASCII digit into its numeric value for the given radix.
174 176
export fn digitFromAscii(ch: u8, radix: u32) -> ?u32 {
250 252
    return IntLiteral { text, magnitude: value, radix: radixType };
251 253
}
252 254
253 255
/// Process escape sequences in a raw string, writing the result into `dst`.
254 256
/// Returns the number of bytes written.
255 -
export fn unescapeString(raw: *[u8], dst: *mut [u8]) -> u32 {
257 +
export fn unescapeString(raw: *[u8], dst: &mut [u8]) -> u32 {
256 258
    let mut i: u32 = 0;
257 259
    let mut j: u32 = 0;
258 260
259 261
    while i < raw.len {
260 262
        if raw[i] == '\\' and i + 1 < raw.len {
lib/std/io.rad +17 -10
1 1
//! Input/output utilities.
2 2
use std::fmt;
3 3
use std::intrinsics;
4 4
5 -
export fn print(str: *[u8]) {
5 +
/// Write the bytes to standard output.
6 +
export fn print(str: &[u8]) {
6 7
    intrinsics::ecall(64, 1, str.ptr as i64, str.len as i64, 0);
7 8
}
8 9
9 -
export fn printError(str: *[u8]) {
10 +
/// Write the bytes to standard error.
11 +
export fn printError(str: &[u8]) {
10 12
    intrinsics::ecall(64, 2, str.ptr as i64, str.len as i64, 0);
11 13
}
12 14
13 -
export fn printLn(str: *[u8]) {
15 +
/// Write the bytes and a newline to standard output.
16 +
export fn printLn(str: &[u8]) {
14 17
    print(str);
15 18
    print("\n");
16 19
}
17 20
21 +
/// Write a signed decimal integer to standard output.
18 22
export fn printI32(val: i32) {
19 23
    let mut buffer: [u8; 11] = [0; 11];
20 -
    let result: *[u8] = fmt::formatI32(val, &mut buffer[..]);
21 -
    print(result);
24 +
    let start = fmt::formatI32(val, &mut buffer[..]);
25 +
    print(&buffer[start..]);
22 26
}
23 27
28 +
/// Write an unsigned decimal integer to standard output.
24 29
export fn printU32(val: u32) {
25 30
    let mut buffer: [u8; 10] = [0; 10];
26 -
    let result: *[u8] = fmt::formatU32(val, &mut buffer[..]);
27 -
    print(result);
31 +
    let start = fmt::formatU32(val, &mut buffer[..]);
32 +
    print(&buffer[start..]);
28 33
}
29 34
35 +
/// Write a Boolean value to standard output.
30 36
export fn printBool(val: bool) {
31 37
    let mut buffer: [u8; 5] = [0; 5];
32 -
    let result: *[u8] = fmt::formatBool(val, &mut buffer[..]);
33 -
    print(result);
38 +
    let start = fmt::formatBool(val, &mut buffer[..]);
39 +
    print(&buffer[start..]);
34 40
}
35 41
36 -
export fn read(buf: *mut [u8]) -> u32 {
42 +
/// Read standard input into the buffer and return the system call result.
43 +
export fn read(buf: &mut [u8]) -> u32 {
37 44
    return intrinsics::ecall(63, 0, buf.ptr as i64, buf.len as i64, 0) as u32;
38 45
}
39 46
40 47
export fn readToEnd(buf: *mut [u8]) -> *[u8] {
41 48
    let mut total: u32 = 0;
lib/std/lang/alloc.rad +17 -17
33 33
/// Allocate `size` bytes with the given alignment.
34 34
///
35 35
/// Returns an opaque pointer to the allocated memory. Throws `AllocError` if
36 36
/// the arena is exhausted. The caller is responsible for casting to the
37 37
/// appropriate type and initializing the memory.
38 -
export fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
38 +
export fn alloc(arena: &mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) {
39 39
    assert alignment > 0;
40 40
    assert size > 0;
41 41
42 42
    let aligned = mem::alignUp(arena.offset, alignment);
43 43
    let newOffset = aligned + size;
52 52
}
53 53
54 54
/// Reset the arena, allowing all memory to be reused.
55 55
///
56 56
/// Does not zero the memory.
57 -
export fn reset(arena: *mut Arena) {
57 +
export fn reset(arena: &mut Arena) {
58 58
    set arena.offset = 0;
59 59
}
60 60
61 61
/// Save the current arena state for later restoration.
62 -
export fn save(arena: *Arena) -> u32 {
62 +
export fn save(arena: &Arena) -> u32 {
63 63
    return arena.offset;
64 64
}
65 65
66 66
/// Restore the arena to a previously saved state, reclaiming all
67 67
/// allocations made since that point.
68 -
export fn restore(arena: *mut Arena, savedOffset: u32) {
68 +
export fn restore(arena: &mut Arena, savedOffset: u32) {
69 69
    set arena.offset = savedOffset;
70 70
}
71 71
72 72
/// Returns the number of bytes currently allocated.
73 -
export fn used(arena: *Arena) -> u32 {
73 +
export fn used(arena: &Arena) -> u32 {
74 74
    return arena.offset;
75 75
}
76 76
77 77
/// Returns the number of bytes remaining in the arena.
78 -
export fn remaining(arena: *Arena) -> u32 {
78 +
export fn remaining(arena: &Arena) -> u32 {
79 79
    return arena.data.len as u32 - arena.offset;
80 80
}
81 81
82 82
/// Returns the remaining buffer as a mutable slice.
83 -
export fn remainingBuf(arena: *mut Arena) -> *mut [u8] {
83 +
export fn remainingBuf(arena: &mut Arena) -> *mut [u8] {
84 84
    return &mut arena.data[arena.offset..];
85 85
}
86 86
87 87
/// Commits `size` bytes of allocation, advancing the offset.
88 88
/// Use after writing to the buffer returned by [`remainingBuf`].
89 -
export fn commit(arena: *mut Arena, size: u32) {
89 +
export fn commit(arena: &mut Arena, size: u32) {
90 90
    set arena.offset += size;
91 91
}
92 92
93 93
/// Allocate a slice of `count` elements, each of `size` bytes with given alignment.
94 94
///
95 95
/// Returns a type-erased slice that should be cast to the appropriate `*[T]`.
96 96
/// The slice length is set to `count` (element count, not bytes).
97 97
/// Throws `AllocError` if the arena is exhausted.
98 -
export fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
98 +
export fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
99 99
    if count == 0 {
100 100
        return &mut [];
101 101
    }
102 102
    let ptr = try alloc(arena, size * count, alignment);
103 103
112 112
/// pointer, a byte size and an alignment, and must return a pointer to
113 113
/// the allocated memory or panic on failure.
114 114
export record Allocator: Copy {
115 115
    /// Allocation function. Returns a pointer to `size` bytes
116 116
    /// aligned to `alignment`, or panics on failure.
117 -
    func: fn(*mut opaque, u32, u32) -> *mut opaque,
117 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
118 118
    /// Opaque context pointer passed to `func`.
119 -
    ctx: *mut opaque,
119 +
    ctx: *unsafe mut opaque,
120 120
}
121 121
122 -
/// Create an `Allocator` backed by an `Arena`.
123 -
export fn arenaAllocator(arena: *mut Arena) -> Allocator {
122 +
/// Create an allocator whose arena must outlive all uses of the allocator.
123 +
export unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator {
124 124
    return Allocator {
125 125
        func: arenaAllocFn,
126 -
        ctx: arena as *mut opaque,
126 +
        ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque,
127 127
    };
128 128
}
129 129
130 130
/// Arena allocation function conforming to the `Allocator` interface.
131 -
fn arenaAllocFn(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque {
132 -
    let arena = ctx as *mut Arena;
133 -
    return try! alloc(arena, size, alignment);
131 +
unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
132 +
    let arena = ctx as *unsafe mut Arena;
133 +
    return try! alloc(&mut *arena, size, alignment);
134 134
}
lib/std/lang/alloc/tests.rad +1 -1
103 103
    };
104 104
    try testing::expect(failed);
105 105
}
106 106
107 107
/// Test the Allocator interface backed by an arena.
108 -
@test fn testAllocator() throws (testing::TestError) {
108 +
@test unsafe fn testAllocator() throws (testing::TestError) {
109 109
    static STORAGE: [u8; 256] = undefined;
110 110
    let mut arena = super::new(&mut STORAGE[..]);
111 111
    let a = super::arenaAllocator(&mut arena);
112 112
113 113
    // Allocate through the Allocator indirection.
lib/std/lang/ast.rad +12 -7
26 26
        nextId: 0,
27 27
    };
28 28
}
29 29
30 30
/// Create an empty `*mut [*Node]` slice with the given capacity.
31 -
export fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] {
31 +
export fn nodeSlice(arena: &mut NodeArena, capacity: u32) -> *mut [*Node] {
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
57 57
export record Attributes: Copy {
58 58
    list: *mut [*Node],
59 59
}
60 60
61 61
/// Check if an attributes list contains an attribute.
62 -
export fn attributesContains(self: *Attributes, attr: Attribute) -> bool {
62 +
export fn attributesContains(self: &Attributes, attr: Attribute) -> bool {
63 63
    for node in self.list {
64 64
        if let case NodeValue::Attribute(a) = node.value; a == attr {
65 65
            return true;
66 66
        }
67 67
    }
206 206
        fields: *mut [*Node],
207 207
        /// Whether this record has labeled fields.
208 208
        labeled: bool,
209 209
    },
210 210
    /// Anonymous function type.
211 -
    Fn(FnSig),
211 +
    Fn {
212 +
        /// Parameter, return, and error types.
213 +
        sig: FnSig,
214 +
        /// Whether a call requires an unsafe function body.
215 +
        isUnsafe: bool,
216 +
    },
212 217
    /// Trait object type, eg. `*opaque Allocator`, `&opaque Allocator`, or
213 218
    /// `*unsafe opaque Allocator`.
214 219
    TraitObject {
215 220
        /// Ownership and safety class.
216 221
        class: types::PointerClass,
818 823
        else => return false,
819 824
    }
820 825
}
821 826
822 827
/// Allocate a new AST node from the arena with the given span and value.
823 -
export fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
828 +
export fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
824 829
    let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node));
825 830
    let node = p as *mut Node;
826 831
    let nodeId = arena.nextId;
827 832
    set arena.nextId = nodeId + 1;
828 833
830 835
831 836
    return node;
832 837
}
833 838
834 839
/// Allocate a synthetic AST node with a zero-length span.
835 -
export fn synthNode(arena: *mut NodeArena, value: NodeValue) -> *mut Node {
840 +
export fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *mut Node {
836 841
    return allocNode(arena, Span { offset: 0, length: 0 }, value);
837 842
}
838 843
839 844
/// Synthetic module with a single function in it.
840 845
record SynthFnMod: Copy {
843 848
    /// The function block.
844 849
    fnBody: *Node
845 850
}
846 851
847 852
/// Synthesize a module with a function in it with the given name and statements.
848 -
export fn synthFnModule(
849 -
    arena: *mut NodeArena, name: *[u8], bodyStmts: *mut [*Node]
853 +
export unsafe fn synthFnModule(
854 +
    arena: &mut NodeArena, name: *[u8], bodyStmts: *mut [*Node]
850 855
) -> SynthFnMod {
851 856
    let a = alloc::arenaAllocator(&mut arena.arena);
852 857
    let fnName = synthNode(arena, NodeValue::Ident(name));
853 858
    let params: *mut [*Node] = &mut [];
854 859
    let throwList: *mut [*Node] = &mut [];
lib/std/lang/ast/printer.rad +17 -16
82 82
        case types::PointerClass::Unsafe => return unsafeHead,
83 83
    }
84 84
}
85 85
86 86
/// Convert a type signature to an S-expression.
87 -
fn typeSigToExpr(a: *mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
87 +
fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
88 88
    match sig {
89 89
        case super::TypeSig::Void => return sexpr::sym("void"),
90 90
        case super::TypeSig::Opaque => return sexpr::sym("opaque"),
91 91
        case super::TypeSig::Bool => return sexpr::sym("bool"),
92 92
        case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)),
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 109
            return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])),
110 -
        case super::TypeSig::Fn(sig) => {
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 -
            return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]);
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 117
        }
117 118
        case super::TypeSig::TraitObject { class, traitName, mutable } => {
118 119
            let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj");
119 120
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable
120 121
                else sexpr::list(a, head, &[toExpr(a, traitName)]);
121 122
        }
122 123
    }
123 124
}
124 125
125 126
/// Convert a node slice to a slice of expressions.
126 -
fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
127 +
fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
127 128
    if nodes.len == 0 {
128 129
        return &[];
129 130
    }
130 131
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
131 132
    for node, i in nodes {
133 134
    }
134 135
    return buf;
135 136
}
136 137
137 138
/// Convert optional attributes to an attribute list expression.
138 -
fn attributesToExpr(a: *mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
139 +
fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
139 140
    let mut exprs: *[sexpr::Expr] = &[];
140 141
    if let list = attrs {
141 142
        set exprs = nodeListToExprs(a, &list.list[..]);
142 143
    }
143 144
    return sexpr::list(a, "attrs", exprs);
144 145
}
145 146
146 147
/// Convert an optional node to an expression, or return placeholder.
147 -
fn toExprOpt(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
148 +
fn toExprOpt(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
148 149
    if let n = opt {
149 150
        return toExpr(a, n);
150 151
    }
151 152
    return sexpr::sym("_");
152 153
}
153 154
154 155
/// Convert an optional node to an expression, or return `Null`.
155 -
fn toExprOrNull(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
156 +
fn toExprOrNull(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
156 157
    if let n = opt {
157 158
        return toExpr(a, n);
158 159
    }
159 160
    return sexpr::Expr::Null;
160 161
}
161 162
162 163
/// Convert an optional guard.
163 -
fn guardExpr(a: *mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr {
164 +
fn guardExpr(a: &mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr {
164 165
    if let g = guard {
165 166
        return sexpr::list(a, "guard", &[toExpr(a, g)]);
166 167
    }
167 168
    return sexpr::Expr::Null;
168 169
}
169 170
170 171
/// Convert a list of match prongs to expressions.
171 -
fn prongListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
172 +
fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
172 173
    if nodes.len == 0 {
173 174
        return &[];
174 175
    }
175 176
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
176 177
    for prong, i in nodes {
185 186
    }
186 187
    return buf;
187 188
}
188 189
189 190
/// Convert a match prong to an S-expression.
190 -
fn prongToExpr(a: *mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
191 +
fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
191 192
    match p.arm {
192 193
        case super::ProngArm::Case(patterns) => {
193 194
            return sexpr::block(a, "case", &[
194 195
                sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])),
195 196
                guardExpr(a, p.guard)
207 208
    }
208 209
}
209 210
210 211
/// Convert a record field declaration to an S-expression.
211 212
fn fieldToExpr(
212 -
    a: *mut alloc::Arena,
213 +
    a: &mut alloc::Arena,
213 214
    field: ?*super::Node,
214 215
    type: *super::Node,
215 216
    value: ?*super::Node
216 217
) -> sexpr::Expr {
217 218
    return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]);
218 219
}
219 220
220 221
/// Convert a list of record fields to expressions.
221 -
fn fieldListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
222 +
fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
222 223
    if nodes.len == 0 {
223 224
        return &[];
224 225
    }
225 226
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
226 227
    for node, i in nodes {
235 236
    }
236 237
    return buf;
237 238
}
238 239
239 240
/// Convert a union variant to an S-expression.
240 -
fn variantToExpr(a: *mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
241 +
fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
241 242
    return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]);
242 243
}
243 244
244 245
/// Convert a list of union variants to expressions.
245 -
fn variantListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
246 +
fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
246 247
    if nodes.len == 0 {
247 248
        return &[];
248 249
    }
249 250
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
250 251
    for node, i in nodes {
259 260
    }
260 261
    return buf;
261 262
}
262 263
263 264
/// Convert an AST node to an S-expression.
264 -
export fn toExpr(a: *mut alloc::Arena, node: *super::Node) -> sexpr::Expr {
265 +
export fn toExpr(a: &mut alloc::Arena, node: *super::Node) -> sexpr::Expr {
265 266
    match node.value {
266 267
        case super::NodeValue::Placeholder => return sexpr::sym("_"),
267 268
        case super::NodeValue::Nil => return sexpr::sym("nil"),
268 269
        case super::NodeValue::Undef => return sexpr::sym("undefined"),
269 270
        case super::NodeValue::Bool(v) => {
514 515
        else => return sexpr::sym("?"),
515 516
    }
516 517
}
517 518
518 519
/// Dump the tree rooted at `root`, using the provided arena for allocation.
519 -
export fn printTree(root: *super::Node, arena: *mut alloc::Arena) {
520 +
export unsafe fn printTree(root: *super::Node, arena: &mut alloc::Arena) {
520 521
    match root.value {
521 522
        case super::NodeValue::Block(blk) => {
522 523
            for stmt, i in blk.statements {
523 524
                sexpr::print(toExpr(arena, stmt), 0);
524 525
                if i < blk.statements.len - 1 { io::print("\n\n"); }
lib/std/lang/gen/bitset.rad +23 -20
19 19
    return (n + 31) / 32;
20 20
}
21 21
22 22
/// A fixed-size bitset backed by an array of 32-bit words.
23 23
export record Bitset: Copy {
24 -
    /// Backing storage for bits, organized as 32-bit words.
25 -
    bits: *mut [u32],
24 +
    /// Backing words. The storage must outlive the bitset and its iterators.
25 +
    bits: *unsafe mut [u32],
26 26
    /// Number of bits this bitset can hold.
27 27
    len: u32,
28 28
}
29 29
30 30
/// Create a new bitset backed by the given zero-initialized storage.
31 -
export fn new(bits: *mut [u32]) -> Bitset {
32 -
    return Bitset { bits, len: bits.len * 32 };
31 +
/// The storage must outlive the bitset and its iterators.
32 +
export unsafe fn new(bits: &mut [u32]) -> Bitset {
33 +
    return Bitset { bits: bits as *unsafe mut [u32], len: bits.len * 32 };
33 34
}
34 35
35 36
/// Create a new bitset backed by the given storage, zeroing it first.
36 -
export fn init(bits: *mut [u32]) -> Bitset {
37 +
/// The storage must outlive the bitset and its iterators.
38 +
export unsafe fn init(bits: &mut [u32]) -> Bitset {
37 39
    for i in 0..bits.len {
38 40
        set bits[i] = 0;
39 41
    }
40 42
    return new(bits);
41 43
}
42 44
43 45
/// Create a bitset from arena allocation.
44 -
export fn allocate(arena: *mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) {
46 +
export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) {
45 47
    let numWords = wordsFor(len);
46 48
    let bits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *mut [u32];
47 49
48 50
    return init(bits);
49 51
}
50 52
51 53
/// Set bit `n` in the bitset.
52 -
export fn put(bs: *mut Bitset, n: u32) {
54 +
export unsafe fn put(bs: &mut Bitset, n: u32) {
53 55
    if n >= bs.len {
54 56
        return;
55 57
    }
56 58
    let word = n / 32;
57 59
    let b = n % 32;
58 60
59 61
    set bs.bits[word] |= (1 << b);
60 62
}
61 63
62 64
/// Clear bit `n` in the bitset.
63 -
export fn clear(bs: *mut Bitset, n: u32) {
65 +
export unsafe fn clear(bs: &mut Bitset, n: u32) {
64 66
    if n >= bs.len {
65 67
        return;
66 68
    }
67 69
    let word = n / 32;
68 70
    let b = n % 32;
69 71
70 72
    set bs.bits[word] &= ~(1 << b);
71 73
}
72 74
73 75
/// Check if bit `n` is set.
74 -
export fn contains(bs: *Bitset, n: u32) -> bool {
76 +
export unsafe fn contains(bs: &Bitset, n: u32) -> bool {
75 77
    if n >= bs.len {
76 78
        return false;
77 79
    }
78 80
    let word = n / 32;
79 81
    let b = n % 32;
80 82
81 83
    return (bs.bits[word] & (1 << b)) <> 0;
82 84
}
83 85
84 86
/// Count the number of set bits.
85 -
export fn count(bs: *Bitset) -> u32 {
87 +
export unsafe fn count(bs: &Bitset) -> u32 {
86 88
    let mut total: u32 = 0;
87 89
    let numWords = bs.bits.len;
88 90
    for i in 0..numWords {
89 91
        let word = bs.bits[i];
90 92
        if word <> 0 {
105 107
106 108
    return n & 0x3F;
107 109
}
108 110
109 111
/// Union: `dst = dst | src`.
110 -
export fn union_(dst: *mut Bitset, src: *Bitset) {
112 +
export unsafe fn union_(dst: &mut Bitset, src: &Bitset) {
111 113
    let numWords = dst.bits.len;
112 114
    let srcWords = src.bits.len;
113 115
    let minWords = min(numWords, srcWords);
114 116
    for i in 0..minWords {
115 117
        set dst.bits[i] |= src.bits[i];
116 118
    }
117 119
}
118 120
119 121
/// Subtract: `dst = dst - src`.
120 -
export fn subtract(dst: *mut Bitset, src: *Bitset) {
122 +
export unsafe fn subtract(dst: &mut Bitset, src: &Bitset) {
121 123
    let numWords = dst.bits.len;
122 124
    let srcWords = src.bits.len;
123 125
    let minWords = min(numWords, srcWords);
124 126
    for i in 0..minWords {
125 127
        set dst.bits[i] &= ~src.bits[i];
126 128
    }
127 129
}
128 130
129 131
/// Check if two bitsets are equal.
130 -
export fn eq(a: *Bitset, b: *Bitset) -> bool {
132 +
export unsafe fn eq(a: &Bitset, b: &Bitset) -> bool {
131 133
    let numWordsA = a.bits.len;
132 134
    let numWordsB = b.bits.len;
133 135
    let minWords = min(numWordsA, numWordsB);
134 136
135 137
    for i in 0..minWords {
149 151
    }
150 152
    return true;
151 153
}
152 154
153 155
/// Copy bits from source to destination.
154 -
export fn copy(dst: *mut Bitset, src: *Bitset) {
156 +
export unsafe fn copy(dst: &mut Bitset, src: &Bitset) {
155 157
    let numWords = dst.bits.len;
156 158
    let srcWords = src.bits.len;
157 159
    let minWords = min(numWords, srcWords);
158 160
159 161
    for i in 0..minWords {
164 166
        set dst.bits[i] = 0;
165 167
    }
166 168
}
167 169
168 170
/// Clear all bits.
169 -
export fn clearAll(bs: *mut Bitset) {
171 +
export unsafe fn clearAll(bs: &mut Bitset) {
170 172
    let numWords = bs.bits.len;
171 173
    for i in 0..numWords {
172 174
        set bs.bits[i] = 0;
173 175
    }
174 176
}
175 177
176 178
/// Iterator state for iterating set bits.
177 179
export record BitIter: Copy {
178 -
    /// Bitset being iterated.
179 -
    bs: *Bitset,
180 +
    /// Bitset being iterated. It must outlive this iterator.
181 +
    bs: *unsafe Bitset,
180 182
    /// Current word index.
181 183
    wordIdx: u32,
182 184
    /// Remaining bits in the current word (visited bits cleared).
183 185
    remaining: u32,
184 186
}
185 187
186 188
/// Create an iterator over set bits.
187 -
export fn iter(bs: *Bitset) -> BitIter {
189 +
/// The bitset and its backing words must outlive the iterator.
190 +
export unsafe fn iter(bs: &Bitset) -> BitIter {
188 191
    let remaining = bs.bits[0] if bs.len > 0 else 0;
189 -
    return BitIter { bs, wordIdx: 0, remaining };
192 +
    return BitIter { bs: bs as *unsafe Bitset, wordIdx: 0, remaining };
190 193
}
191 194
192 195
/// Get the next set bit, or nil if none remain.
193 -
export fn iterNext(it: *mut BitIter) -> ?u32 {
196 +
export unsafe fn iterNext(it: &mut BitIter) -> ?u32 {
194 197
    let numWords = it.bs.bits.len;
195 198
    // Skip to next non-zero word.
196 199
    while it.remaining == 0 {
197 200
        set it.wordIdx += 1;
198 201
        if it.wordIdx >= numWords {
lib/std/lang/gen/bitset/tests.rad +11 -11
1 1
//! Tests for the bitset module.
2 2
3 3
use std::testing;
4 4
5 5
/// Test [`super::init`] and basic set/contains operations.
6 -
@test fn testInit() throws (testing::TestError) {
6 +
@test unsafe fn testInit() throws (testing::TestError) {
7 7
    let mut bits: [u32; 4] = undefined;
8 8
    let mut bs = super::init(&mut bits[..]);
9 9
10 10
    // `init` should zero-initialize, so all bits start unset.
11 11
    try testing::expect(not super::contains(&bs, 0));
30 30
    try testing::expect(not super::contains(&bs, 33));
31 31
    try testing::expect(not super::contains(&bs, 126));
32 32
}
33 33
34 34
/// Test clear operation.
35 -
@test fn testClear() throws (testing::TestError) {
35 +
@test unsafe fn testClear() throws (testing::TestError) {
36 36
    let mut bits: [u32; 2] = [0; 2];
37 37
    let mut bs = super::new(&mut bits[..]);
38 38
39 39
    super::put(&mut bs, 0);
40 40
    super::put(&mut bs, 31);
50 50
    try testing::expect(not super::contains(&bs, 31));
51 51
    try testing::expect(super::contains(&bs, 32));
52 52
}
53 53
54 54
/// Test population count.
55 -
@test fn testCount() throws (testing::TestError) {
55 +
@test unsafe fn testCount() throws (testing::TestError) {
56 56
    let mut bits: [u32; 2] = [0; 2];
57 57
    let mut bs = super::new(&mut bits[..]);
58 58
59 59
    try testing::expect(super::count(&bs) == 0);
60 60
69 69
    super::clear(&mut bs, 31);
70 70
    try testing::expect(super::count(&bs) == 3);
71 71
}
72 72
73 73
/// Test union operation.
74 -
@test fn testUnion() throws (testing::TestError) {
74 +
@test unsafe fn testUnion() throws (testing::TestError) {
75 75
    let mut bits_a: [u32; 2] = [0; 2];
76 76
    let mut bits_b: [u32; 2] = [0; 2];
77 77
    let mut a = super::new(&mut bits_a[..]);
78 78
    let mut b = super::new(&mut bits_b[..]);
79 79
90 90
    try testing::expect(super::contains(&a, 20));
91 91
    try testing::expect(super::count(&a) == 3);
92 92
}
93 93
94 94
/// Test subtract operation.
95 -
@test fn testSubtract() throws (testing::TestError) {
95 +
@test unsafe fn testSubtract() throws (testing::TestError) {
96 96
    let mut bits_a: [u32; 2] = [0; 2];
97 97
    let mut bits_b: [u32; 2] = [0; 2];
98 98
    let mut a = super::new(&mut bits_a[..]);
99 99
    let mut b = super::new(&mut bits_b[..]);
100 100
112 112
    try testing::expect(super::contains(&a, 20));
113 113
    try testing::expect(super::count(&a) == 2);
114 114
}
115 115
116 116
/// Test equality check.
117 -
@test fn testEq() throws (testing::TestError) {
117 +
@test unsafe fn testEq() throws (testing::TestError) {
118 118
    let mut bits_a: [u32; 2] = [0; 2];
119 119
    let mut bits_b: [u32; 2] = [0; 2];
120 120
    let mut a = super::new(&mut bits_a[..]);
121 121
    let mut b = super::new(&mut bits_b[..]);
122 122
136 136
    super::put(&mut b, 33);
137 137
    try testing::expect(not super::eq(&a, &b));
138 138
}
139 139
140 140
/// Test copy operation.
141 -
@test fn testCopy() throws (testing::TestError) {
141 +
@test unsafe fn testCopy() throws (testing::TestError) {
142 142
    let mut bits_a: [u32; 2] = [0; 2];
143 143
    let mut bits_b: [u32; 2] = [0; 2];
144 144
    let mut a = super::new(&mut bits_a[..]);
145 145
    let mut b = super::new(&mut bits_b[..]);
146 146
155 155
    try testing::expect(super::contains(&b, 31));
156 156
    try testing::expect(super::contains(&b, 63));
157 157
}
158 158
159 159
/// Test clearAll operation.
160 -
@test fn testClearAll() throws (testing::TestError) {
160 +
@test unsafe fn testClearAll() throws (testing::TestError) {
161 161
    let mut bits: [u32; 2] = [0; 2];
162 162
    let mut bs = super::new(&mut bits[..]);
163 163
164 164
    super::put(&mut bs, 0);
165 165
    super::put(&mut bs, 31);
172 172
    try testing::expect(not super::contains(&bs, 0));
173 173
    try testing::expect(not super::contains(&bs, 31));
174 174
}
175 175
176 176
/// Test iteration over set bits.
177 -
@test fn testIter() throws (testing::TestError) {
177 +
@test unsafe fn testIter() throws (testing::TestError) {
178 178
    let mut bits: [u32; 2] = [0; 2];
179 179
    let mut bs = super::new(&mut bits[..]);
180 180
181 181
    super::put(&mut bs, 3);
182 182
    super::put(&mut bs, 31);
195 195
    try testing::expect(count == 4);
196 196
    try testing::expect(sum == 3 + 31 + 32 + 50);
197 197
}
198 198
199 199
/// Test iteration on empty bitset.
200 -
@test fn testIterEmpty() throws (testing::TestError) {
200 +
@test unsafe fn testIterEmpty() throws (testing::TestError) {
201 201
    let mut bits: [u32; 2] = [0; 2];
202 202
    let mut bs = super::new(&mut bits[..]);
203 203
    let mut it = super::iter(&bs);
204 204
    let result = super::iterNext(&mut it);
205 205
216 216
    try testing::expect(super::wordsFor(64) == 2);
217 217
    try testing::expect(super::wordsFor(65) == 3);
218 218
}
219 219
220 220
/// Test out-of-bounds access is safe.
221 -
@test fn testOutOfBounds() throws (testing::TestError) {
221 +
@test unsafe fn testOutOfBounds() throws (testing::TestError) {
222 222
    let mut bits: [u32; 1] = [0; 1];
223 223
    let mut bs = super::new(&mut bits[..]);
224 224
225 225
    // Setting beyond length should be ignored.
226 226
    super::put(&mut bs, 100);
lib/std/lang/gen/data.rad +10 -15
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 39
    items: *[il::Data],
40 40
    syms: *mut [DataSym],
41 -
    count: *mut u32,
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 50
    items: *[il::Data],
51 51
    syms: *mut [DataSym],
52 -
    count: *mut u32,
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;
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 fn emitSection(
86 86
    items: *[il::Data],
87 -
    dataSymMap: *DataSymMap,
88 -
    fnLabels: *labels::Labels,
87 +
    dataSymMap: &DataSymMap,
88 +
    fnLabels: &labels::Labels,
89 89
    codeBase: u32,
90 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 fn emitSectionAtOffset(
98 98
    items: *[il::Data],
99 -
    dataSymMap: *DataSymMap,
100 -
    fnLabels: *labels::Labels,
99 +
    dataSymMap: &DataSymMap,
100 +
    fnLabels: &labels::Labels,
101 101
    codeBase: u32,
102 102
    buf: *mut [u8],
103 103
    readOnly: bool,
104 104
    startOffset: u32
105 105
) -> u32 {
114 114
                let v = &data.values[j];
115 115
                for _ in 0..v.count {
116 116
                    match v.item {
117 117
                        case il::DataItem::Val { typ, val } => {
118 118
                            let size = il::typeSize(typ);
119 -
                            let valPtr = &val as *u8;
120 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size));
119 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size));
121 120
122 121
                            set offset += size;
123 122
                        },
124 123
                        case il::DataItem::Sym(name) => {
125 124
                            let addr = lookupAddr(dataSymMap, name) else {
126 125
                                panic "emitSectionAtOffset: data symbol not found";
127 126
                            };
128 127
                            let addr64: u64 = addr as u64;
129 -
                            let addrPtr = &addr64 as *u8;
130 -
131 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
128 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
132 129
133 130
                            set offset += @sizeOf(u64);
134 131
                        },
135 132
                        case il::DataItem::Fn(name) => {
136 133
                            let addr = codeBase + labels::funcOffset(fnLabels, name) as u32;
137 134
                            let addr64: u64 = addr as u64;
138 -
                            let addrPtr = &addr64 as *u8;
139 -
140 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
135 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8));
141 136
142 137
                            set offset += @sizeOf(*u8);
143 138
                        },
144 139
                        case il::DataItem::Str(s) => {
145 140
                            try! mem::copy(&mut buf[offset..], s);
166 161
    }
167 162
    return DataSymMap { dict: d, syms };
168 163
}
169 164
170 165
/// Resolve a data symbol to its final absolute address using the hash map.
171 -
export fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 {
166 +
export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u32 {
172 167
    if let v = dict::get(&m.dict, name) {
173 168
        return v as u32;
174 169
    }
175 170
    return nil;
176 171
}
lib/std/lang/gen/labels.rad +6 -6
30 30
        funcs: dict::init(funcEntries),
31 31
    };
32 32
}
33 33
34 34
/// Reset block count for a new function.
35 -
export fn resetBlocks(l: *mut Labels) {
35 +
export fn resetBlocks(l: &mut Labels) {
36 36
    set l.blockCount = 0;
37 37
}
38 38
39 39
/// Record a block's code offset by its index. O(1).
40 -
export fn recordBlock(l: *mut Labels, blockIdx: u32, offset: i32) {
40 +
export fn recordBlock(l: &mut Labels, blockIdx: u32, offset: i32) {
41 41
    assert blockIdx < l.blockOffsets.len, "recordBlock: block index out of range";
42 42
    set l.blockOffsets[blockIdx] = offset;
43 43
    set l.blockCount += 1;
44 44
}
45 45
46 46
/// Look up a block's byte offset by index. O(1).
47 -
export fn blockOffset(l: *Labels, blockIdx: u32) -> i32 {
47 +
export fn blockOffset(l: &Labels, blockIdx: u32) -> i32 {
48 48
    assert blockIdx < l.blockCount, "blockOffset: block not recorded";
49 49
    return l.blockOffsets[blockIdx];
50 50
}
51 51
52 52
/// Look up a function's byte offset by name.
53 -
export fn funcOffset(l: *Labels, name: *[u8]) -> i32 {
53 +
export fn funcOffset(l: &Labels, name: *[u8]) -> i32 {
54 54
    if let offset = dict::get(&l.funcs, name) {
55 55
        return offset;
56 56
    }
57 57
    panic "funcOffset: unknown function";
58 58
}
59 59
60 60
/// Compute branch offset to a block given source instruction index.
61 -
export fn branchToBlock(l: *Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 {
61 +
export fn branchToBlock(l: &Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 {
62 62
    let targetOffset = blockOffset(l, blockIdx);
63 63
    let srcOffset = srcIndex as i32 * instrSize;
64 64
65 65
    return targetOffset - srcOffset;
66 66
}
67 67
68 68
/// Compute branch offset to a function given source instruction index.
69 -
export fn branchToFunc(l: *Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 {
69 +
export fn branchToFunc(l: &Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 {
70 70
    let targetOffset = funcOffset(l, name);
71 71
    let srcOffset = srcIndex as i32 * instrSize;
72 72
73 73
    return targetOffset - srcOffset;
74 74
}
lib/std/lang/gen/regalloc.rad +3 -3
46 46
47 47
/// Run register allocation on a function.
48 48
///
49 49
/// Returns a mapping from SSA registers to physical registers, plus
50 50
/// spill information.
51 -
export fn allocate(
51 +
export unsafe fn allocate(
52 52
    func: *il::Fn,
53 -
    config: *TargetConfig,
54 -
    arena: *mut alloc::Arena
53 +
    config: &TargetConfig,
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);
58 58
    // Phase 2: Spill analysis (determine which values need stack slots).
59 59
    let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, arena);
lib/std/lang/gen/regalloc/assign.rad +41 -31
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 -
    current: *mut RegMap,
41 -
    usedRegs: *mut bitset::Bitset,
40 +
    current: *unsafe mut RegMap,
41 +
    usedRegs: *unsafe mut bitset::Bitset,
42 42
    /// Last operand-use index for each register used in the current block.
43 43
    lastUse: *[u32],
44 -
    live: *liveness::LiveInfo,
44 +
    live: *unsafe liveness::LiveInfo,
45 45
    blockIdx: u32,
46 46
    instrIdx: u32,
47 47
    allocatable: *[gen::Reg],
48 48
    calleeSaved: *[gen::Reg],
49 49
    assignments: *mut [?gen::Reg],
50 -
    spillInfo: *spill::SpillInfo,
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.
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 -
export fn assign(
62 +
export unsafe fn assign(
63 63
    func: *il::Fn,
64 -
    live: *liveness::LiveInfo,
65 -
    spillInfo: *spill::SpillInfo,
66 -
    config: *super::TargetConfig,
67 -
    arena: *mut alloc::Arena
64 +
    live: &liveness::LiveInfo,
65 +
    spillInfo: &spill::SpillInfo,
66 +
    config: &super::TargetConfig,
67 +
    arena: &mut alloc::Arena
68 68
) -> AssignInfo throws (alloc::AllocError) {
69 69
    let maxReg = live.maxReg;
70 70
    let blockCount = func.blocks.len;
71 71
    let allocatable = config.allocatable;
72 72
109 109
        // Record every operand before allocation. Only current-block operands
110 110
        // query this table, so every read is initialized by this scan.
111 111
        // Entries for other registers need not be cleared between blocks.
112 112
        for instr, i in block.instrs {
113 113
            let mut ctx = LastUseCtx { lastUse, index: i };
114 -
            il::forEachReg(instr, recordLastUseCb, &mut ctx as *mut opaque);
114 +
            il::forEachReg(instr, recordLastUseCb, &mut ctx as &mut opaque);
115 115
        }
116 116
117 117
        // Reset for new block.
118 118
        set current.n = 0;
119 119
        bitset::clearAll(&mut usedRegs);
148 148
        for instr, i in block.instrs {
149 149
            let mut ctx = InstrCtx {
150 150
                current: &mut current,
151 151
                usedRegs: &mut usedRegs,
152 152
                lastUse,
153 -
                live,
153 +
                live: live as *unsafe liveness::LiveInfo,
154 154
                blockIdx: b,
155 155
                instrIdx: i,
156 156
                allocatable,
157 157
                calleeSaved: config.calleeSaved,
158 158
                assignments,
159 -
                spillInfo,
159 +
                spillInfo: spillInfo as *unsafe spill::SpillInfo,
160 160
            };
161 -
            il::forEachReg(instr, processInstrRegCb, &mut ctx as *mut opaque);
161 +
            il::forEachReg(instr, processInstrRegCb, &mut ctx as &mut opaque);
162 162
163 163
            // Allocate destination.
164 164
            if let dst = il::instrDst(instr) {
165 165
                if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) {
166 166
                    set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo);
185 185
        usedCalleeSaved,
186 186
    };
187 187
}
188 188
189 189
/// Create an empty register map.
190 -
fn createRegMap(arena: *mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
190 +
fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
191 191
    let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32];
192 192
    let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg];
193 193
194 194
    return RegMap { virtRegs, physRegs, n: 0 };
195 195
}
196 196
197 197
/// Find physical register for a virtual register in RegMap.
198 -
fn rmapFind(rmap: *RegMap, virtReg: u32) -> ?gen::Reg {
198 +
fn rmapFind(rmap: &RegMap, virtReg: u32) -> ?gen::Reg {
199 199
    for i in 0..rmap.n {
200 200
        if rmap.virtRegs[i] == virtReg {
201 201
            return rmap.physRegs[i];
202 202
        }
203 203
    }
204 204
    return nil;
205 205
}
206 206
207 207
/// Add a mapping to the register map.
208 -
fn rmapSet(rmap: *mut RegMap, virtReg: u32, physReg: gen::Reg) {
208 +
fn rmapSet(rmap: &mut RegMap, virtReg: u32, physReg: gen::Reg) {
209 209
    assert rmap.n < MAX_ACTIVE, "rmapSet: register map overflow";
210 210
    set rmap.virtRegs[rmap.n] = virtReg;
211 211
    set rmap.physRegs[rmap.n] = physReg;
212 212
    set rmap.n += 1;
213 213
}
214 214
215 215
/// Remove a mapping from the register map and return its physical register.
216 -
fn rmapRemove(rmap: *mut RegMap, virtReg: u32) -> ?gen::Reg {
216 +
fn rmapRemove(rmap: &mut RegMap, virtReg: u32) -> ?gen::Reg {
217 217
    for i in 0..rmap.n {
218 218
        if rmap.virtRegs[i] == virtReg {
219 219
            let phys = rmap.physRegs[i];
220 220
            // Swap with last and decrement.
221 221
            set rmap.n -= 1;
228 228
    }
229 229
    return nil;
230 230
}
231 231
232 232
/// Find first free register in pool, allocate it, return it.
233 -
fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
233 +
unsafe fn findFreeInPool(usedRegs: &mut bitset::Bitset, current: &mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
234 234
    for i in 0..pool.len {
235 235
        let r = pool[i];
236 236
        if not bitset::contains(usedRegs, *r as u32) {
237 237
            bitset::put(usedRegs, *r as u32);
238 238
            rmapSet(current, ssaReg, r);
242 242
    return nil;
243 243
}
244 244
245 245
/// Allocate a physical register for an SSA register.
246 246
/// Cross-call values are steered to callee-saved registers.
247 -
fn rallocReg(
248 -
    current: *mut RegMap,
249 -
    usedRegs: *mut bitset::Bitset,
247 +
unsafe fn rallocReg(
248 +
    current: &mut RegMap,
249 +
    usedRegs: &mut bitset::Bitset,
250 250
    ssaReg: u32,
251 251
    allocatable: *[gen::Reg],
252 252
    calleeSaved: *[gen::Reg],
253 -
    spillInfo: *spill::SpillInfo
253 +
    spillInfo: &spill::SpillInfo
254 254
) -> gen::Reg {
255 255
    // Check if already assigned.
256 256
    if let phys = rmapFind(current, ssaReg) {
257 257
        return phys;
258 258
    }
270 270
    }
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 -
fn recordLastUseCb(reg: il::Reg, ctxPtr: *mut opaque) {
276 -
    let ctx = ctxPtr as *mut LastUseCtx;
275 +
fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) {
276 +
    recordLastUse(reg, ctxPtr as &mut LastUseCtx);
277 +
}
278 +
279 +
/// Record the last instruction that uses the register.
280 +
fn recordLastUse(reg: il::Reg, ctx: &mut LastUseCtx) {
277 281
    set ctx.lastUse[reg.n] = ctx.index;
278 282
}
279 283
280 284
/// Free operands with no later block use or live-out use, then allocate missing uses.
281 -
fn processInstrRegCb(reg: il::Reg, ctxPtr: *mut opaque) {
282 -
    let ctx = ctxPtr as *mut InstrCtx;
285 +
unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) {
286 +
    processInstrReg(reg, ctxPtr as &mut InstrCtx);
287 +
}
288 +
289 +
/// Release expired registers and assign a register for this operand.
290 +
unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) {
283 291
    if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) {
284 -
        if let phys = rmapRemove(ctx.current, reg.n) {
285 -
            bitset::clear(ctx.usedRegs, *phys as u32);
292 +
        if let phys = rmapRemove(&mut *ctx.current, reg.n) {
293 +
            bitset::clear(&mut *ctx.usedRegs, *phys as u32);
286 294
        }
287 295
    }
288 296
    assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds";
289 -
    if spill::isSpilled(ctx.spillInfo, reg) {
297 +
    if spill::isSpilled(&*ctx.spillInfo, reg) {
290 298
        return; // Spilled values don't get physical registers.
291 299
    }
292 300
    if ctx.assignments[reg.n] == nil {
301 +
        let current = ctx.current;
302 +
        let usedRegs = ctx.usedRegs;
293 303
        set ctx.assignments[reg.n] = rallocReg(
294 -
            ctx.current, ctx.usedRegs, reg.n, ctx.allocatable,
295 -
            ctx.calleeSaved, ctx.spillInfo
304 +
            &mut *current, &mut *usedRegs, reg.n, ctx.allocatable,
305 +
            ctx.calleeSaved, &*ctx.spillInfo
296 306
        );
297 307
    }
298 308
}
lib/std/lang/gen/regalloc/liveness.rad +28 -16
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 fn analyze(func: *il::Fn, arena: *mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) {
64 +
export unsafe fn analyze(func: *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 [],
83 83
        let block = &func.blocks[b];
84 84
        for p in block.params {
85 85
            set maxReg = maxRegNum(p.value.n, maxReg);
86 86
        }
87 87
        for i in 0..block.instrs.len {
88 -
            il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as *mut opaque);
88 +
            il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as &mut opaque);
89 89
            if let dst = il::instrDst(block.instrs[i]) {
90 90
                set maxReg = maxRegNum(dst.n, maxReg);
91 91
            }
92 92
        }
93 93
    }
132 132
    return LiveInfo { liveIn, liveOut, defs, uses, blockCount, 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 -
fn computeAndUpdateLiveIn(
137 +
unsafe fn computeAndUpdateLiveIn(
138 138
    dst: *mut bitset::Bitset,
139 139
    liveOut: *bitset::Bitset,
140 140
    defs: *bitset::Bitset,
141 141
    uses: *bitset::Bitset
142 142
) -> bool {
151 151
    }
152 152
    return changed;
153 153
}
154 154
155 155
/// Compute local defs and uses for a single block.
156 -
fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) {
156 +
unsafe fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *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];
162 162
        let mut ctx = DefsUses { defs, uses };
163 -
        il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque);
163 +
        il::forEachReg(instr, addUseCallback, &mut ctx as &mut opaque);
164 164
165 165
        if let dst = il::instrDst(instr) {
166 166
            bitset::put(defs, dst.n);
167 167
        }
168 168
    }
169 169
}
170 170
171 171
/// Callback for [`il::forEachReg`]: adds register to uses if not already defined.
172 -
fn addUseCallback(reg: il::Reg, ctx: *mut opaque) {
173 -
    let c = ctx as *mut DefsUses;
172 +
unsafe fn addUseCallback(reg: il::Reg, ctx: &mut opaque) {
173 +
    addUse(reg, ctx as &mut DefsUses);
174 +
}
175 +
176 +
/// Add an undefined register to the use set.
177 +
unsafe fn addUse(reg: il::Reg, c: &mut DefsUses) {
174 178
    if not bitset::contains(c.defs, reg.n) {
175 179
        bitset::put(c.uses, reg.n);
176 180
    }
177 181
}
178 182
179 183
/// Callback for [`il::forEachReg`]: updates max register number.
180 -
fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) {
181 -
    let max = ctx as *mut u32;
184 +
fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) {
185 +
    updateMaxReg(reg, ctx as &mut u32);
186 +
}
187 +
188 +
/// Update the largest register number.
189 +
fn updateMaxReg(reg: il::Reg, max: &mut u32) {
182 190
    set *max = maxRegNum(reg.n, *max);
183 191
}
184 192
185 193
/// Return the larger of n+1 and current.
186 194
fn maxRegNum(n: u32, current: u32) -> u32 {
189 197
    }
190 198
    return current;
191 199
}
192 200
193 201
/// Add successor live-in sets to the block's live-out set.
194 -
fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
202 +
unsafe fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
195 203
    if block.instrs.len == 0 {
196 204
        return;
197 205
    }
198 206
    let term = block.instrs[block.instrs.len - 1];
199 207
213 221
        else => {},
214 222
    }
215 223
}
216 224
217 225
/// Union a target block's live-in set into the block's live-out set.
218 -
fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
226 +
unsafe fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) {
219 227
    bitset::union_(liveOut, &liveIn[target]);
220 228
}
221 229
222 230
/// Check if a register has any use after this instruction.
223 -
export fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
231 +
export unsafe fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
224 232
    let block = &func.blocks[blockIdx];
225 233
226 234
    if bitset::contains(&info.liveOut[blockIdx], reg.n) {
227 235
        return true;
228 236
    }
233 241
    }
234 242
    return false;
235 243
}
236 244
237 245
/// Check if an instruction uses a specific register.
238 -
fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool {
246 +
unsafe fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool {
239 247
    let mut ctx = FindCtx { target: reg.n, found: false };
240 -
    il::forEachReg(instr, findRegCallback, &mut ctx as *mut opaque);
248 +
    il::forEachReg(instr, findRegCallback, &mut ctx as &mut opaque);
241 249
    return ctx.found;
242 250
}
243 251
244 252
/// Callback for [`il::forEachReg`]: sets found if register matches target.
245 -
fn findRegCallback(reg: il::Reg, ctx: *mut opaque) {
246 -
    let c = ctx as *mut FindCtx;
253 +
fn findRegCallback(reg: il::Reg, ctx: &mut opaque) {
254 +
    findReg(reg, ctx as &mut FindCtx);
255 +
}
256 +
257 +
/// Set the match flag when the register equals the target.
258 +
fn findReg(reg: il::Reg, c: &mut FindCtx) {
247 259
    if reg.n == c.target {
248 260
        set c.found = true;
249 261
    }
250 262
}
lib/std/lang/gen/regalloc/spill.rad +28 -24
77 77
    costs: *mut [SpillCost],
78 78
    weight: u32,
79 79
}
80 80
81 81
/// Analyze a function and determine which values need spill slots.
82 -
export fn analyze(
82 +
export unsafe fn analyze(
83 83
    func: *il::Fn,
84 -
    live: *liveness::LiveInfo,
84 +
    live: &liveness::LiveInfo,
85 85
    numRegs: u32,
86 86
    numCalleeSaved: u32,
87 87
    slotSize: u32,
88 -
    arena: *mut alloc::Arena
88 +
    arena: &mut alloc::Arena
89 89
) -> SpillInfo throws (alloc::AllocError) {
90 90
    let maxReg = live.maxReg;
91 91
    if maxReg == 0 {
92 92
        let calleeClass = try bitset::allocate(arena, 0);
93 93
        return SpillInfo {
140 140
            // Remove definition from live set.
141 141
            if let dst = il::instrDst(instr) {
142 142
                bitset::clear(&mut scratch, dst.n);
143 143
            }
144 144
            // Add uses to live set.
145 -
            il::forEachReg(instr, addRegToSetCallback, &mut scratch as *mut opaque);
145 +
            il::forEachReg(instr, addRegToSetCallback, &mut scratch as &mut opaque);
146 146
        }
147 147
        // Also limit pressure at block entry.
148 148
        limitPressure(&mut scratch, &mut spilled, costs, numRegs);
149 149
    }
150 150
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 -
fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) {
181 +
unsafe fn fillCosts(func: *il::Fn, costs: *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;
202 202
                    set costs[dst.n].defs = costs[dst.n].defs + weight;
203 203
                }
204 204
            }
205 205
            // Count uses.
206 206
            let mut ctx = CountCtx { costs, weight };
207 -
            il::forEachReg(instr, countRegUseCallback, &mut ctx as *mut opaque);
207 +
            il::forEachReg(instr, countRegUseCallback, &mut ctx as &mut opaque);
208 208
        }
209 209
    }
210 210
}
211 211
212 212
/// Sort candidates by cost (ascending) using insertion sort, then spill
213 213
/// the cheapest `excess` values: set in `spilled`, clear in `source`.
214 -
fn spillCheapest(
215 -
    c: *mut Candidates,
214 +
unsafe fn spillCheapest(
215 +
    c: &mut Candidates,
216 216
    excess: u32,
217 -
    source: *mut bitset::Bitset,
218 -
    spilled: *mut bitset::Bitset
217 +
    source: &mut bitset::Bitset,
218 +
    spilled: &mut bitset::Bitset
219 219
) {
220 220
    // Insertion sort ascending by cost.
221 221
    for i in 1..c.n {
222 222
        let key = c.entries[i];
223 223
        let mut j: u32 = i;
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 -
fn collectCandidates(bs: *bitset::Bitset, costs: *[SpillCost]) -> Candidates {
238 +
unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *[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 {
247 247
    }
248 248
    return c;
249 249
}
250 250
251 251
/// Limit register pressure by marking low-cost values as spilled.
252 -
fn limitPressure(
253 -
    live: *mut bitset::Bitset,
254 -
    spilled: *mut bitset::Bitset,
252 +
unsafe fn limitPressure(
253 +
    live: &mut bitset::Bitset,
254 +
    spilled: &mut bitset::Bitset,
255 255
    costs: *[SpillCost],
256 256
    numRegs: u32
257 257
) {
258 258
    let liveCount = bitset::count(live);
259 259
    if liveCount <= numRegs {
274 274
/// Limit cross-call pressure by spilling values that exceed callee-saved capacity.
275 275
///
276 276
/// At a call site, every live value must survive the call in a callee-saved
277 277
/// register. If the count exceeds `numCalleeSaved`, spill the cheapest
278 278
/// crossing values.
279 -
fn limitCrossCallPressure(
280 -
    live: *mut bitset::Bitset,
281 -
    spilled: *mut bitset::Bitset,
279 +
unsafe fn limitCrossCallPressure(
280 +
    live: &mut bitset::Bitset,
281 +
    spilled: &mut bitset::Bitset,
282 282
    costs: *[SpillCost],
283 -
    calleeClass: *mut bitset::Bitset,
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.
288 288
    let mut candidates: [CostEntry; 256] = undefined;
310 310
        }
311 311
    }
312 312
}
313 313
314 314
/// Callback for [`il::forEachReg`]: increments use count for register.
315 -
fn countRegUseCallback(reg: il::Reg, ctxPtr: *mut opaque) {
316 -
    let ctx = ctxPtr as *mut CountCtx;
315 +
fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) {
316 +
    countRegUse(reg, ctxPtr as &mut CountCtx);
317 +
}
318 +
319 +
/// Add the block weight to the register use count.
320 +
fn countRegUse(reg: il::Reg, ctx: &mut CountCtx) {
317 321
    assert reg.n < ctx.costs.len, "countRegUseCallback: register out of bounds";
318 322
    set ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight;
319 323
}
320 324
321 325
/// Callback for [`il::forEachReg`]: adds register to live set.
322 -
fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) {
323 -
    bitset::put(ctx as *mut bitset::Bitset, reg.n);
326 +
unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) {
327 +
    bitset::put(ctx as &mut bitset::Bitset, reg.n);
324 328
}
325 329
326 330
/// Check if a register is spilled.
327 -
export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool {
331 +
export fn isSpilled(info: &SpillInfo, reg: il::Reg) -> bool {
328 332
    if reg.n >= info.maxReg {
329 333
        return false;
330 334
    }
331 335
    return info.slots[reg.n] >= 0;
332 336
}
333 337
334 338
/// Get spill slot offset for a register, or `nil` if not spilled.
335 -
export fn spillSlot(info: *SpillInfo, reg: il::Reg) -> ?i32 {
339 +
export fn spillSlot(info: &SpillInfo, reg: il::Reg) -> ?i32 {
336 340
    if isSpilled(info, reg) {
337 341
        return info.slots[reg.n];
338 342
    }
339 343
    return nil;
340 344
}
lib/std/lang/il.rad +3 -3
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 fn formatQualifiedName(arena: *mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] {
85 +
export 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];
425 425
    }
426 426
}
427 427
428 428
/// Call a function for each register used by an instruction.
429 429
/// This is called by the register allocator to analyze register usage.
430 -
export fn forEachReg(instr: Instr, f: fn(Reg, *mut opaque), ctx: *mut opaque) {
430 +
export unsafe fn forEachReg(instr: Instr, f: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
431 431
    match instr {
432 432
        case Instr::Reserve { size, .. } =>
433 433
            withReg(size, f, ctx),
434 434
        case Instr::Load { src, .. } => f(src, ctx),
435 435
        case Instr::Sload { src, .. } => f(src, ctx),
503 503
             Instr::MemoryFence => {},
504 504
    }
505 505
}
506 506
507 507
/// Call callback if value is a register.
508 -
fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) {
508 +
unsafe fn withReg(val: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
509 509
    if let case Val::Reg(r) = val {
510 510
        callback(r, ctx);
511 511
    }
512 512
}
lib/std/lang/il/printer.rad +32 -30
6 6
///////////////////////
7 7
// String formatting //
8 8
///////////////////////
9 9
10 10
/// Write a `u32` before its stack buffer leaves scope.
11 -
fn writeU32(out: *mut sexpr::Output, val: u32) {
11 +
unsafe fn writeU32(out: &mut sexpr::Output, val: u32) {
12 12
    let mut digits: [u8; 10] = undefined;
13 -
    write(out, fmt::formatU32(val, &mut digits[..]));
13 +
    let start = fmt::formatU32(val, &mut digits[..]);
14 +
    write(out, &digits[start..]);
14 15
}
15 16
16 17
/// Write an `i32` before its stack buffer leaves scope.
17 -
fn writeI32(out: *mut sexpr::Output, val: i32) {
18 +
unsafe fn writeI32(out: &mut sexpr::Output, val: i32) {
18 19
    let mut digits: [u8; 12] = undefined;
19 -
    write(out, fmt::formatI32(val, &mut digits[..]));
20 +
    let start = fmt::formatI32(val, &mut digits[..]);
21 +
    write(out, &digits[start..]);
20 22
}
21 23
22 24
/// Write an `i64` before its stack buffer leaves scope.
23 -
fn writeI64(out: *mut sexpr::Output, val: i64) {
25 +
unsafe fn writeI64(out: &mut sexpr::Output, val: i64) {
24 26
    let mut digits: [u8; 20] = undefined;
25 -
    write(out, fmt::formatI64(val, &mut digits[..]));
27 +
    let start = fmt::formatI64(val, &mut digits[..]);
28 +
    write(out, &digits[start..]);
26 29
}
27 30
28 31
/////////////////////
29 32
// Output helpers  //
30 33
/////////////////////
31 34
32 35
/// Write a string to the output.
33 -
fn write(out: *mut sexpr::Output, s: *[u8]) {
36 +
unsafe fn write(out: &mut sexpr::Output, s: &[u8]) {
34 37
    sexpr::write(out, s);
35 38
}
36 39
37 40
/// Emit indentation.
38 -
fn indent(out: *mut sexpr::Output, depth: u32) {
41 +
unsafe fn indent(out: &mut sexpr::Output, depth: u32) {
39 42
    for _ in 0..depth {
40 43
        write(out, "    ");
41 44
    }
42 45
}
43 46
50 53
    }
51 54
    return false;
52 55
}
53 56
54 57
/// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`.
55 -
fn writeSymbol(out: *mut sexpr::Output, name: *[u8]) {
58 +
unsafe fn writeSymbol(out: &mut sexpr::Output, name: *[u8]) {
56 59
    write(out, "$");
57 60
    if needsQuoting(name) {
58 61
        write(out, "\"");
59 62
        write(out, name);
60 63
        write(out, "\"");
76 79
        case super::Type::W64 => return "w64",
77 80
    }
78 81
}
79 82
80 83
/// Write a type.
81 -
fn writeType(out: *mut sexpr::Output, typ: super::Type) {
84 +
unsafe fn writeType(out: &mut sexpr::Output, typ: super::Type) {
82 85
    write(out, typeStr(typ));
83 86
}
84 87
85 88
////////////////////////
86 89
// Operation printing //
122 125
////////////////////
123 126
// Value printing //
124 127
////////////////////
125 128
126 129
/// Write a value (register, immediate, symbol, or undefined).
127 -
fn writeVal(out: *mut sexpr::Output, val: super::Val) {
130 +
unsafe fn writeVal(out: &mut sexpr::Output, val: super::Val) {
128 131
    match val {
129 132
        case super::Val::Reg(reg) => writeReg(out, reg),
130 133
        case super::Val::Imm(v) => writeI64(out, v),
131 134
        case super::Val::DataSym(name) => writeSymbol(out, name),
132 135
        case super::Val::FnAddr(name) => writeSymbol(out, name),
133 136
        case super::Val::Undef => write(out, "undefined"),
134 137
    }
135 138
}
136 139
137 140
/// Write a register in one operation before its stack buffer leaves scope.
138 -
fn writeReg(out: *mut sexpr::Output, reg: super::Reg) {
141 +
unsafe fn writeReg(out: &mut sexpr::Output, reg: super::Reg) {
139 142
    let mut buffer: [u8; 11] = undefined;
140 -
    let digits = fmt::formatU32(reg.n, &mut buffer[1..]);
141 -
    let start = buffer.len - digits.len - 1;
143 +
    let start = fmt::formatU32(reg.n, &mut buffer[1..]);
142 144
    set buffer[start] = '%';
143 145
    write(out, &buffer[start..]);
144 146
}
145 147
146 148
/// Write a comma-separated argument list in parentheses.
147 -
fn writeArgs(out: *mut sexpr::Output, args: *[super::Val]) {
149 +
unsafe fn writeArgs(out: &mut sexpr::Output, args: *[super::Val]) {
148 150
    write(out, "(");
149 151
    for arg, i in args {
150 152
        if i > 0 {
151 153
            write(out, ", ");
152 154
        }
154 156
    }
155 157
    write(out, ")");
156 158
}
157 159
158 160
/// Write a typed parameter (e.g., `w32 %0`).
159 -
fn writeParam(out: *mut sexpr::Output, param: super::Param) {
161 +
unsafe fn writeParam(out: &mut sexpr::Output, param: super::Param) {
160 162
    writeType(out, param.type);
161 163
    write(out, " ");
162 164
    writeReg(out, param.value);
163 165
}
164 166
165 167
/// Write a comma-separated parameter list.
166 -
fn writeParams(out: *mut sexpr::Output, params: *[super::Param]) {
168 +
unsafe fn writeParams(out: &mut sexpr::Output, params: *[super::Param]) {
167 169
    for param, i in params {
168 170
        if i > 0 {
169 171
            write(out, ", ");
170 172
        }
171 173
        writeParam(out, param);
175 177
//////////////////////////
176 178
// Instruction printing //
177 179
//////////////////////////
178 180
179 181
/// Write an instruction.
180 -
fn writeInstr(out: *mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) {
182 +
unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) {
181 183
    match inst {
182 184
        // Memory operations.
183 185
        case super::Instr::Reserve { dst, size, alignment } => {
184 186
            write(out, "reserve ");
185 187
            writeReg(out, dst);
344 346
        }
345 347
    }
346 348
}
347 349
348 350
/// Write a typed binary operation: `op type %dst %a %b`.
349 -
fn writeTypedBinOp(
350 -
    out: *mut sexpr::Output,
351 +
unsafe fn writeTypedBinOp(
352 +
    out: &mut sexpr::Output,
351 353
    name: *[u8],
352 354
    typ: super::Type,
353 355
    dst: super::Reg,
354 356
    va: super::Val,
355 357
    vb: super::Val
364 366
    write(out, " ");
365 367
    writeVal(out, vb);
366 368
}
367 369
368 370
/// Write a typed unary operation: `op type %dst %val`.
369 -
fn writeTypedUnaryOp(
370 -
    out: *mut sexpr::Output,
371 +
unsafe fn writeTypedUnaryOp(
372 +
    out: &mut sexpr::Output,
371 373
    name: *[u8],
372 374
    typ: super::Type,
373 375
    dst: super::Reg,
374 376
    val: super::Val
375 377
) {
385 387
////////////////////
386 388
// Block printing //
387 389
////////////////////
388 390
389 391
/// Write a basic block.
390 -
fn writeBlock(out: *mut sexpr::Output, blocks: *[super::Block], block: *super::Block) {
392 +
unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *[super::Block], block: *super::Block) {
391 393
    // Block label.
392 394
    write(out, "  @");
393 395
    write(out, block.label);
394 396
395 397
    // Block parameters.
411 413
///////////////////////
412 414
// Function printing //
413 415
///////////////////////
414 416
415 417
/// Write a function.
416 -
fn writeFn(out: *mut sexpr::Output, f: *super::Fn) {
418 +
unsafe fn writeFn(out: &mut sexpr::Output, f: *super::Fn) {
417 419
    // Function signature.
418 420
    if f.isExtern {
419 421
        write(out, "extern ");
420 422
    }
421 423
    write(out, "fn ");
445 447
///////////////////
446 448
// Data printing //
447 449
///////////////////
448 450
449 451
/// Write a data item.
450 -
fn writeDataItem(out: *mut sexpr::Output, item: super::DataItem) {
452 +
unsafe fn writeDataItem(out: &mut sexpr::Output, item: super::DataItem) {
451 453
    match item {
452 454
        case super::DataItem::Val { typ, val } => {
453 455
            writeType(out, typ);
454 456
            write(out, " ");
455 457
            writeI64(out, val);
471 473
        }
472 474
    }
473 475
}
474 476
475 477
/// Write a data value (item with optional repeat count).
476 -
fn writeDataValue(out: *mut sexpr::Output, value: super::DataValue) {
478 +
unsafe fn writeDataValue(out: &mut sexpr::Output, value: super::DataValue) {
477 479
    writeDataItem(out, value.item);
478 480
    if value.count > 1 {
479 481
        write(out, " * ");
480 482
        writeU32(out, value.count);
481 483
    }
482 484
}
483 485
484 486
/// Write global data.
485 -
fn writeData(out: *mut sexpr::Output, d: super::Data) {
487 +
unsafe fn writeData(out: &mut sexpr::Output, d: super::Data) {
486 488
    write(out, "data ");
487 489
    if not d.readOnly {
488 490
        write(out, "mut ");
489 491
    }
490 492
    writeSymbol(out, d.name);
503 505
//////////////////////
504 506
// Program printing //
505 507
//////////////////////
506 508
507 509
/// Print a program.
508 -
export fn printProgram(out: *mut sexpr::Output, program: *super::Program) {
510 +
export unsafe fn printProgram(out: &mut sexpr::Output, program: &super::Program) {
509 511
    // Data declarations.
510 512
    for data, i in program.data {
511 513
        writeData(out, data);
512 514
        if i < program.data.len - 1 or program.fns.len > 0 {
513 515
            write(out, "\n");
521 523
        }
522 524
    }
523 525
}
524 526
525 527
/// Print a program to a buffer, returning the written slice.
526 -
export fn printProgramToBuffer(
527 -
    program: *super::Program,
528 +
export unsafe fn printProgramToBuffer(
529 +
    program: &super::Program,
528 530
    buf: *mut [u8]
529 531
) -> *[u8] {
530 532
    let mut pos: u32 = 0;
531 533
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
532 534
    printProgram(&mut out, program);
lib/std/lang/lower.rad +489 -481
135 135
    /// Invalid variable use.
136 136
    InvalidUse,
137 137
    /// Unexpected node value.
138 138
    UnexpectedNodeValue(*ast::Node),
139 139
    /// Unexpected type.
140 -
    UnexpectedType(*resolver::Type),
140 +
    UnexpectedType(resolver::Type),
141 141
142 142
    /// Missing control flow target.
143 143
    MissingTarget,
144 144
    /// Missing metadata that should have been set by resolver.
145 145
    MissingMetadata,
264 264
    Default,
265 265
}
266 266
267 267
/// Function sink used by lowerers that consume functions as they are produced.
268 268
export record FnSink: Copy {
269 -
    /// Opaque context passed to the sink callback.
270 -
    ctx: *mut opaque,
269 +
    /// Opaque context. It must remain valid for every sink callback.
270 +
    ctx: *unsafe mut opaque,
271 271
    /// Callback invoked for each lowered function.
272 -
    emitFn: fn(*mut opaque, *il::Fn, FnRole),
272 +
    emitFn: unsafe fn(*unsafe mut opaque, *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.
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 286
export record Lowerer: Copy {
287 287
    /// Arena for persistent lowering state, including data and symbol names.
288 -
    arena: *mut alloc::Arena,
288 +
    arena: *unsafe mut alloc::Arena,
289 289
    /// Arena for allocations owned by the function currently being lowered.
290 -
    fnArena: *mut alloc::Arena,
290 +
    fnArena: *unsafe mut alloc::Arena,
291 291
    /// Allocator backed by the arena.
292 292
    allocator: alloc::Allocator,
293 293
    /// Resolver for type information. Used to query types, symbols, and
294 294
    /// compile-time constant values during lowering.
295 -
    resolver: *resolver::Resolver,
295 +
    resolver: *unsafe resolver::Resolver,
296 296
    /// Module graph for cross-module symbol resolution.
297 -
    moduleGraph: ?*module::ModuleGraph,
297 +
    moduleGraph: ?*unsafe module::ModuleGraph,
298 298
    /// Package name for qualified symbol names.
299 299
    pkgName: *[u8],
300 300
    /// Current module being lowered.
301 301
    currentMod: ?u16,
302 302
    /// Global data items (string literals, constants, static arrays).
340 340
    return maxSize;
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 -
fn getOrAssignErrorTag(self: *mut Lowerer, errType: resolver::Type) -> u32 {
345 +
fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 {
346 346
    for entry in self.errTags {
347 347
        if entry.ty == errType {
348 348
            return entry.tag;
349 349
        }
350 350
    }
355 355
356 356
    return tag;
357 357
}
358 358
359 359
/// Emit one function to the active output.
360 -
fn emitFunction(self: *mut Lowerer, func: *il::Fn, role: FnRole) {
360 +
unsafe fn emitFunction(self: &mut Lowerer, func: *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);
415 415
    }
416 416
    return true;
417 417
}
418 418
419 419
/// Append a data value to the builder.
420 -
fn dataBuilderPush(b: *mut DataValueBuilder, value: il::DataValue) {
420 +
fn dataBuilderPush(b: &mut DataValueBuilder, value: il::DataValue) {
421 421
    b.values.append(value, b.allocator);
422 422
423 423
    if value.count > 0 and not dataIsZero(value.item) {
424 424
        set b.zeroInit = false;
425 425
    }
426 426
}
427 427
428 428
/// Return the accumulated values.
429 -
fn dataBuilderFinish(b: *DataValueBuilder) -> ConstDataResult {
429 +
fn dataBuilderFinish(b: &DataValueBuilder) -> ConstDataResult {
430 430
    return ConstDataResult {
431 431
        values: &b.values[..],
432 432
        zeroInit: b.zeroInit,
433 433
    };
434 434
}
672 672
673 673
/// Per-function lowering state. Created fresh for each function and contains
674 674
/// all the mutable state needed during function body lowering.
675 675
record FnLowerer: Copy {
676 676
    /// Reference to the module-level lowerer.
677 -
    low: *mut Lowerer,
677 +
    low: *unsafe mut Lowerer,
678 678
    /// Allocator for IL allocations.
679 679
    allocator: alloc::Allocator,
680 680
    /// Type signature of the function being lowered.
681 681
    fnType: *resolver::FnType,
682 682
    /// Function name, used as prefix for generated data symbols.
740 740
/// 2. Iterates over top-level declarations, lowering each.
741 741
/// 3. Returns the complete IL program with functions and data section.
742 742
///
743 743
/// The resolver must have already processed the AST -- we rely on its type
744 744
/// annotations, symbol table, and constant evaluations.
745 -
export fn lower(
746 -
    res: *resolver::Resolver,
745 +
export unsafe fn lower(
746 +
    res: &resolver::Resolver,
747 747
    root: *ast::Node,
748 748
    pkgName: *[u8],
749 -
    arena: *mut alloc::Arena
749 +
    arena: &mut alloc::Arena
750 750
) -> il::Program throws (LowerError) {
751 751
    let mut low = Lowerer {
752 -
        arena: arena,
753 -
        fnArena: arena,
752 +
        arena: arena as *unsafe mut alloc::Arena,
753 +
        fnArena: arena as *unsafe mut alloc::Arena,
754 754
        allocator: alloc::arenaAllocator(arena),
755 -
        resolver: res,
755 +
        resolver: res as *unsafe resolver::Resolver,
756 756
        moduleGraph: nil,
757 757
        pkgName,
758 758
        currentMod: nil,
759 759
        data: &mut [],
760 760
        output: FnOutput::Accumulate(&mut []),
771 771
/////////////////////////////////
772 772
// Multi-Module Lowering API   //
773 773
/////////////////////////////////
774 774
775 775
/// Create a lowerer for multi-module compilation.
776 -
export fn lowerer(
777 -
    res: *resolver::Resolver,
778 -
    graph: *module::ModuleGraph,
776 +
/// The resolver, graph, and both arenas must outlive the returned lowerer.
777 +
export unsafe fn lowerer(
778 +
    res: *unsafe resolver::Resolver,
779 +
    graph: &module::ModuleGraph,
779 780
    pkgName: *[u8],
780 -
    arena: *mut alloc::Arena,
781 -
    fnArena: *mut alloc::Arena,
781 +
    arena: *unsafe mut alloc::Arena,
782 +
    fnArena: *unsafe mut alloc::Arena,
782 783
    options: LowerOptions
783 784
) -> Lowerer {
784 785
    return Lowerer {
785 -
        arena,
786 -
        fnArena,
787 -
        allocator: alloc::arenaAllocator(arena),
788 -
        resolver: res,
789 -
        moduleGraph: graph,
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,
790 +
        moduleGraph: graph as *unsafe module::ModuleGraph,
790 791
        pkgName,
791 792
        currentMod: nil,
792 793
        data: &mut [],
793 794
        output: FnOutput::Accumulate(&mut []),
794 795
        fnSyms: &mut [],
798 799
    };
799 800
}
800 801
801 802
/// Lower a module's AST into the lowerer.
802 803
/// Call this for each module in the package, then use `finalize` to get the program.
803 -
export fn lowerModule(
804 -
    low: *mut Lowerer,
804 +
export unsafe fn lowerModule(
805 +
    low: &mut Lowerer,
805 806
    moduleId: u16,
806 807
    root: *ast::Node,
807 808
    isRoot: bool
808 809
) throws (LowerError) {
809 810
    set low.currentMod = moduleId;
810 811
    try lowerDecls(low, root, isRoot);
811 812
}
812 813
813 814
/// Lower all top-level declarations in a block.
814 -
fn lowerDecls(low: *mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) {
815 +
unsafe fn lowerDecls(low: &mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) {
815 816
    let case ast::NodeValue::Block(block) = root.value else {
816 817
        throw LowerError::ExpectedBlock(root);
817 818
    };
818 819
    let stmtsList = block.statements;
819 820
843 844
        }
844 845
    }
845 846
}
846 847
847 848
/// Finalize lowering and return the unified IL program.
848 -
export fn finalize(low: *Lowerer) -> il::Program {
849 +
export fn finalize(low: &Lowerer) -> il::Program {
849 850
    let mut fns: *mut [*il::Fn] = undefined;
850 851
    match low.output {
851 852
        case FnOutput::Accumulate(accumulated) => {
852 853
            set fns = accumulated;
853 854
        }
865 866
// Qualified Name Construction //
866 867
/////////////////////////////////
867 868
868 869
/// Get module path segments for the current or specified module.
869 870
/// Returns empty slice if no module graph or module not found.
870 -
fn getModulePath(self: *mut Lowerer, modId: ?u16) -> *[*[u8]] {
871 +
unsafe fn getModulePath(self: &mut Lowerer, modId: ?u16) -> *[*[u8]] {
871 872
    let graph = self.moduleGraph else {
872 873
        return &[];
873 874
    };
874 875
    let mut id = modId;
875 876
    if id == nil {
876 877
        set id = self.currentMod;
877 878
    }
878 879
    let actualId = id else {
879 880
        return &[];
880 881
    };
881 -
    let entry = module::get(graph, actualId) else {
882 +
    let entry = module::get(&*graph, actualId) else {
882 883
        return &[];
883 884
    };
884 885
    return module::moduleQualifiedPath(entry);
885 886
}
886 887
887 888
/// Build a qualified name string for a symbol.
888 889
/// If `modId` is nil, uses current module.
889 -
fn qualifyName(self: *mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] {
890 +
unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] {
890 891
    let path = getModulePath(self, modId);
891 892
    if path.len == 0 {
892 893
        return name;
893 894
    }
894 -
    return il::formatQualifiedName(self.arena, path, name);
895 +
    return il::formatQualifiedName(&mut *self.arena, path, name);
895 896
}
896 897
897 898
/// Register a function symbol with its qualified name.
898 899
/// Called when lowering function declarations, so cross-package calls can find
899 900
/// the function by name.
900 -
fn registerFnSym(self: *mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) {
901 +
fn registerFnSym(self: &mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) {
901 902
    self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator);
902 903
}
903 904
904 905
/// Look up a function's qualified name by its symbol.
905 906
/// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered).
906 907
// TODO: This is kind of dubious as an optimization, if it depends on the order
907 908
// in which modules are lowered.
908 909
// TODO: Use a hash table here?
909 -
fn lookupFnSym(self: *Lowerer, sym: *resolver::Symbol) -> ?*[u8] {
910 +
fn lookupFnSym(self: &Lowerer, sym: *resolver::Symbol) -> ?*[u8] {
910 911
    for entry in self.fnSyms {
911 912
        if entry.sym == sym {
912 913
            return entry.qualName;
913 914
        }
914 915
    }
915 916
    return nil;
916 917
}
917 918
918 919
/// Set the package context for lowering.
919 920
/// Called before lowering each package.
920 -
export fn setPackage(self: *mut Lowerer, graph: *module::ModuleGraph, pkgName: *[u8]) {
921 -
    set self.moduleGraph = graph;
921 +
/// The graph must outlive later uses of the lowerer.
922 +
export unsafe fn setPackage(self: &mut Lowerer, graph: &module::ModuleGraph, pkgName: *[u8]) {
923 +
    set self.moduleGraph = graph as *unsafe module::ModuleGraph;
922 924
    set self.pkgName = pkgName;
923 925
    set self.currentMod = nil;
924 926
}
925 927
926 928
/// Create a new function lowerer for a given function type and name.
927 -
fn fnLowerer(
928 -
    self: *mut Lowerer,
929 +
unsafe fn fnLowerer(
930 +
    self: &mut Lowerer,
929 931
    node: *ast::Node,
930 932
    fnType: *resolver::FnType,
931 933
    qualName: *[u8]
932 934
) -> FnLowerer {
933 -
    let loopStack = try! alloc::allocSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx];
935 +
    let loopStack = try! alloc::allocSlice(&mut *self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx];
934 936
935 937
    let mut fnLow = FnLowerer {
936 -
        low: self,
937 -
        allocator: alloc::arenaAllocator(self.fnArena),
938 +
        low: self as *unsafe mut Lowerer,
939 +
        allocator: alloc::arenaAllocator(&mut *self.fnArena),
938 940
        fnType: fnType,
939 941
        fnName: qualName,
940 942
        vars: &mut [],
941 943
        params: &mut [],
942 944
        blockData: &mut [],
968 970
/// This sets up the per-function lowering state, processes parameters,
969 971
/// then lowers the function body into a CFG of basic blocks.
970 972
///
971 973
/// For throwing functions, the return type is a result aggregate
972 974
/// rather than the declared return type.
973 -
fn lowerFnDecl(self: *mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) {
975 +
unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) {
974 976
    if not shouldLowerFn(&decl, self.options.buildTest) {
975 977
        return nil;
976 978
    }
977 979
    let case ast::NodeValue::Ident(name) = decl.name.value else {
978 980
        throw LowerError::ExpectedIdentifier;
979 981
    };
980 -
    let data = resolver::nodeData(self.resolver, node);
982 +
    let data = resolver::nodeData(&*self.resolver, node);
981 983
    let case resolver::Type::Fn(fnType) = data.ty else {
982 984
        throw LowerError::ExpectedFunction;
983 985
    };
984 986
    let isExtern = checkAttr(decl.attrs, ast::Attribute::Extern);
985 987
997 999
    // as the first argument; the callee writes the return value into it.
998 1000
    if requiresReturnParam(fnType) and not isExtern {
999 1001
        set fnLow.returnReg = nextReg(&mut fnLow);
1000 1002
    }
1001 1003
    let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil);
1002 -
    let func = try! alloc::alloc(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1004 +
    let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1003 1005
1004 1006
    set *func = il::Fn {
1005 1007
        name: qualName,
1006 1008
        params: lowParams,
1007 1009
        returnType: undefined,
1028 1030
1029 1031
    return func;
1030 1032
}
1031 1033
1032 1034
/// Build a qualified name of the form "Type::method".
1033 -
fn instanceMethodName(self: *mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] {
1035 +
unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] {
1034 1036
    let sepLen: u32 = 2; // "::"
1035 1037
    let totalLen = typeName.len + sepLen + methodName.len;
1036 -
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1038 +
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1037 1039
    let mut pos: u32 = 0;
1038 1040
1039 1041
    set pos += try! mem::copy(&mut buf[pos..], typeName);
1040 1042
    set pos += try! mem::copy(&mut buf[pos..], "::");
1041 1043
    set pos += try! mem::copy(&mut buf[pos..], methodName);
1043 1045
1044 1046
    return qualifyName(self, modId, &buf[..totalLen]);
1045 1047
}
1046 1048
1047 1049
/// Build a v-table data name of the form "vtable::Type::Trait".
1048 -
fn vtableName(self: *mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] {
1050 +
unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] {
1049 1051
    let prefix = "vtable::";
1050 1052
    let sepLen: u32 = 2; // "::"
1051 1053
    let totalLen = prefix.len + typeName.len + sepLen + traitName.len;
1052 -
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1054 +
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1053 1055
    let mut pos: u32 = 0;
1054 1056
1055 1057
    set pos += try! mem::copy(&mut buf[pos..], prefix);
1056 1058
    set pos += try! mem::copy(&mut buf[pos..], typeName);
1057 1059
    set pos += try! mem::copy(&mut buf[pos..], "::");
1066 1068
/// Each method in the instance block is lowered as a standalone function
1067 1069
/// with a qualified name of the form `Type::method`. A read-only v-table
1068 1070
/// data record is emitted containing pointers to these functions, ordered
1069 1071
/// by the trait's method indices. The v-table is later referenced when
1070 1072
/// constructing trait objects for dynamic dispatch.
1071 -
fn lowerInstanceDecl(
1072 -
    self: *mut Lowerer,
1073 +
unsafe fn lowerInstanceDecl(
1074 +
    self: &mut Lowerer,
1073 1075
    node: *ast::Node,
1074 1076
    traitNameNode: *ast::Node,
1075 1077
    targetTypeNode: *ast::Node,
1076 1078
    methods: *mut [*ast::Node]
1077 1079
) throws (LowerError) {
1078 1080
    // Look up the trait and type from the resolver.
1079 -
    let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym
1081 +
    let traitSym = resolver::nodeData(&*self.resolver, traitNameNode).sym
1080 1082
        else throw LowerError::MissingSymbol(traitNameNode);
1081 1083
    let case resolver::SymbolData::Trait(traitInfo) = traitSym.data
1082 1084
        else throw LowerError::MissingMetadata;
1083 -
    let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym
1085 +
    let typeSym = resolver::nodeData(&*self.resolver, targetTypeNode).sym
1084 1086
        else throw LowerError::MissingSymbol(targetTypeNode);
1085 1087
1086 1088
    let tName = traitSym.name;
1087 1089
    let typeName = typeSym.name;
1088 1090
1122 1124
    }
1123 1125
1124 1126
    // Create v-table in data section, used for dynamic dispatch.
1125 1127
    let vName = vtableName(self, nil, typeName, tName);
1126 1128
    let values = try! alloc::allocSlice(
1127 -
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32
1129 +
        &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32
1128 1130
    ) as *mut [il::DataValue];
1129 1131
1130 1132
    for i in 0..traitInfo.methods.len {
1131 1133
        set values[i] = il::DataValue {
1132 1134
            item: il::DataItem::Fn(methodNames[i]),
1143 1145
    }, self.allocator);
1144 1146
}
1145 1147
1146 1148
/// Lower a method node into an IL function with the given qualified name.
1147 1149
/// Shared by both instance methods and standalone methods.
1148 -
fn lowerMethod(
1149 -
    self: *mut Lowerer,
1150 +
unsafe fn lowerMethod(
1151 +
    self: &mut Lowerer,
1150 1152
    node: *ast::Node,
1151 1153
    qualName: *[u8],
1152 1154
    receiverName: *ast::Node,
1153 1155
    sig: ast::FnSig,
1154 1156
    body: *ast::Node,
1155 1157
) -> ?*il::Fn throws (LowerError) {
1156 -
    let data = resolver::nodeData(self.resolver, node);
1158 +
    let data = resolver::nodeData(&*self.resolver, node);
1157 1159
    let case resolver::Type::Fn(fnType) = data.ty else {
1158 1160
        throw LowerError::ExpectedFunction;
1159 1161
    };
1160 1162
    let sym = data.sym else throw LowerError::MissingSymbol(node);
1161 1163
    registerFnSym(self, sym, qualName);
1163 1165
    let mut fnLow = fnLowerer(self, node, fnType, qualName);
1164 1166
    if requiresReturnParam(fnType) {
1165 1167
        set fnLow.returnReg = nextReg(&mut fnLow);
1166 1168
    }
1167 1169
    let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1168 -
    let func = try! alloc::alloc(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1170 +
    let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1169 1171
1170 1172
    set *func = il::Fn {
1171 1173
        name: qualName,
1172 1174
        params: lowParams,
1173 1175
        returnType: ilType(self, *fnType.returnType),
1184 1186
    return func;
1185 1187
}
1186 1188
1187 1189
/// Lower a standalone method declaration.
1188 1190
/// Produces a function with qualified name `Type::method`.
1189 -
fn lowerMethodDecl(
1190 -
    self: *mut Lowerer,
1191 +
unsafe fn lowerMethodDecl(
1192 +
    self: &mut Lowerer,
1191 1193
    node: *ast::Node,
1192 1194
    name: *ast::Node,
1193 1195
    receiverName: *ast::Node,
1194 1196
    sig: ast::FnSig,
1195 1197
    body: *ast::Node,
1196 1198
) -> ?*il::Fn throws (LowerError) {
1197 -
    let sym = resolver::nodeData(self.resolver, node).sym
1199 +
    let sym = resolver::nodeData(&*self.resolver, node).sym
1198 1200
        else throw LowerError::MissingSymbol(node);
1199 1201
    let case ast::NodeValue::Ident(mName) = name.value
1200 1202
        else throw LowerError::ExpectedIdentifier;
1201 -
    let me = resolver::findMethodBySymbol(self.resolver, sym)
1203 +
    let me = resolver::findMethodBySymbol(&*self.resolver, sym)
1202 1204
        else throw LowerError::MissingMetadata;
1203 1205
    let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName);
1204 1206
1205 1207
    return try lowerMethod(self, node, qualName, receiverName, sig, body);
1206 1208
}
1207 1209
1208 1210
/// Check if a function should be lowered.
1209 -
fn shouldLowerFn(decl: *ast::FnDecl, buildTest: bool) -> bool {
1211 +
fn shouldLowerFn(decl: &ast::FnDecl, buildTest: bool) -> bool {
1210 1212
    if checkAttr(decl.attrs, ast::Attribute::Test) {
1211 1213
        return buildTest;
1212 1214
    }
1213 1215
    return true;
1214 1216
}
1221 1223
    return false;
1222 1224
}
1223 1225
1224 1226
/// Create a label with a numeric suffix, eg. `@base0`.
1225 1227
/// This ensures unique labels like `@then0`, `@then1`, etc.
1226 -
fn labelWithSuffix(self: *mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) {
1228 +
unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) {
1227 1229
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1228 -
    let suffixText = fmt::formatU32(suffix, &mut digits[..]);
1229 -
    let totalLen = base.len + suffixText.len;
1230 -
    let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8];
1230 +
    let start = fmt::formatU32(suffix, &mut digits[..]);
1231 +
    let totalLen = base.len + digits.len - start;
1232 +
    let buf = try! alloc::allocSlice(&mut *self.low.fnArena, 1, 1, totalLen) as *mut [u8];
1231 1233
1232 1234
    try! mem::copy(&mut buf[..base.len], base);
1233 -
    try! mem::copy(&mut buf[base.len..totalLen], suffixText);
1235 +
    try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]);
1234 1236
1235 1237
    return &buf[..totalLen];
1236 1238
}
1237 1239
1238 1240
/// Generate a unique label by appending the global counter to the base.
1239 -
fn nextLabel(self: *mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) {
1241 +
unsafe fn nextLabel(self: &mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) {
1240 1242
    let idx = self.labelCounter;
1241 1243
    set self.labelCounter += 1;
1242 1244
1243 1245
    return try labelWithSuffix(self, base, idx);
1244 1246
}
1266 1268
        else => panic,
1267 1269
    }
1268 1270
}
1269 1271
1270 1272
/// Convert a constant value to an IL value.
1271 -
fn constValueToVal(self: *mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) {
1273 +
unsafe fn constValueToVal(self: &mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) {
1272 1274
    if let case resolver::ConstValue::String(s) = val {
1273 1275
        return try lowerStringLit(self, node, s);
1274 1276
    }
1275 1277
    return il::Val::Imm(constToScalar(val));
1276 1278
}
1277 1279
1278 1280
/// 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 {
1281 +
fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem {
1280 1282
    if let case resolver::ConstValue::String(s) = val {
1281 1283
        return il::DataItem::Str(s);
1282 1284
    }
1283 1285
    // Bool and char are byte-sized; integer uses the declared type.
1284 1286
    let mut irTyp = il::Type::W8;
1288 1290
    return il::DataItem::Val { typ: irTyp, val: constToScalar(val) };
1289 1291
}
1290 1292
1291 1293
/// Lower scalar-like constant nodes into data values, including fallback handling
1292 1294
/// for void-variant tags and slice string initializers.
1293 -
fn lowerConstScalarDataInto(
1294 -
    self: *mut Lowerer,
1295 +
unsafe fn lowerConstScalarDataInto(
1296 +
    self: &mut Lowerer,
1295 1297
    node: *ast::Node,
1296 1298
    ty: resolver::Type,
1297 1299
    dataPrefix: *[u8],
1298 -
    b: *mut DataValueBuilder
1300 +
    b: &mut DataValueBuilder
1299 1301
) throws (LowerError) {
1300 -
    let val = resolver::constValueEntry(self.resolver, node) else {
1301 -
        if let idx = voidVariantIndex(self.resolver, node) {
1302 +
    let val = resolver::constValueEntry(&*self.resolver, node) else {
1303 +
        if let idx = voidVariantIndex(&*self.resolver, node) {
1302 1304
            dataBuilderPush(b, il::DataValue {
1303 1305
                item: il::DataItem::Val { typ: il::Type::W8, val: idx },
1304 1306
                count: 1
1305 1307
            });
1306 1308
            return;
1320 1322
        count: 1
1321 1323
    });
1322 1324
}
1323 1325
1324 1326
/// Lower a constant or static declaration to the data section.
1325 -
fn lowerDataDecl(
1326 -
    self: *mut Lowerer,
1327 +
unsafe fn lowerDataDecl(
1328 +
    self: &mut Lowerer,
1327 1329
    node: *ast::Node,
1328 1330
    value: *ast::Node,
1329 1331
    readOnly: bool
1330 1332
) throws (LowerError) {
1331 -
    let data = resolver::nodeData(self.resolver, node);
1333 +
    let data = resolver::nodeData(&*self.resolver, node);
1332 1334
    let sym = data.sym else {
1333 1335
        throw LowerError::MissingSymbol(node);
1334 1336
    };
1335 1337
    if data.ty == resolver::Type::Unknown {
1336 1338
        throw LowerError::MissingType(node);
1350 1352
        values: result.values,
1351 1353
    }, self.allocator);
1352 1354
}
1353 1355
1354 1356
/// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`.
1355 -
fn dataSliceHeader(b: *mut DataValueBuilder, dataSym: *[u8], len: u32) {
1357 +
fn dataSliceHeader(b: &mut DataValueBuilder, dataSym: *[u8], len: u32) {
1356 1358
    dataBuilderPush(b, il::DataValue {
1357 1359
        item: il::DataItem::Sym(dataSym),
1358 1360
        count: 1
1359 1361
    });
1360 1362
    dataBuilderPush(b, il::DataValue {
1372 1374
        count: 1
1373 1375
    });
1374 1376
}
1375 1377
1376 1378
/// Lower a compile-time `&[...]` expression to a concrete slice header.
1377 -
fn lowerConstAddressSliceInto(
1378 -
    self: *mut Lowerer,
1379 +
unsafe fn lowerConstAddressSliceInto(
1380 +
    self: &mut Lowerer,
1379 1381
    addr: ast::AddressOf,
1380 1382
    ty: resolver::Type,
1381 1383
    dataPrefix: *[u8],
1382 -
    b: *mut DataValueBuilder
1384 +
    b: &mut DataValueBuilder
1383 1385
) throws (LowerError) {
1384 1386
    let case resolver::Type::Slice { mutable, .. } = ty
1385 1387
        else throw LowerError::ExpectedSliceOrArray;
1386 -
    let targetTy = resolver::typeFor(self.resolver, addr.target)
1388 +
    let targetTy = resolver::typeFor(&*self.resolver, addr.target)
1387 1389
        else throw LowerError::MissingType(addr.target);
1388 1390
    let case resolver::Type::Array(arrInfo) = targetTy
1389 1391
        else throw LowerError::ExpectedArray;
1390 1392
1391 1393
    let mut nested = dataBuilder(self.allocator);
1407 1409
    dataSliceHeader(b, dataName, arrInfo.length);
1408 1410
}
1409 1411
1410 1412
/// Lower a constant expression payload into a builder without slot padding.
1411 1413
/// Compute the type layout only when undefined data needs a byte count.
1412 -
fn lowerConstDataPayloadInto(
1413 -
    self: *mut Lowerer,
1414 +
unsafe fn lowerConstDataPayloadInto(
1415 +
    self: &mut Lowerer,
1414 1416
    node: *ast::Node,
1415 1417
    ty: resolver::Type,
1416 1418
    dataPrefix: *[u8],
1417 -
    b: *mut DataValueBuilder
1419 +
    b: &mut DataValueBuilder
1418 1420
) throws (LowerError) {
1419 1421
    // Function pointer references in constant data.
1420 1422
    if let case resolver::Type::Fn(_) = ty {
1421 -
        let sym = resolver::nodeData(self.resolver, node).sym
1423 +
        let sym = resolver::nodeData(&*self.resolver, node).sym
1422 1424
            else throw LowerError::MissingSymbol(node);
1423 -
        let modId = resolver::moduleIdForSymbol(self.resolver, sym);
1425 +
        let modId = resolver::moduleIdForSymbol(&*self.resolver, sym);
1424 1426
        let qualName = qualifyName(self, modId, sym.name);
1425 1427
        dataBuilderPush(b, il::DataValue {
1426 1428
            item: il::DataItem::Fn(qualName), count: 1,
1427 1429
        });
1428 1430
        return;
1429 1431
    }
1430 1432
    // In constant data, a void variant of a mixed union still occupies the
1431 1433
    // full tagged-union slot. Emitting only the tag corrupts following fields.
1432 -
    if let sym = resolver::nodeData(self.resolver, node).sym {
1434 +
    if let sym = resolver::nodeData(&*self.resolver, node).sym {
1433 1435
        if let case resolver::SymbolData::Variant { type: resolver::Type::Void, .. } = sym.data {
1434 1436
            if let case resolver::Type::Nominal(resolver::NominalType::Union(_)) = ty {
1435 1437
                try lowerConstUnionVariantInto(self, node, sym, ty, &mut [], dataPrefix, b);
1436 1438
                return;
1437 1439
            }
1450 1452
        case ast::NodeValue::ArrayRepeatLit(repeat) =>
1451 1453
            try lowerConstArrayRepeatInto(self, repeat, ty, dataPrefix, b),
1452 1454
        case ast::NodeValue::RecordLit(recLit) =>
1453 1455
            try lowerConstRecordLitInto(self, node, recLit, ty, dataPrefix, b),
1454 1456
        case ast::NodeValue::Call(call) => {
1455 -
            let calleeSym = resolver::nodeData(self.resolver, call.callee).sym
1457 +
            let calleeSym = resolver::nodeData(&*self.resolver, call.callee).sym
1456 1458
                else throw LowerError::MissingSymbol(call.callee);
1457 1459
            match calleeSym.data {
1458 1460
                case resolver::SymbolData::Variant { .. } =>
1459 1461
                    try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b),
1460 1462
                case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => {
1466 1468
        case ast::NodeValue::AddressOf(addr) => {
1467 1469
            try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b);
1468 1470
        }
1469 1471
        case ast::NodeValue::Ident(_) => {
1470 1472
            // Identifier referencing a constant.
1471 -
            let sym = resolver::nodeData(self.resolver, node).sym
1473 +
            let sym = resolver::nodeData(&*self.resolver, node).sym
1472 1474
                else throw LowerError::MissingSymbol(node);
1473 1475
            let case ast::NodeValue::ConstDecl(decl) = sym.node.value
1474 1476
                else throw LowerError::MissingConst(node);
1475 1477
1476 1478
            try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b);
1477 1479
        },
1478 1480
        case ast::NodeValue::ScopeAccess(_) => {
1479 -
            let sym = resolver::nodeData(self.resolver, node).sym
1481 +
            let sym = resolver::nodeData(&*self.resolver, node).sym
1480 1482
                else throw LowerError::MissingSymbol(node);
1481 1483
            if let case ast::NodeValue::ConstDecl(decl) = sym.node.value {
1482 1484
                try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b);
1483 1485
            } else {
1484 1486
                try lowerConstScalarDataInto(self, node, ty, dataPrefix, b);
1490 1492
        }
1491 1493
    }
1492 1494
}
1493 1495
1494 1496
/// Lower a constant expression into a builder, padding to the given slot size.
1495 -
fn lowerConstDataInto(
1496 -
    self: *mut Lowerer,
1497 +
unsafe fn lowerConstDataInto(
1498 +
    self: &mut Lowerer,
1497 1499
    node: *ast::Node,
1498 1500
    ty: resolver::Type,
1499 1501
    slotSize: u32,
1500 1502
    dataPrefix: *[u8],
1501 -
    b: *mut DataValueBuilder
1503 +
    b: &mut DataValueBuilder
1502 1504
) throws (LowerError) {
1503 1505
    let layout = resolver::getTypeLayout(ty);
1504 1506
    try lowerConstDataPayloadInto(self, node, ty, dataPrefix, b);
1505 1507
    // Pad to fill the enclosing slot.
1506 1508
    let padding = slotSize - layout.size;
1509 1511
    }
1510 1512
}
1511 1513
1512 1514
/// Flatten a constant array literal `[a, b, c]` into a builder.
1513 1515
/// Each element payload fills its type size; no extra slot padding is needed.
1514 -
fn lowerConstArrayLitInto(
1515 -
    self: *mut Lowerer,
1516 +
unsafe fn lowerConstArrayLitInto(
1517 +
    self: &mut Lowerer,
1516 1518
    elems: *mut [*ast::Node],
1517 1519
    ty: resolver::Type,
1518 1520
    dataPrefix: *[u8],
1519 -
    b: *mut DataValueBuilder
1521 +
    b: &mut DataValueBuilder
1520 1522
) throws (LowerError) {
1521 1523
    let case resolver::Type::Array(arrInfo) = ty
1522 1524
        else throw LowerError::ExpectedArray;
1523 1525
    let elemTy = *arrInfo.item;
1524 1526
1528 1530
}
1529 1531
1530 1532
/// Build data values for a constant array repeat literal `[item; count]`.
1531 1533
/// Repeat element payloads without extra slot padding. Undefined data uses
1532 1534
/// the element layout to compute the total byte count.
1533 -
fn lowerConstArrayRepeatInto(
1534 -
    self: *mut Lowerer,
1535 +
unsafe fn lowerConstArrayRepeatInto(
1536 +
    self: &mut Lowerer,
1535 1537
    repeat: ast::ArrayRepeatLit,
1536 1538
    ty: resolver::Type,
1537 1539
    dataPrefix: *[u8],
1538 -
    b: *mut DataValueBuilder
1540 +
    b: &mut DataValueBuilder
1539 1541
) throws (LowerError) {
1540 1542
    let case resolver::Type::Array(arrInfo) = ty
1541 1543
        else throw LowerError::ExpectedArray;
1542 1544
    let length = arrInfo.length;
1543 1545
    let elemTy = *arrInfo.item;
1546 1548
        let elemLayout = resolver::getTypeLayout(elemTy);
1547 1549
        dataBuilderPush(b, il::DataValue {
1548 1550
            item: il::DataItem::Undef,
1549 1551
            count: elemLayout.size * length
1550 1552
        });
1551 -
    } else if let val = resolver::constValueEntry(self.resolver, repeat.item) {
1553 +
    } else if let val = resolver::constValueEntry(&*self.resolver, repeat.item) {
1552 1554
        if let case resolver::ConstValue::String(_) = val {
1553 1555
            // A string used as a slice is represented by a three-word slice
1554 1556
            // header, not by the bytes of the string itself.
1555 1557
            for _ in 0..length {
1556 1558
                try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b);
1568 1570
    }
1569 1571
}
1570 1572
1571 1573
/// Build data values for a constant record literal.
1572 1574
/// Each field is lowered with a slot size that includes trailing padding.
1573 -
fn lowerConstRecordLitInto(
1574 -
    self: *mut Lowerer,
1575 +
unsafe fn lowerConstRecordLitInto(
1576 +
    self: &mut Lowerer,
1575 1577
    node: *ast::Node,
1576 1578
    recLit: ast::RecordLit,
1577 1579
    ty: resolver::Type,
1578 1580
    dataPrefix: *[u8],
1579 -
    b: *mut DataValueBuilder
1581 +
    b: &mut DataValueBuilder
1580 1582
) throws (LowerError) {
1581 1583
    match ty {
1582 1584
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
1583 1585
            try lowerConstRecordCtorInto(self, recLit.fields, recInfo, dataPrefix, b);
1584 1586
        }
1585 1587
        case resolver::Type::Nominal(resolver::NominalType::Union(_)) => {
1586 1588
            let typeName = recLit.typeName else {
1587 1589
                throw LowerError::ExpectedVariant;
1588 1590
            };
1589 -
            let sym = resolver::nodeData(self.resolver, typeName).sym else {
1591 +
            let sym = resolver::nodeData(&*self.resolver, typeName).sym else {
1590 1592
                throw LowerError::MissingSymbol(typeName);
1591 1593
            };
1592 1594
            try lowerConstUnionVariantInto(self, node, sym, ty, recLit.fields, dataPrefix, b);
1593 1595
        }
1594 1596
        else => throw LowerError::ExpectedRecord,
1595 1597
    }
1596 1598
}
1597 1599
1598 1600
/// Build data values for record constants.
1599 -
fn lowerConstRecordCtorInto(
1600 -
    self: *mut Lowerer,
1601 +
unsafe fn lowerConstRecordCtorInto(
1602 +
    self: &mut Lowerer,
1601 1603
    args: *mut [*ast::Node],
1602 1604
    recInfo: resolver::RecordType,
1603 1605
    dataPrefix: *[u8],
1604 -
    b: *mut DataValueBuilder
1606 +
    b: &mut DataValueBuilder
1605 1607
) throws (LowerError) {
1606 1608
    let layout = recInfo.layout;
1607 1609
    for argNode, i in args {
1608 1610
        let mut valueNode = argNode;
1609 1611
        if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value {
1620 1622
        try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b);
1621 1623
    }
1622 1624
}
1623 1625
1624 1626
/// Build data values for a constant union variant value from payload fields/args.
1625 -
fn lowerConstUnionVariantInto(
1626 -
    self: *mut Lowerer,
1627 +
unsafe fn lowerConstUnionVariantInto(
1628 +
    self: &mut Lowerer,
1627 1629
    node: *ast::Node,
1628 1630
    variantSym: *mut resolver::Symbol,
1629 1631
    ty: resolver::Type,
1630 1632
    payloadArgs: *mut [*ast::Node],
1631 1633
    dataPrefix: *[u8],
1632 -
    b: *mut DataValueBuilder
1634 +
    b: &mut DataValueBuilder
1633 1635
) throws (LowerError) {
1634 1636
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data
1635 1637
        else throw LowerError::UnexpectedNodeValue(node);
1636 1638
1637 1639
    let unionInfo = unionInfoFromType(ty) else {
1680 1682
    }
1681 1683
}
1682 1684
1683 1685
/// Find an existing string data entry with matching content.
1684 1686
// TODO: Optimize with hash table or remove?
1685 -
fn findStringData(self: *Lowerer, s: *[u8]) -> ?*[u8] {
1687 +
fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] {
1686 1688
    for d in self.data {
1687 1689
        if d.values.len == 1 {
1688 1690
            if let case il::DataItem::Str(existing) = d.values[0].item {
1689 1691
                if mem::eq(existing, s) {
1690 1692
                    return d.name;
1695 1697
    return nil;
1696 1698
}
1697 1699
1698 1700
/// Compose a segmented symbol name from a list of path segments.
1699 1701
/// Example: `["func", "nominal", "VALUE"]` -> `func$nominal$VALUE`.
1700 -
fn buildSegmentedName(
1701 -
    self: *mut Lowerer,
1702 -
    segments: *[*[u8]]
1702 +
unsafe fn buildSegmentedName(
1703 +
    self: &mut Lowerer,
1704 +
    segments: &[*[u8]]
1703 1705
) -> *[u8] throws (LowerError) {
1704 1706
    assert segments.len > 0;
1705 1707
1706 1708
    let mut totalLen: u32 = 0;
1707 1709
    for segment in segments {
1708 1710
        set totalLen += segment.len;
1709 1711
    }
1710 1712
    if segments.len > 1 {
1711 1713
        set totalLen += segments.len - 1;
1712 1714
    }
1713 -
    let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8];
1715 +
    let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8];
1714 1716
    let mut pos: u32 = 0;
1715 1717
1716 1718
    for segment, i in segments {
1717 1719
        set pos += try! mem::copy(&mut buf[pos..], segment);
1718 1720
        if i + 1 <> segments.len {
1724 1726
1725 1727
    return &buf[..totalLen];
1726 1728
}
1727 1729
1728 1730
/// Generate a unique name for declaration-local backing data entries.
1729 -
fn nextDeclDataName(
1730 -
    self: *mut Lowerer,
1731 +
unsafe fn nextDeclDataName(
1732 +
    self: &mut Lowerer,
1731 1733
    prefix: *[u8],
1732 1734
    count: u32,
1733 1735
    namespace: *[u8]
1734 1736
) -> *[u8] throws (LowerError) {
1735 1737
    let mut digits: [u8; fmt::U32_STR_LEN] = undefined;
1736 -
    let suffix = fmt::formatU32(count, &mut digits[..]);
1737 -
    let segments: *mut [*[u8]] = &mut [prefix, namespace, suffix];
1738 +
    let start = fmt::formatU32(count, &mut digits[..]);
1739 +
    let suffix = try! alloc::allocSlice(&mut *self.arena, 1, 1, digits.len - start) as *mut [u8];
1740 +
    try! mem::copy(suffix, &digits[start..]);
1741 +
    let segments = [prefix, namespace, suffix];
1738 1742
1739 -
    return try buildSegmentedName(self, segments);
1743 +
    return try buildSegmentedName(self, &segments[..]);
1740 1744
}
1741 1745
1742 1746
/// Append a data entry using a function-local literal namespace (`prefix$literal$N`).
1743 -
fn pushDeclData(
1744 -
    self: *mut Lowerer,
1747 +
unsafe fn pushDeclData(
1748 +
    self: &mut Lowerer,
1745 1749
    size: u32,
1746 1750
    alignment: u32,
1747 1751
    readOnly: bool,
1748 1752
    values: *[il::DataValue],
1749 1753
    dataPrefix: *[u8]
1750 1754
) -> *[u8] throws (LowerError) {
1751 -
    let name = try nextDeclDataName(self, dataPrefix, self.data.len, "literal");
1755 +
    let dataCount = self.data.len;
1756 +
    let name = try nextDeclDataName(self, dataPrefix, dataCount, "literal");
1752 1757
    self.data.append(il::Data {
1753 1758
        name,
1754 1759
        size,
1755 1760
        alignment,
1756 1761
        readOnly,
1760 1765
1761 1766
    return name;
1762 1767
}
1763 1768
1764 1769
/// Find or create read-only string data and return its symbol name.
1765 -
fn getOrCreateStringData(
1766 -
    self: *mut Lowerer,
1770 +
unsafe fn getOrCreateStringData(
1771 +
    self: &mut Lowerer,
1767 1772
    s: *[u8],
1768 1773
    dataPrefix: *[u8]
1769 1774
) -> *[u8] throws (LowerError) {
1770 1775
    if let existing = findStringData(self, s) {
1771 1776
        return existing;
1772 1777
    }
1773 1778
    let values = try! alloc::allocSlice(
1774 -
        self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1
1779 +
        &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1
1775 1780
    ) as *mut [il::DataValue];
1776 1781
1777 1782
    set values[0] = il::DataValue {
1778 1783
        item: il::DataItem::Str(s),
1779 1784
        count: 1
1817 1822
            },
1818 1823
    }
1819 1824
}
1820 1825
1821 1826
/// Compare two data value slices for structural equality.
1822 -
fn dataValuesEq(a: *[il::DataValue], b: *[il::DataValue]) -> bool {
1827 +
fn dataValuesEq(a: &[il::DataValue], b: &[il::DataValue]) -> bool {
1823 1828
    if a.len <> b.len {
1824 1829
        return false;
1825 1830
    }
1826 1831
    for i in 0..a.len {
1827 1832
        if a[i].count <> b[i].count {
1834 1839
    return true;
1835 1840
}
1836 1841
1837 1842
/// Find an existing read-only slice data entry with matching values.
1838 1843
// TODO: Optimize with hash table or remove?
1839 -
fn findSliceData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1844 +
fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1840 1845
    for d in self.data {
1841 1846
        if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) {
1842 1847
            return d.name;
1843 1848
        }
1844 1849
    }
1845 1850
    return nil;
1846 1851
}
1847 1852
1848 1853
/// Find existing constant data entry with matching content.
1849 1854
/// Handles both string data and slice data.
1850 -
fn findConstData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1855 +
fn findConstData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] {
1851 1856
    // Fast path for strings.
1852 1857
    if values.len == 1 and alignment == 1 {
1853 1858
        if let case il::DataItem::Str(s) = values[0].item {
1854 1859
            return findStringData(self, s);
1855 1860
        }
1858 1863
    return findSliceData(self, values, alignment);
1859 1864
}
1860 1865
1861 1866
/// Lower constant data to a slice value.
1862 1867
/// Creates or reuses a data section entry, then builds a slice header on the stack.
1863 -
fn lowerConstDataAsSlice(
1864 -
    self: *mut FnLowerer,
1865 -
    result: *ConstDataResult,
1868 +
unsafe fn lowerConstDataAsSlice(
1869 +
    self: &mut FnLowerer,
1870 +
    result: &ConstDataResult,
1866 1871
    alignment: u32,
1867 1872
    readOnly: bool,
1868 1873
    elemTy: *resolver::Type,
1869 1874
    mutable: bool,
1870 1875
    length: u32
1873 1878
    let elemLayout = resolver::getTypeLayout(*elemTy);
1874 1879
    let size = elemLayout.size * length;
1875 1880
    let mut dataName: *[u8] = undefined;
1876 1881
    let mut found: ?*[u8] = nil;
1877 1882
    if readOnly {
1878 -
        set found = findConstData(self.low, values, alignment);
1883 +
        set found = findConstData(&mut *self.low, values, alignment);
1879 1884
    }
1880 1885
    if let name = found {
1881 1886
        set dataName = name;
1882 1887
    } else {
1883 1888
        set dataName = try nextDataName(self);
1899 1904
        self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64)
1900 1905
    );
1901 1906
}
1902 1907
1903 1908
/// Generate a unique data name for inline literals, eg. `fnName$literal$N`.
1904 -
fn nextDataName(self: *mut FnLowerer) -> *[u8] throws (LowerError) {
1909 +
unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) {
1905 1910
    let counter = self.dataCounter;
1906 1911
    set self.dataCounter += 1;
1907 -
    return try nextDeclDataName(self.low, self.fnName, counter, "literal");
1912 +
    let fnName = self.fnName;
1913 +
    return try nextDeclDataName(&mut *self.low, fnName, counter, "literal");
1908 1914
}
1909 1915
1910 1916
/// Assign a unique function-local data symbol name.
1911 -
fn registerLocalDataDeclName(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) {
1912 -
    let sym = resolver::nodeData(self.low.resolver, node).sym
1917 +
unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
1918 +
    let sym = resolver::nodeData(&*self.low.resolver, node).sym
1913 1919
        else throw LowerError::MissingSymbol(node);
1914 1920
1915 1921
    let prefix = self.fnName;
1916 -
    let segments: *mut [*[u8]] = &mut [prefix, "nominal", sym.name];
1917 -
    let name = try buildSegmentedName(self.low, segments);
1922 +
    let segments = [prefix, "nominal", sym.name];
1923 +
    let name = try buildSegmentedName(&mut *self.low, &segments[..]);
1918 1924
1919 1925
    set sym.name = name;
1920 1926
}
1921 1927
1922 1928
/// Get the next available SSA register.
1923 -
fn nextReg(self: *mut FnLowerer) -> il::Reg {
1929 +
fn nextReg(self: &mut FnLowerer) -> il::Reg {
1924 1930
    let reg = il::Reg { n: self.regCounter };
1925 1931
    set self.regCounter += 1;
1926 1932
    return reg;
1927 1933
}
1928 1934
1929 1935
/// Look up the resolved type of an AST node, or throw `MissingType`.
1930 -
fn typeOf(self: *mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
1931 -
    let ty = resolver::typeFor(self.low.resolver, node)
1936 +
unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
1937 +
    let ty = resolver::typeFor(&*self.low.resolver, node)
1932 1938
        else throw LowerError::MissingType(node);
1933 1939
    return ty;
1934 1940
}
1935 1941
1936 1942
/// Look up the symbol for an AST node, or throw `MissingSymbol`.
1937 -
fn symOf(self: *mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) {
1938 -
    let sym = resolver::nodeData(self.low.resolver, node).sym
1943 +
unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) {
1944 +
    let sym = resolver::nodeData(&*self.low.resolver, node).sym
1939 1945
        else throw LowerError::MissingSymbol(node);
1940 1946
    return sym;
1941 1947
}
1942 1948
1943 1949
/// Remove the last block parameter and its associated variable.
1944 1950
/// Used when detecting a trivial phi that can be eliminated.
1945 -
fn removeLastBlockParam(self: *mut FnLowerer, block: BlockId) {
1951 +
fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) {
1946 1952
    let blk = getBlockMut(self, block);
1947 1953
    if blk.params.len > 0 {
1948 1954
        // TODO: Use `pop`?
1949 1955
        set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap);
1950 1956
    }
1957 1963
/// Rewrite cached SSA values for a variable across all blocks, and also
1958 1964
/// rewrite any terminator arguments that reference the provisional register.
1959 1965
/// The latter is necessary because recursive SSA resolution may have already
1960 1966
/// patched terminator arguments with the provisional value before it was
1961 1967
/// found to be trivial.
1962 -
fn rewriteCachedVarValue(self: *mut FnLowerer, v: Var, from: il::Val, to: il::Val) {
1968 +
fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) {
1963 1969
    for i in 0..self.blockData.len {
1964 1970
        let blk = getBlockMut(self, BlockId(i));
1965 1971
        if blk.vars[*v] == from {
1966 1972
            set blk.vars[*v] = to;
1967 1973
        }
2014 2020
/// Create a new basic block with the given label base.
2015 2021
///
2016 2022
/// The block is initially unsealed (predecessors may be added later) and empty.
2017 2023
/// Returns a [`BlockId`] that can be used for jumps and branches. The block must
2018 2024
/// be switched to via [`switchToBlock`] before instructions can be emitted.
2019 -
fn createBlock(self: *mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) {
2025 +
unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) {
2020 2026
    let label = try nextLabel(self, labelBase);
2021 2027
    let id = BlockId(self.blockData.len);
2022 2028
    let varCount = self.fnType.localCount;
2023 -
    let vars = try! alloc::allocSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val];
2029 +
    let vars = try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val];
2024 2030
2025 2031
    for i in 0..varCount {
2026 2032
        set vars[i] = nil;
2027 2033
    }
2028 2034
    self.blockData.append(BlockData {
2039 2045
2040 2046
    return id;
2041 2047
}
2042 2048
2043 2049
/// Create a new block with a single parameter.
2044 -
fn createBlockWithParam(
2045 -
    self: *mut FnLowerer,
2050 +
unsafe fn createBlockWithParam(
2051 +
    self: &mut FnLowerer,
2046 2052
    labelBase: *[u8],
2047 2053
    param: il::Param
2048 2054
) -> BlockId throws (LowerError) {
2049 2055
    let block = try createBlock(self, labelBase);
2050 2056
    let blk = getBlockMut(self, block);
2053 2059
    return block;
2054 2060
}
2055 2061
2056 2062
/// Switch to building a different block.
2057 2063
/// All subsequent `emit` calls will add instructions to this block.
2058 -
fn switchToBlock(self: *mut FnLowerer, block: BlockId) {
2064 +
fn switchToBlock(self: &mut FnLowerer, block: BlockId) {
2059 2065
    set self.currentBlock = block;
2060 2066
}
2061 2067
2062 2068
/// Seal a block, indicating all predecessor edges are now known.
2063 2069
///
2064 2070
/// Sealing enables SSA construction to resolve variable uses by looking up
2065 2071
/// values from predecessors and inserting block parameters as needed. It
2066 2072
/// does not prevent instructions from being added to the block.
2067 -
fn sealBlock(self: *mut FnLowerer, block: BlockId) throws (LowerError) {
2073 +
unsafe fn sealBlock(self: &mut FnLowerer, block: BlockId) throws (LowerError) {
2068 2074
    let blk = getBlockMut(self, block);
2069 2075
    let case Sealed::No = blk.sealState else {
2070 2076
        return; // Already sealed.
2071 2077
    };
2072 2078
    // Keep the current parameter list. Resolution can add more parameters.
2078 2084
        try resolveBlockArgs(self, block, Var(varId), paramIdx);
2079 2085
    }
2080 2086
}
2081 2087
2082 2088
/// Seal a block and switch to it.
2083 -
fn switchToAndSeal(self: *mut FnLowerer, block: BlockId) throws (LowerError) {
2089 +
unsafe fn switchToAndSeal(self: &mut FnLowerer, block: BlockId) throws (LowerError) {
2084 2090
    try sealBlock(self, block);
2085 2091
    switchToBlock(self, block);
2086 2092
}
2087 2093
2088 2094
/// Get block data by block id.
2089 -
fn getBlock(self: *FnLowerer, block: BlockId) -> *BlockData {
2095 +
fn getBlock(self: &FnLowerer, block: BlockId) -> *BlockData {
2090 2096
    return &self.blockData[*block];
2091 2097
}
2092 2098
2093 2099
/// Get mutable block data by block id.
2094 -
fn getBlockMut(self: *mut FnLowerer, block: BlockId) -> *mut BlockData {
2100 +
fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *mut BlockData {
2095 2101
    return &mut self.blockData[*block];
2096 2102
}
2097 2103
2098 2104
/// Get the current block being built.
2099 -
fn currentBlock(self: *FnLowerer) -> BlockId {
2105 +
fn currentBlock(self: &FnLowerer) -> BlockId {
2100 2106
    let block = self.currentBlock else {
2101 2107
        panic "currentBlock: no current block";
2102 2108
    };
2103 2109
    return block;
2104 2110
}
2106 2112
//////////////////////////
2107 2113
// Instruction Emission //
2108 2114
//////////////////////////
2109 2115
2110 2116
/// Emit an instruction to the current block.
2111 -
fn emit(self: *mut FnLowerer, instr: il::Instr) {
2117 +
unsafe fn emit(self: &mut FnLowerer, instr: il::Instr) {
2112 2118
    let blk = self.currentBlock else panic;
2113 2119
    let mut block = getBlockMut(self, blk);
2114 2120
2115 2121
    // Track whether this function is a leaf.
2116 2122
    if self.isLeaf {
2126 2132
    }
2127 2133
    block.instrs.append(instr, self.allocator);
2128 2134
}
2129 2135
2130 2136
/// Emit an unconditional jump to `target`.
2131 -
fn emitJmp(self: *mut FnLowerer, target: BlockId) throws (LowerError) {
2137 +
unsafe fn emitJmp(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2132 2138
    emit(self, il::Instr::Jmp { target: *target, args: &mut [] });
2133 2139
    addPredecessor(self, target, currentBlock(self));
2134 2140
}
2135 2141
2136 2142
/// Emit an unconditional jump to `target` with a single argument.
2137 -
fn emitJmpWithArg(self: *mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) {
2143 +
unsafe fn emitJmpWithArg(self: &mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) {
2138 2144
    let args = try allocVal(self, arg);
2139 2145
    emit(self, il::Instr::Jmp { target: *target, args });
2140 2146
    addPredecessor(self, target, currentBlock(self));
2141 2147
}
2142 2148
2143 2149
/// Emit an unconditional jump to `target` and switch to it.
2144 -
fn switchAndJumpTo(self: *mut FnLowerer, target: BlockId) throws (LowerError) {
2150 +
unsafe fn switchAndJumpTo(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2145 2151
    try emitJmp(self, target);
2146 2152
    switchToBlock(self, target);
2147 2153
}
2148 2154
2149 2155
/// Emit a conditional branch based on `cond`.
2150 -
fn emitBr(self: *mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) {
2156 +
unsafe fn emitBr(self: &mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) {
2151 2157
    assert thenBlock <> elseBlock;
2152 2158
    emit(self, il::Instr::Br {
2153 2159
        op: il::CmpOp::Ne,
2154 2160
        typ: il::Type::W32,
2155 2161
        a: il::Val::Reg(cond),
2162 2168
    addPredecessor(self, thenBlock, currentBlock(self));
2163 2169
    addPredecessor(self, elseBlock, currentBlock(self));
2164 2170
}
2165 2171
2166 2172
/// Emit a compare-and-branch instruction with the given comparison op.
2167 -
fn emitBrCmp(
2168 -
    self: *mut FnLowerer,
2173 +
unsafe fn emitBrCmp(
2174 +
    self: &mut FnLowerer,
2169 2175
    op: il::CmpOp,
2170 2176
    typ: il::Type,
2171 2177
    a: il::Val,
2172 2178
    b: il::Val,
2173 2179
    thenBlock: BlockId,
2182 2188
    addPredecessor(self, thenBlock, currentBlock(self));
2183 2189
    addPredecessor(self, elseBlock, currentBlock(self));
2184 2190
}
2185 2191
2186 2192
/// Emit a guard that traps with `ebreak` when a comparison is false.
2187 -
fn emitTrapUnlessCmp(
2188 -
    self: *mut FnLowerer,
2193 +
unsafe fn emitTrapUnlessCmp(
2194 +
    self: &mut FnLowerer,
2189 2195
    op: il::CmpOp,
2190 2196
    typ: il::Type,
2191 2197
    a: il::Val,
2192 2198
    b: il::Val
2193 2199
) throws (LowerError) {
2222 2228
    }
2223 2229
    return aa <= bb;
2224 2230
}
2225 2231
2226 2232
/// Emit an `ebreak` when `a < b` holds.
2227 -
fn emitTrapIfLt(
2228 -
    self: *mut FnLowerer,
2233 +
unsafe fn emitTrapIfLt(
2234 +
    self: &mut FnLowerer,
2229 2235
    typ: il::Type,
2230 2236
    a: il::Val,
2231 2237
    b: il::Val
2232 2238
) throws (LowerError) {
2233 2239
    let trapBlock = try createBlock(self, "guard#trap");
2242 2248
    try switchToAndSeal(self, passBlock);
2243 2249
}
2244 2250
2245 2251
/// Emit a conditional branch. Uses fused compare-and-branch for simple scalar
2246 2252
/// comparisons, falls back to separate comparison plus branch otherwise.
2247 -
fn emitCondBranch(
2248 -
    self: *mut FnLowerer,
2253 +
unsafe fn emitCondBranch(
2254 +
    self: &mut FnLowerer,
2249 2255
    cond: *ast::Node,
2250 2256
    thenBlock: BlockId,
2251 2257
    elseBlock: BlockId
2252 2258
) throws (LowerError) {
2253 2259
    // Try fused compare-and-branch for simple scalar comparisons.
2258 2264
            let operandTy = scalarComparisonType(leftTy, rightTy);
2259 2265
            let unsigned = isUnsignedType(operandTy);
2260 2266
            if let op = cmpOpFrom(binop.op, unsigned) {
2261 2267
                let a = try lowerExpr(self, binop.left);
2262 2268
                let b = try lowerExpr(self, binop.right);
2263 -
                let typ = ilType(self.low, operandTy);
2269 +
                let typ = ilType(&mut *self.low, operandTy);
2264 2270
2265 2271
                // Swap operands if needed.
2266 2272
                match binop.op {
2267 2273
                    case ast::BinaryOp::Gt => // `a > b` = `b < a`
2268 2274
                        try emitBrCmp(self, op, typ, b, a, thenBlock, elseBlock),
2283 2289
2284 2290
    try emitBr(self, condReg, thenBlock, elseBlock);
2285 2291
}
2286 2292
2287 2293
/// Emit a 32-bit store instruction at the given offset.
2288 -
fn emitStoreW32At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2294 +
unsafe fn emitStoreW32At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2289 2295
    emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset });
2290 2296
}
2291 2297
2292 2298
/// Emit a 32-bit load instruction at the given offset.
2293 -
fn emitLoadW32At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2299 +
unsafe fn emitLoadW32At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2294 2300
    emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset });
2295 2301
}
2296 2302
2297 2303
/// Emit an 8-bit store instruction at the given offset.
2298 -
fn emitStoreW8At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2304 +
unsafe fn emitStoreW8At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2299 2305
    emit(self, il::Instr::Store { typ: il::Type::W8, src, dst, offset });
2300 2306
}
2301 2307
2302 2308
/// Emit an 8-bit load instruction at the given offset.
2303 -
fn emitLoadW8At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2309 +
unsafe fn emitLoadW8At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2304 2310
    emit(self, il::Instr::Load { typ: il::Type::W8, dst, src, offset });
2305 2311
}
2306 2312
2307 2313
/// Emit a 64-bit store instruction at the given offset.
2308 -
fn emitStoreW64At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2314 +
unsafe fn emitStoreW64At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) {
2309 2315
    emit(self, il::Instr::Store { typ: il::Type::W64, src, dst, offset });
2310 2316
}
2311 2317
2312 2318
/// Emit a 64-bit load instruction at the given offset.
2313 -
fn emitLoadW64At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2319 +
unsafe fn emitLoadW64At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) {
2314 2320
    emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset });
2315 2321
}
2316 2322
2317 2323
/// Load a tag from memory at `src` plus `offset` with the given IL type.
2318 -
fn loadTag(self: *mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val {
2324 +
unsafe fn loadTag(self: &mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val {
2319 2325
    let dst = nextReg(self);
2320 2326
    emit(self, il::Instr::Load { typ: tagType, dst, src, offset });
2321 2327
    return il::Val::Reg(dst);
2322 2328
}
2323 2329
2324 2330
/// Load the data pointer from a slice value.
2325 -
fn loadSlicePtr(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Reg {
2331 +
unsafe fn loadSlicePtr(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Reg {
2326 2332
    let ptrReg = nextReg(self);
2327 2333
    emitLoadW64At(self, ptrReg, sliceReg, SLICE_PTR_OFFSET);
2328 2334
    return ptrReg;
2329 2335
}
2330 2336
2331 2337
/// Load the length from a slice value.
2332 -
fn loadSliceLen(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2338 +
unsafe fn loadSliceLen(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2333 2339
    let lenReg = nextReg(self);
2334 2340
    emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET);
2335 2341
    return il::Val::Reg(lenReg);
2336 2342
}
2337 2343
2338 2344
/// Load the capacity from a slice value.
2339 -
fn loadSliceCap(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2345 +
unsafe fn loadSliceCap(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val {
2340 2346
    let capReg = nextReg(self);
2341 2347
    emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET);
2342 2348
    return il::Val::Reg(capReg);
2343 2349
}
2344 2350
2345 2351
/// Emit a load instruction for a scalar value at `src` plus `offset`.
2346 2352
/// For reading values that may be aggregates, use `emitRead` instead.
2347 -
fn emitLoad(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2353 +
unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2348 2354
    let dst = nextReg(self);
2349 -
    let ilTyp = ilType(self.low, typ);
2355 +
    let ilTyp = ilType(&mut *self.low, typ);
2350 2356
2351 2357
    if isSignedType(typ) {
2352 2358
        emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset });
2353 2359
    } else {
2354 2360
        emit(self, il::Instr::Load { typ: ilTyp, dst, src, offset });
2356 2362
    return il::Val::Reg(dst);
2357 2363
}
2358 2364
2359 2365
/// Read a value from memory at `src` plus `offset`. Aggregates are represented
2360 2366
/// as pointers, so we return the address directly. Scalars are loaded via [`emitLoad`].
2361 -
fn emitRead(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2367 +
unsafe fn emitRead(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val {
2362 2368
    if isAggregateType(typ) {
2363 2369
        let ptr = emitPtrOffset(self, src, offset);
2364 2370
        return il::Val::Reg(ptr);
2365 2371
    }
2366 2372
    return emitLoad(self, src, offset, typ);
2367 2373
}
2368 2374
2369 2375
/// Emit a copy instruction that loads a data symbol's address into a register.
2370 -
fn emitDataAddr(self: *mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2376 +
unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2371 2377
    let dst = nextReg(self);
2372 -
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2373 -
    let qualName = qualifyName(self.low, modId, sym.name);
2378 +
    let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym);
2379 +
    let qualName = qualifyName(&mut *self.low, modId, sym.name);
2374 2380
2375 2381
    emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) });
2376 2382
2377 2383
    return dst;
2378 2384
}
2379 2385
2380 2386
/// Emit a copy instruction that loads a function's address into a register.
2381 -
fn emitFnAddr(self: *mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2387 +
unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg {
2382 2388
    let dst = nextReg(self);
2383 -
    let modId = resolver::moduleIdForSymbol(self.low.resolver, sym);
2384 -
    let qualName = qualifyName(self.low, modId, sym.name);
2389 +
    let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym);
2390 +
    let qualName = qualifyName(&mut *self.low, modId, sym.name);
2385 2391
2386 2392
    emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) });
2387 2393
2388 2394
    return dst;
2389 2395
}
2390 2396
2391 2397
/// Emit pattern tests for a single pattern.
2392 -
fn emitPatternMatch(
2393 -
    self: *mut FnLowerer,
2394 -
    subject: *MatchSubject,
2398 +
unsafe fn emitPatternMatch(
2399 +
    self: &mut FnLowerer,
2400 +
    subject: &MatchSubject,
2395 2401
    pattern: *ast::Node,
2396 2402
    matchBlock: BlockId,
2397 2403
    fallthrough: BlockId
2398 2404
) throws (LowerError) {
2399 2405
    // Wildcards always match; array patterns are tested element-by-element
2433 2439
        }
2434 2440
        case MatchSubjectKind::Union(unionInfo) => {
2435 2441
            assert not isNil;
2436 2442
2437 2443
            let case resolver::NodeExtra::UnionVariant { tag: variantTag, .. } =
2438 -
                resolver::nodeData(self.low.resolver, pattern).extra
2444 +
                resolver::nodeData(&*self.low.resolver, pattern).extra
2439 2445
            else {
2440 2446
                throw LowerError::ExpectedVariant;
2441 2447
            };
2442 2448
            // Void unions are passed by value (the tag itself).
2443 2449
            // Non-void unions are passed by reference (need to load tag).
2479 2485
}
2480 2486
2481 2487
/// Emit branches for multiple patterns. The first pattern that matches
2482 2488
/// causes a jump to the match block. If no patterns match, we jump to the
2483 2489
/// fallthrough block.
2484 -
fn emitPatternMatches(
2485 -
    self: *mut FnLowerer,
2486 -
    subject: *MatchSubject,
2487 -
    patterns: *mut [*ast::Node],
2490 +
unsafe fn emitPatternMatches(
2491 +
    self: &mut FnLowerer,
2492 +
    subject: &MatchSubject,
2493 +
    patterns: &[*ast::Node],
2488 2494
    matchBlock: BlockId,
2489 2495
    fallthrough: BlockId
2490 2496
) throws (LowerError) {
2491 2497
    assert patterns.len > 0;
2492 2498
2508 2514
2509 2515
/// Emit a match binding pattern.
2510 2516
/// Binding patterns always match for regular values, but for optionals they
2511 2517
/// check for the presence of a value. Jumps to `valuePresent` on success,
2512 2518
/// `valueAbsent` on failure.
2513 -
fn emitBindingTest(
2514 -
    self: *mut FnLowerer,
2515 -
    subject: *MatchSubject,
2519 +
unsafe fn emitBindingTest(
2520 +
    self: &mut FnLowerer,
2521 +
    subject: &MatchSubject,
2516 2522
    valuePresent: BlockId,
2517 2523
    valueAbsent: BlockId
2518 2524
) throws (LowerError) {
2519 2525
    match subject.kind {
2520 2526
        case MatchSubjectKind::OptionalPtr, MatchSubjectKind::OptionalAggregate => {
2527 2533
        }
2528 2534
    }
2529 2535
}
2530 2536
2531 2537
/// Emit a jump to target if the current block hasn't terminated, then seal the target block.
2532 -
fn emitJmpAndSeal(self: *mut FnLowerer, target: BlockId) throws (LowerError) {
2538 +
unsafe fn emitJmpAndSeal(self: &mut FnLowerer, target: BlockId) throws (LowerError) {
2533 2539
    if not blockHasTerminator(self) {
2534 2540
        try emitJmp(self, target);
2535 2541
    }
2536 2542
    try sealBlock(self, target);
2537 2543
}
2538 2544
2539 2545
/// Check if the current block already has a terminator instruction.
2540 -
fn blockHasTerminator(self: *FnLowerer) -> bool {
2546 +
fn blockHasTerminator(self: &FnLowerer) -> bool {
2541 2547
    let blk = getBlock(self, currentBlock(self));
2542 2548
    if blk.instrs.len == 0 {
2543 2549
        return false;
2544 2550
    }
2545 2551
    match blk.instrs[blk.instrs.len - 1] {
2567 2573
///         return 0;   // @else diverges, no jump to merge.
2568 2574
///     }
2569 2575
///
2570 2576
/// In the above example, the merge block stays `nil`, and no code is generated
2571 2577
/// after the `if`. The merge block is created on first use.
2572 -
fn emitMergeIfUnterminated(self: *mut FnLowerer, mergeBlock: *mut ?BlockId) throws (LowerError) {
2578 +
unsafe fn emitMergeIfUnterminated(self: &mut FnLowerer, mergeBlock: &mut ?BlockId) throws (LowerError) {
2573 2579
    if not blockHasTerminator(self) {
2574 2580
        if *mergeBlock == nil {
2575 2581
            set *mergeBlock = try createBlock(self, "merge");
2576 2582
        }
2577 2583
        let target = *mergeBlock else { throw LowerError::MissingTarget; };
2583 2589
// Control Flow Edge Management //
2584 2590
//////////////////////////////////
2585 2591
2586 2592
/// Add a predecessor edge from `pred` to `target`.
2587 2593
/// Must be called before the target block is sealed. Duplicates are ignored.
2588 -
fn addPredecessor(self: *mut FnLowerer, target: BlockId, pred: BlockId) {
2594 +
fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) {
2589 2595
    let blk = getBlockMut(self, target);
2590 2596
    assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block";
2591 2597
    let preds = &mut blk.preds;
2592 2598
    for i in 0..preds.len {
2593 2599
        if preds[i] == *pred { // Avoid duplicate predecessor entries.
2596 2602
    }
2597 2603
    preds.append(*pred, self.allocator);
2598 2604
}
2599 2605
2600 2606
/// Finalize all blocks and return the block array.
2601 -
fn finalizeBlocks(self: *mut FnLowerer) -> *[il::Block] throws (LowerError) {
2607 +
unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *[il::Block] throws (LowerError) {
2608 +
    let blockCount = self.blockData.len;
2602 2609
    let blocks = try! alloc::allocSlice(
2603 -
        self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), self.blockData.len
2610 +
        &mut *self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount
2604 2611
    ) as *mut [il::Block];
2605 2612
2606 2613
    for i in 0..self.blockData.len {
2607 2614
        let data = &self.blockData[i];
2608 2615
2622 2629
// Loop Management //
2623 2630
/////////////////////
2624 2631
2625 2632
/// Enter a loop context for break/continue handling.
2626 2633
/// `continueBlock` is `nil` when the continue target is created lazily.
2627 -
fn enterLoop(self: *mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) {
2634 +
fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) {
2628 2635
    assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow";
2629 2636
    let slot = &mut self.loopStack[self.loopDepth];
2630 2637
2631 2638
    set slot.breakTarget = breakBlock;
2632 2639
    set slot.continueTarget = continueBlock;
2633 2640
    set self.loopDepth += 1;
2634 2641
}
2635 2642
2636 2643
/// Exit the current loop context.
2637 -
fn exitLoop(self: *mut FnLowerer) {
2644 +
fn exitLoop(self: &mut FnLowerer) {
2638 2645
    assert self.loopDepth <> 0, "exitLoop: loopDepth is zero";
2639 2646
    set self.loopDepth -= 1;
2640 2647
}
2641 2648
2642 2649
/// Get the current loop context.
2643 -
fn currentLoop(self: *mut FnLowerer) -> ?*mut LoopCtx {
2650 +
fn currentLoop(self: &mut FnLowerer) -> ?*mut LoopCtx {
2644 2651
    if self.loopDepth == 0 {
2645 2652
        return nil;
2646 2653
    }
2647 2654
    return &mut self.loopStack[self.loopDepth - 1];
2648 2655
}
2649 2656
2650 2657
/// Get or lazily create the continue target block for the current loop.
2651 -
fn getOrCreateContinueBlock(self: *mut FnLowerer) -> BlockId throws (LowerError) {
2658 +
unsafe fn getOrCreateContinueBlock(self: &mut FnLowerer) -> BlockId throws (LowerError) {
2652 2659
    let ctx = currentLoop(self) else {
2653 2660
        throw LowerError::OutsideOfLoop;
2654 2661
    };
2655 2662
    if let block = ctx.continueTarget {
2656 2663
        return block;
2659 2666
    set ctx.continueTarget = block;
2660 2667
    return block;
2661 2668
}
2662 2669
2663 2670
/// Allocate a slice of values in the lowering arena.
2664 -
fn allocVals(self: *mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) {
2665 -
    return try! alloc::allocSlice(self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val];
2671 +
unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) {
2672 +
    return try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val];
2666 2673
}
2667 2674
2668 2675
/// Allocate a single-value slice in the lowering arena.
2669 -
fn allocVal(self: *mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) {
2676 +
unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) {
2670 2677
    let args = try allocVals(self, 1);
2671 2678
    set args[0] = val;
2672 2679
    return args;
2673 2680
}
2674 2681
2761 2768
// block is later sealed via [`sealBlock`], all incomplete block params are resolved.
2762 2769
2763 2770
/// Declare a new source-level variable and define its initial value.
2764 2771
/// If called before any block exists (e.g., for parameters), the definition is skipped.
2765 2772
fn newVar(
2766 -
    self: *mut FnLowerer,
2773 +
    self: &mut FnLowerer,
2767 2774
    name: ?*[u8],
2768 2775
    type: il::Type,
2769 2776
    mutable: bool,
2770 2777
    val: il::Val
2771 2778
) -> Var {
2781 2788
2782 2789
/// Define (write) a variable. Record the SSA value of a variable in the
2783 2790
/// current block. Called when a variable is assigned or initialized (`let`
2784 2791
/// bindings, assignments, loop updates). When [`useVar`] is later called,
2785 2792
/// it will retrieve this value.
2786 -
fn defVar(self: *mut FnLowerer, v: Var, val: il::Val) {
2793 +
fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) {
2787 2794
    assert *v < self.vars.len;
2788 2795
    set getBlockMut(self, currentBlock(self)).vars[*v] = val;
2789 2796
}
2790 2797
2791 2798
/// Use (read) the current value of a variable in the current block.
2792 2799
/// May insert block parameters if the value must come from predecessors.
2793 -
fn useVar(self: *mut FnLowerer, v: Var) -> il::Val throws (LowerError) {
2800 +
unsafe fn useVar(self: &mut FnLowerer, v: Var) -> il::Val throws (LowerError) {
2794 2801
    return try useVarInBlock(self, currentBlock(self), v);
2795 2802
}
2796 2803
2797 2804
/// Resolve which SSA definition of a variable reaches a use point in a given block.
2798 2805
///
2799 2806
/// Given a variable and a block where it's used, this function finds the
2800 2807
/// correct [`il::Val`] that holds the variable's value at that program point.
2801 2808
/// When control flow merges from multiple predecessors with different
2802 2809
/// definitions, it creates a block parameter to unify them.
2803 -
fn useVarInBlock(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
2810 +
unsafe fn useVarInBlock(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
2804 2811
    assert *v < self.vars.len;
2805 2812
2806 2813
    let blk = getBlockMut(self, block);
2807 2814
    if let val = blk.vars[*v] {
2808 2815
        return val;
2833 2840
    return try createBlockParam(self, block, v);
2834 2841
}
2835 2842
2836 2843
/// Look up a variable by name in the current scope.
2837 2844
/// Searches from most recently declared to first, enabling shadowing.
2838 -
fn lookupVarByName(self: *FnLowerer, name: *[u8]) -> ?Var {
2845 +
fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var {
2839 2846
    let mut id = self.vars.len;
2840 2847
    while id > 0 {
2841 2848
        set id -= 1;
2842 2849
        if let varName = self.vars[id].name {
2843 2850
            // Names are interned strings, so pointer comparison suffices.
2848 2855
    }
2849 2856
    return nil;
2850 2857
}
2851 2858
2852 2859
/// Look up a local variable bound to an identifier node.
2853 -
fn lookupLocalVar(self: *FnLowerer, node: *ast::Node) -> ?Var {
2860 +
fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var {
2854 2861
    let case ast::NodeValue::Ident(name) = node.value else {
2855 2862
        return nil;
2856 2863
    };
2857 2864
    return lookupVarByName(self, name);
2858 2865
}
2859 2866
2860 2867
/// Save current lexical variable scope depth.
2861 -
fn enterVarScope(self: *FnLowerer) -> u32 {
2868 +
fn enterVarScope(self: &FnLowerer) -> u32 {
2862 2869
    return self.vars.len;
2863 2870
}
2864 2871
2865 2872
/// Restore lexical variable scope depth.
2866 -
fn exitVarScope(self: *mut FnLowerer, savedVarsLen: u32) {
2873 +
fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) {
2867 2874
    set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap);
2868 2875
}
2869 2876
2870 2877
/// Get the metadata for a variable.
2871 -
fn getVar(self: *FnLowerer, v: Var) -> *VarData {
2878 +
fn getVar(self: &FnLowerer, v: Var) -> *VarData {
2872 2879
    assert *v < self.vars.len;
2873 2880
    return &self.vars[*v];
2874 2881
}
2875 2882
2876 2883
/// Create a block parameter to merge a variable's value from multiple
2887 2894
///     @end(w32 %1)              // x = %1, merged from predecessors
2888 2895
///       ret %1;
2889 2896
///
2890 2897
/// Create a register `%1` as a block parameter. In a sealed block, patch each
2891 2898
/// predecessor's jump to pass its value of `x`. Otherwise, defer until sealing.
2892 -
fn createBlockParam(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
2899 +
unsafe fn createBlockParam(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) {
2893 2900
    // Entry block must not have block parameters.
2894 2901
    assert block <> self.entryBlock, "createBlockParam: entry block must not have block parameters";
2895 2902
    // Allocate a register to hold the merged value.
2896 2903
    let reg = nextReg(self);
2897 2904
    let type = getVar(self, v).type;
2933 2940
///     x3 = phi(x1, x2)
2934 2941
///
2935 2942
/// This representation avoids the need for phi nodes to reference their
2936 2943
/// predecessor blocks explicitly, since the control flow edges already encode
2937 2944
/// that information.
2938 -
fn resolveBlockArgs(self: *mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) {
2945 +
unsafe fn resolveBlockArgs(self: &mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) {
2939 2946
    let blk = getBlock(self, block);
2940 2947
2941 2948
    // For each predecessor, recursively look up the variable's reaching definition
2942 2949
    // in that block, then patch the predecessor's terminator to pass that value
2943 2950
    // as an argument to this block's parameter.
2951 2958
    }
2952 2959
}
2953 2960
2954 2961
/// Check if a block parameter is trivial, i.e. all predecessors provide
2955 2962
/// the same value. Returns the trivial value if so.
2956 -
fn getTrivialPhiVal(self: *mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) {
2963 +
unsafe fn getTrivialPhiVal(self: &mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) {
2957 2964
    let blk = getBlock(self, block);
2958 2965
    // Get the block parameter register.
2959 2966
    let paramReg = blk.vars[*v];
2960 2967
    // Check if all predecessors provide the same value.
2961 2968
    let mut sameVal: ?il::Val = nil;
2985 2992
    return sameVal;
2986 2993
}
2987 2994
2988 2995
/// Patch a single terminator argument for a specific edge. This is used during
2989 2996
/// SSA construction to pass variable values along control flow edges.
2990 -
fn patchTerminatorArg(
2991 -
    self: *mut FnLowerer,
2997 +
unsafe fn patchTerminatorArg(
2998 +
    self: &mut FnLowerer,
2992 2999
    from: BlockId,         // The predecessor block containing the terminator to patch.
2993 3000
    target: u32,           // The index of the target block we're passing the value to.
2994 3001
    paramIdx: u32,         // The index of the block parameter to set.
2995 3002
    val: il::Val           // The value to pass as the argument.
2996 3003
) {
3033 3040
        }
3034 3041
    }
3035 3042
}
3036 3043
3037 3044
/// Grow an args array to hold at least the given capacity.
3038 -
fn growArgs(self: *mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] {
3045 +
unsafe fn growArgs(self: &mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] {
3039 3046
    if args.len >= capacity {
3040 3047
        return args;
3041 3048
    }
3042 3049
    let newArgs = try! alloc::allocSlice(
3043 -
        self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3050 +
        &mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity
3044 3051
    ) as *mut [il::Val];
3045 3052
3046 3053
    for arg, i in args {
3047 3054
        set newArgs[i] = arg;
3048 3055
    }
3063 3070
    return name;
3064 3071
}
3065 3072
3066 3073
/// Lower function parameters. Declares variables for each parameter.
3067 3074
/// When a receiver name is passed, we're handling a trait method.
3068 -
fn lowerParams(
3069 -
    self: *mut FnLowerer,
3075 +
unsafe fn lowerParams(
3076 +
    self: &mut FnLowerer,
3070 3077
    fnType: resolver::FnType,
3071 3078
    astParams: *mut [*ast::Node],
3072 3079
    receiverName: ?*ast::Node
3073 3080
) -> *[il::Param] throws (LowerError) {
3074 3081
    let offset: u32 = 1 if self.returnReg <> nil else 0;
3077 3084
        return &[];
3078 3085
    }
3079 3086
    assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS;
3080 3087
3081 3088
    let params = try! alloc::allocSlice(
3082 -
        self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3089 +
        &mut *self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen
3083 3090
    ) as *mut [il::Param];
3084 3091
3085 3092
    if let reg = self.returnReg {
3086 3093
        set params[0] = il::Param { value: reg, type: il::Type::W64 };
3087 3094
    }
3088 3095
    for i in 0..fnType.paramTypes.len as u32 {
3089 -
        let type = ilType(self.low, *fnType.paramTypes[i]);
3096 +
        let type = ilType(&mut *self.low, *fnType.paramTypes[i]);
3090 3097
        let reg = nextReg(self);
3091 3098
3092 3099
        set params[i + offset] = il::Param { value: reg, type };
3093 3100
3094 3101
        // Declare the parameter variable. For the receiver, the name comes
3113 3120
    }
3114 3121
    return params;
3115 3122
}
3116 3123
3117 3124
/// Resolve match subject.
3118 -
fn lowerMatchSubject(self: *mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) {
3125 +
unsafe fn lowerMatchSubject(self: &mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) {
3119 3126
    let mut val = try lowerExpr(self, subject);
3120 3127
    let subjectType = try typeOf(self, subject);
3121 3128
    let unwrapped = resolver::unwrapMatchSubject(subjectType);
3122 3129
3123 3130
    // When matching an aggregate by value, copy it to a fresh stack slot so
3130 3137
3131 3138
    let mut bindType = unwrapped.effectiveTy;
3132 3139
    if let case resolver::Type::Optional(inner) = unwrapped.effectiveTy {
3133 3140
        set bindType = *inner;
3134 3141
    }
3135 -
    let ilType = ilType(self.low, unwrapped.effectiveTy);
3142 +
    let ilType = ilType(&mut *self.low, unwrapped.effectiveTy);
3136 3143
    let kind = matchSubjectKind(unwrapped.effectiveTy);
3137 3144
3138 3145
    return MatchSubject { val, type: unwrapped.effectiveTy, ilType, bindType, kind, by: unwrapped.by };
3139 3146
}
3140 3147
3153 3160
        else => return false,
3154 3161
    }
3155 3162
}
3156 3163
3157 3164
/// Load the tag byte from a tagged value aggregate (optionals and unions).
3158 -
fn tvalTagReg(self: *mut FnLowerer, base: il::Reg) -> il::Reg {
3165 +
unsafe fn tvalTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg {
3159 3166
    let tagReg = nextReg(self);
3160 3167
    emitLoadW8At(self, tagReg, base, TVAL_TAG_OFFSET);
3161 3168
    return tagReg;
3162 3169
}
3163 3170
3164 3171
/// Load the tag word from a result aggregate.
3165 -
fn resultTagReg(self: *mut FnLowerer, base: il::Reg) -> il::Reg {
3172 +
unsafe fn resultTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg {
3166 3173
    let tagReg = nextReg(self);
3167 3174
    emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET);
3168 3175
    return tagReg;
3169 3176
}
3170 3177
3171 3178
/// Get the register to compare against `0` for optional `nil` checking.
3172 3179
/// For null-ptr-optimized types, loads the data pointer, or returns it
3173 3180
/// directly for scalar pointers. For aggregates, returns the tag register.
3174 -
fn optionalNilReg(self: *mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) {
3181 +
unsafe fn optionalNilReg(self: &mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) {
3175 3182
    let reg = emitValToReg(self, val);
3176 3183
3177 3184
    match typ {
3178 3185
        case resolver::Type::Optional(resolver::Type::Slice { .. }) => {
3179 3186
            let ptrReg = nextReg(self);
3185 3192
        else => return reg,
3186 3193
    }
3187 3194
}
3188 3195
3189 3196
/// Lower an optional nil check (`opt == nil` or `opt <> nil`).
3190 -
fn lowerNilCheck(self: *mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) {
3197 +
unsafe fn lowerNilCheck(self: &mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) {
3191 3198
    let optTy = try typeOf(self, opt);
3192 3199
    // Handle `nil == nil` or `nil <> nil`.
3193 3200
    if optTy == resolver::Type::Nil {
3194 3201
        return il::Val::Imm(1) if isEq else il::Val::Imm(0);
3195 3202
    }
3203 3210
    let op = il::BinOp::Eq if isEq else il::BinOp::Ne;
3204 3211
    return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0));
3205 3212
}
3206 3213
3207 3214
/// Load the payload value from a tagged value aggregate at the given offset.
3208 -
fn tvalPayloadVal(self: *mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val {
3215 +
unsafe fn tvalPayloadVal(self: &mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val {
3209 3216
    if payload == resolver::Type::Void {
3210 3217
        return il::Val::Undef;
3211 3218
    }
3212 3219
    return emitRead(self, base, valOffset, payload);
3213 3220
}
3214 3221
3215 3222
/// Compute the address of the payload in a tagged value aggregate.
3216 -
fn tvalPayloadAddr(self: *mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val {
3223 +
unsafe fn tvalPayloadAddr(self: &mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val {
3217 3224
    return il::Val::Reg(emitPtrOffset(self, base, valOffset));
3218 3225
}
3219 3226
3220 3227
/// Bind a variable to a tagged value's payload.
3221 -
fn bindPayloadVariable(
3222 -
    self: *mut FnLowerer,
3228 +
unsafe fn bindPayloadVariable(
3229 +
    self: &mut FnLowerer,
3223 3230
    name: *[u8],
3224 3231
    subjectVal: il::Val,
3225 3232
    bindType: resolver::Type,
3226 3233
    matchBy: resolver::MatchBy,
3227 3234
    valOffset: i32,
3234 3241
        case resolver::MatchBy::Value =>
3235 3242
            set payload = tvalPayloadVal(self, base, bindType, valOffset),
3236 3243
        case resolver::MatchBy::Ref, resolver::MatchBy::MutRef =>
3237 3244
            set payload = tvalPayloadAddr(self, base, valOffset),
3238 3245
    };
3239 -
    return newVar(self, name, ilType(self.low, bindType), mutable, payload);
3246 +
    return newVar(self, name, ilType(&mut *self.low, bindType), mutable, payload);
3240 3247
}
3241 3248
3242 3249
/// Bind an identifier from a matched subject.
3243 -
fn bindMatchVariable(
3244 -
    self: *mut FnLowerer,
3245 -
    subject: *MatchSubject,
3250 +
unsafe fn bindMatchVariable(
3251 +
    self: &mut FnLowerer,
3252 +
    subject: &MatchSubject,
3246 3253
    binding: *ast::Node,
3247 3254
    mutable: bool
3248 3255
) -> ?Var throws (LowerError) {
3249 3256
    // Only bind if the pattern is an identifier.
3250 3257
    let case ast::NodeValue::Ident(name) = binding.value else {
3255 3262
    if let case MatchSubjectKind::OptionalAggregate = subject.kind {
3256 3263
        let valOffset = resolver::getOptionalValOffset(subject.bindType) as i32;
3257 3264
        return try bindPayloadVariable(self, name, subject.val, subject.bindType, subject.by, valOffset, mutable);
3258 3265
    }
3259 3266
    // Declare the variable in the current block's scope.
3260 -
    return newVar(self, name, ilType(self.low, subject.bindType), mutable, subject.val);
3267 +
    return newVar(self, name, ilType(&mut *self.low, subject.bindType), mutable, subject.val);
3261 3268
}
3262 3269
3263 3270
/// Bind variables from inside case patterns (union variants, records, slices).
3264 3271
/// `failBlock` is passed when nested patterns may require additional tests
3265 3272
/// that branch on mismatch (e.g. nested union variant tests).
3266 -
fn bindPatternVariables(self: *mut FnLowerer, subject: *MatchSubject, patterns: *mut [*ast::Node], failBlock: BlockId) throws (LowerError) {
3273 +
unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) {
3267 3274
    for pattern in patterns {
3268 3275
3269 3276
        // Handle simple variant patterns like `Variant(x)`.
3270 -
        if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) {
3277 +
        if let arg = resolver::variantPatternBinding(&*self.low.resolver, pattern) {
3271 3278
            let case MatchSubjectKind::Union(unionInfo) = subject.kind
3272 3279
                else panic "bindPatternVariables: expected union subject";
3273 3280
            let valOffset = unionInfo.valOffset as i32;
3274 3281
3275 3282
            // Get the actual field type from the variant's record info.
3276 3283
            // This preserves the original data layout type (e.g. `*T`) even when
3277 3284
            // the resolver resolved the pattern against a dereferenced type (`T`).
3278 -
            let variantExtra = resolver::nodeData(self.low.resolver, pattern).extra;
3285 +
            let variantExtra = resolver::nodeData(&*self.low.resolver, pattern).extra;
3279 3286
            let case resolver::NodeExtra::UnionVariant { ordinal, .. } = variantExtra
3280 3287
                else panic "bindPatternVariables: expected variant extra";
3281 3288
            let payloadType = unionInfo.variants[ordinal].valueType;
3282 3289
            let payloadRec = resolver::getRecord(payloadType)
3283 3290
                else panic "bindPatternVariables: expected record payload";
3315 3322
}
3316 3323
3317 3324
/// Bind variables from an array literal pattern (e.g., `[a, 1, c]`).
3318 3325
/// Each element is either bound as a variable, skipped (placeholder), or
3319 3326
/// tested against the subject element, branching to `failBlock` on mismatch.
3320 -
fn bindArrayPatternElements(
3321 -
    self: *mut FnLowerer,
3322 -
    subject: *MatchSubject,
3327 +
unsafe fn bindArrayPatternElements(
3328 +
    self: &mut FnLowerer,
3329 +
    subject: &MatchSubject,
3323 3330
    items: *mut [*ast::Node],
3324 3331
    failBlock: BlockId
3325 3332
) throws (LowerError) {
3326 3333
    let case resolver::Type::Array(arrInfo) = subject.type
3327 3334
        else throw LowerError::ExpectedSliceOrArray;
3340 3347
        try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock);
3341 3348
    }
3342 3349
}
3343 3350
3344 3351
/// Bind fields from a record pattern.
3345 -
fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3352 +
unsafe fn bindRecordPatternFields(self: &mut FnLowerer, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3346 3353
    // No fields to bind (e.g., `{ .. }`).
3347 3354
    if lit.fields.len == 0 {
3348 3355
        return;
3349 3356
    }
3350 3357
    // Optional value patterns were already compared structurally by
3357 3364
    let case MatchSubjectKind::Union(unionInfo) = subject.kind
3358 3365
        else panic "bindRecordPatternFields: expected union subject";
3359 3366
3360 3367
    // Get the variant index from the pattern node.
3361 3368
    let case resolver::NodeExtra::UnionVariant { ordinal: variantOrdinal, .. } =
3362 -
        resolver::nodeData(self.low.resolver, pattern).extra
3369 +
        resolver::nodeData(&*self.low.resolver, pattern).extra
3363 3370
    else throw LowerError::MissingMetadata;
3364 3371
3365 3372
    // Get the record type from the variant's payload type.
3366 3373
    let payloadType = unionInfo.variants[variantOrdinal].valueType;
3367 3374
    let recInfo = resolver::getRecord(payloadType)
3375 3382
    try bindNestedRecordFields(self, payloadBase, lit, recInfo, subject.by, failBlock);
3376 3383
}
3377 3384
3378 3385
/// Bind a single record field to a pattern variable, with support for nested
3379 3386
/// pattern tests that branch to `failBlock` on mismatch.
3380 -
fn bindFieldVariable(
3381 -
    self: *mut FnLowerer,
3387 +
unsafe fn bindFieldVariable(
3388 +
    self: &mut FnLowerer,
3382 3389
    binding: *ast::Node,
3383 3390
    base: il::Reg,
3384 3391
    fieldInfo: resolver::RecordField,
3385 3392
    matchBy: resolver::MatchBy,
3386 3393
    failBlock: BlockId
3388 3395
    match binding.value {
3389 3396
        case ast::NodeValue::Ident(name) => {
3390 3397
            let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType)
3391 3398
                if matchBy == resolver::MatchBy::Value
3392 3399
                else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset));
3393 -
            newVar(self, name, ilType(self.low, fieldInfo.fieldType), false, val);
3400 +
            newVar(self, name, ilType(&mut *self.low, fieldInfo.fieldType), false, val);
3394 3401
        }
3395 3402
        case ast::NodeValue::Placeholder => {}
3396 3403
        case ast::NodeValue::RecordLit(lit) => {
3397 3404
            // Check if this record literal is a union variant pattern.
3398 3405
            if let keyNode = resolver::patternVariantKeyNode(binding) {
3399 -
                if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(self.low.resolver, keyNode).extra {
3406 +
                if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(&*self.low.resolver, keyNode).extra {
3400 3407
                    try emitNestedFieldTest(self, binding, base, fieldInfo, matchBy, failBlock);
3401 3408
                    return;
3402 3409
                }
3403 3410
            }
3404 3411
            // Plain nested record destructuring pattern.
3424 3431
}
3425 3432
3426 3433
/// Emit a nested pattern test for a record field value, branching to
3427 3434
/// `failBlock` if the pattern does not match. On success, continues in
3428 3435
/// a fresh block and binds any nested variables.
3429 -
fn emitNestedFieldTest(
3430 -
    self: *mut FnLowerer,
3436 +
unsafe fn emitNestedFieldTest(
3437 +
    self: &mut FnLowerer,
3431 3438
    pattern: *ast::Node,
3432 3439
    base: il::Reg,
3433 3440
    fieldInfo: resolver::RecordField,
3434 3441
    matchBy: resolver::MatchBy,
3435 3442
    failBlock: BlockId
3448 3455
            set derefBase = ptrReg;
3449 3456
            set fieldType = *target;
3450 3457
        }
3451 3458
    }
3452 3459
    // Build a MatchSubject for the nested field.
3453 -
    let ilTy = ilType(self.low, fieldType);
3460 +
    let ilTy = ilType(&mut *self.low, fieldType);
3454 3461
    let kind = matchSubjectKind(fieldType);
3455 3462
3456 3463
    // Determine the subject value.
3457 3464
    let mut val: il::Val = undefined;
3458 3465
    if let reg = derefBase {
3478 3485
    let continueBlock = try createBlock(self, "nest");
3479 3486
    try emitPatternMatch(self, &nestedSubject, pattern, continueBlock, failBlock);
3480 3487
    try switchToAndSeal(self, continueBlock);
3481 3488
3482 3489
    // After the test succeeds, bind any nested variables.
3483 -
    let patterns: *mut [*ast::Node] = &mut [pattern];
3484 -
    try bindPatternVariables(self, &nestedSubject, patterns, failBlock);
3490 +
    let patterns = [pattern];
3491 +
    try bindPatternVariables(self, &nestedSubject, &patterns[..], failBlock);
3485 3492
}
3486 3493
3487 3494
/// Bind variables from a nested record literal pattern.
3488 -
fn bindNestedRecordFields(
3489 -
    self: *mut FnLowerer,
3495 +
unsafe fn bindNestedRecordFields(
3496 +
    self: &mut FnLowerer,
3490 3497
    base: il::Reg,
3491 3498
    lit: ast::RecordLit,
3492 3499
    recInfo: resolver::RecordType,
3493 3500
    matchBy: resolver::MatchBy,
3494 3501
    failBlock: BlockId
3495 3502
) throws (LowerError) {
3496 3503
    for fieldNode in lit.fields {
3497 3504
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
3498 3505
            throw LowerError::UnexpectedNodeValue(fieldNode);
3499 3506
        };
3500 -
        let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode)
3507 +
        let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode)
3501 3508
            else throw LowerError::MissingMetadata;
3502 3509
        if fieldIdx >= recInfo.fields.len {
3503 3510
            throw LowerError::MissingMetadata;
3504 3511
        }
3505 3512
        let fieldInfo = recInfo.fields[fieldIdx];
3507 3514
        try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock);
3508 3515
    }
3509 3516
}
3510 3517
3511 3518
/// Lower function body to a list of basic blocks.
3512 -
fn lowerFnBody(self: *mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) {
3519 +
unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) {
3513 3520
    // Create and switch to entry block.
3514 3521
    let entry = try createBlock(self, "entry");
3515 3522
    set self.entryBlock = entry;
3516 3523
    switchToBlock(self, entry);
3517 3524
3540 3547
    }
3541 3548
    return try finalizeBlocks(self);
3542 3549
}
3543 3550
3544 3551
/// Lower a scalar match as a switch instruction.
3545 -
fn lowerMatchSwitch(self: *mut FnLowerer, prongs: *mut [*ast::Node], subject: *MatchSubject, mergeBlock: *mut ?BlockId) throws (LowerError) {
3552 +
unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *mut [*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) {
3546 3553
    let mut blocks: [BlockId; 32] = undefined;
3547 3554
    let mut cases: *mut [il::SwitchCase] = &mut [];
3548 3555
    let mut defaultIdx: u32 = 0;
3549 3556
    let entry = currentBlock(self);
3550 3557
3558 3565
                set defaultIdx = i;
3559 3566
            }
3560 3567
            case ast::ProngArm::Case(pats) => {
3561 3568
                set blocks[i] = try createBlock(self, "case");
3562 3569
                for pat in pats {
3563 -
                    let cv = resolver::constValueEntry(self.low.resolver, pat)
3570 +
                    let cv = resolver::constValueEntry(&*self.low.resolver, pat)
3564 3571
                        else throw LowerError::MissingConst(pat);
3565 3572
3566 3573
                    cases.append(il::SwitchCase {
3567 3574
                        value: constToScalar(cv),
3568 3575
                        target: *blocks[i],
3643 3650
///   arm#1:
3644 3651
///       jmp else#0;                     // guard failed, fallthrough to `else`
3645 3652
///   else#0:
3646 3653
///       ret 0;                          // `else` body
3647 3654
///
3648 -
fn lowerMatch(self: *mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) {
3655 +
unsafe fn lowerMatch(self: &mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) {
3649 3656
    assert m.prongs.len > 0;
3650 3657
3651 3658
    let prongs = m.prongs;
3652 3659
    // Lower the subject expression once; reused across all arms.
3653 3660
    let subject = try lowerMatchSubject(self, m.subject);
3654 3661
    // Merge block created lazily if any arm needs it (i.e., doesn't diverge).
3655 3662
    let mut mergeBlock: ?BlockId = nil;
3656 3663
3657 3664
    // Use `switch` instruction for matches with constant patterns.
3658 -
    if resolver::isMatchConst(self.low.resolver, node) {
3665 +
    if resolver::isMatchConst(&*self.low.resolver, node) {
3659 3666
        try lowerMatchSwitch(self, prongs, &subject, &mut mergeBlock);
3660 3667
        return;
3661 3668
    }
3662 3669
    // Fallback: chained branches.
3663 3670
    let firstArm = try createBlock(self, "arm");
3669 3676
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
3670 3677
            else panic "lowerMatch: expected match prong";
3671 3678
3672 3679
        let isLastArm = i + 1 == prongs.len;
3673 3680
        let hasGuard = prong.guard <> nil;
3674 -
        let catchAll = resolver::isProngCatchAll(self.low.resolver, prongNode);
3681 +
        let catchAll = resolver::isProngCatchAll(&*self.low.resolver, prongNode);
3675 3682
3676 3683
        // Entry block: guard block if present, otherwise the body block.
3677 3684
        // The guard block must be created before the body block so that
3678 3685
        // block indices are in reverse post-order (RPO), which the register
3679 3686
        // allocator requires.
3696 3703
        // Emit pattern test: branch to entry block on match, next arm on fail.
3697 3704
        match prong.arm {
3698 3705
            case ast::ProngArm::Binding(_) if not catchAll =>
3699 3706
                try emitBindingTest(self, &subject, entryBlock, nextArm),
3700 3707
            case ast::ProngArm::Case(patterns) if not catchAll =>
3701 -
                try emitPatternMatches(self, &subject, patterns, entryBlock, nextArm),
3708 +
                try emitPatternMatches(self, &subject, &patterns[..], entryBlock, nextArm),
3702 3709
            else =>
3703 3710
                try emitJmp(self, entryBlock),
3704 3711
        }
3705 3712
        // Switch to entry block, where any variable bindings need to be created.
3706 3713
        try switchToAndSeal(self, entryBlock);
3711 3718
        // block.
3712 3719
        match prong.arm {
3713 3720
            case ast::ProngArm::Binding(pat) =>
3714 3721
                try bindMatchVariable(self, &subject, pat, false),
3715 3722
            case ast::ProngArm::Case(patterns) =>
3716 -
                try bindPatternVariables(self, &subject, patterns, nextArm),
3723 +
                try bindPatternVariables(self, &subject, &patterns[..], nextArm),
3717 3724
            else => {},
3718 3725
        }
3719 3726
3720 3727
        // Evaluate guard if present; can still fail to next arm.
3721 3728
        if let g = prong.guard {
3747 3754
        try switchToAndSeal(self, blk);
3748 3755
    }
3749 3756
}
3750 3757
3751 3758
/// Lower an `if let` statement.
3752 -
fn lowerIfLet(self: *mut FnLowerer, cond: ast::IfLet) throws (LowerError) {
3759 +
unsafe fn lowerIfLet(self: &mut FnLowerer, cond: ast::IfLet) throws (LowerError) {
3753 3760
    let savedVarsLen = enterVarScope(self);
3754 3761
    let subject = try lowerMatchSubject(self, cond.pattern.scrutinee);
3755 3762
    let mut thenBlock: BlockId = undefined;
3756 3763
    if cond.pattern.guard == nil {
3757 3764
        set thenBlock = try createBlock(self, "then");
3785 3792
/// Emit pattern match branch with optional guard, and bind variables.
3786 3793
/// Used by `if-let`, `let-else`, and `while-let` lowering.
3787 3794
///
3788 3795
/// When a guard is present, the guard block is created before `successBlock`
3789 3796
/// to ensure block indices are in RPO.
3790 -
fn lowerPatternMatch(
3791 -
    self: *mut FnLowerer,
3792 -
    subject: *MatchSubject,
3793 -
    pat: *ast::PatternMatch,
3794 -
    successBlock: *mut BlockId,
3797 +
unsafe fn lowerPatternMatch(
3798 +
    self: &mut FnLowerer,
3799 +
    subject: &MatchSubject,
3800 +
    pat: &ast::PatternMatch,
3801 +
    successBlock: &mut BlockId,
3795 3802
    successLabel: *[u8],
3796 3803
    failBlock: BlockId
3797 3804
) throws (LowerError) {
3798 3805
    // If guard present, pattern match jumps to @guard, then guard evaluation
3799 3806
    // jumps to `successBlock` or `failBlock`. Otherwise, jump directly to
3805 3812
    } else {
3806 3813
        set targetBlock = *successBlock;
3807 3814
    }
3808 3815
    match pat.kind {
3809 3816
        case ast::PatternKind::Case => {
3810 -
            let patterns: *mut [*ast::Node] = &mut [pat.pattern];
3817 +
            let patterns = [pat.pattern];
3811 3818
            // Jump to `targetBlock` if the pattern matches, `failBlock` otherwise.
3812 -
            try emitPatternMatches(self, subject, patterns, targetBlock, failBlock);
3819 +
            try emitPatternMatches(self, subject, &patterns[..], targetBlock, failBlock);
3813 3820
            try switchToAndSeal(self, targetBlock);
3814 3821
            // Bind any variables inside the pattern. Nested patterns may
3815 3822
            // emit additional tests that branch to `failBlock`, switching
3816 3823
            // the current block.
3817 -
            try bindPatternVariables(self, subject, patterns, failBlock);
3824 +
            try bindPatternVariables(self, subject, &patterns[..], failBlock);
3818 3825
        }
3819 3826
        case ast::PatternKind::Binding => {
3820 3827
            // Jump to `targetBlock` if there is a value present, `failBlock` otherwise.
3821 3828
            try emitBindingTest(self, subject, targetBlock, failBlock);
3822 3829
            try switchToAndSeal(self, targetBlock);
3837 3844
        try switchToAndSeal(self, *successBlock);
3838 3845
    }
3839 3846
}
3840 3847
3841 3848
/// Lower a `let-else` statement.
3842 -
fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) {
3849 +
unsafe fn lowerLetElse(self: &mut FnLowerer, letElse: ast::LetElse) throws (LowerError) {
3843 3850
    let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee);
3844 3851
    let mut successBlock: BlockId = undefined;
3845 3852
    if letElse.pattern.guard == nil {
3846 3853
        set successBlock = try createBlock(self, "success");
3847 3854
    }
3878 3885
    // Continue at @merge after a successful match or value-producing fallback.
3879 3886
    try switchToAndSeal(self, mergeBlock);
3880 3887
}
3881 3888
3882 3889
/// Lower a `while let` loop as a match-driven loop.
3883 -
fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) {
3890 +
unsafe fn lowerWhileLet(self: &mut FnLowerer, w: ast::WhileLet) throws (LowerError) {
3884 3891
    let savedVarsLen = enterVarScope(self);
3885 3892
    // Create control flow blocks: loop header, body (created lazily when
3886 3893
    // there's a guard), and exit.
3887 3894
    let whileBlock = try createBlock(self, "while");
3888 3895
    let mut bodyBlock: BlockId = undefined;
3911 3918
///////////////////
3912 3919
// Node Lowering //
3913 3920
///////////////////
3914 3921
3915 3922
/// Lower an AST node.
3916 -
fn lowerNode(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) {
3923 +
unsafe fn lowerNode(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
3917 3924
    if self.low.options.debug {
3918 3925
        set self.srcLoc.offset = node.span.offset;
3919 3926
    }
3920 3927
    match node.value {
3921 3928
        case ast::NodeValue::Block(_) => {
3931 3938
            try lowerLet(self, node, l);
3932 3939
        }
3933 3940
        case ast::NodeValue::ConstDecl(decl) => {
3934 3941
            // Local constants lower to data declarations and emit no runtime code.
3935 3942
            try registerLocalDataDeclName(self, node);
3936 -
            try lowerDataDecl(self.low, node, decl.value, true);
3943 +
            try lowerDataDecl(&mut *self.low, node, decl.value, true);
3937 3944
        }
3938 3945
        case ast::NodeValue::StaticDecl(decl) => {
3939 3946
            // Local statics lower to data declarations and emit no runtime code.
3940 3947
            try registerLocalDataDeclName(self, node);
3941 -
            try lowerDataDecl(self.low, node, decl.value, false);
3948 +
            try lowerDataDecl(&mut *self.low, node, decl.value, false);
3942 3949
        }
3943 3950
        case ast::NodeValue::If(i) => {
3944 3951
            try lowerIf(self, i);
3945 3952
        }
3946 3953
        case ast::NodeValue::IfLet(i) => {
4001 4008
        }
4002 4009
    }
4003 4010
}
4004 4011
4005 4012
/// Lower a code block.
4006 -
fn lowerBlock(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) {
4013 +
unsafe fn lowerBlock(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) {
4007 4014
    let case ast::NodeValue::Block(blk) = node.value else {
4008 4015
        throw LowerError::ExpectedBlock(node);
4009 4016
    };
4010 4017
    let savedVarsLen = enterVarScope(self);
4011 4018
    for stmt in blk.statements {
4042 4049
4043 4050
/// Return the effective type of a node after any coercion applied by
4044 4051
/// the resolver. `lowerExpr` already materializes the coercion in the
4045 4052
/// IL value, so the lowerer must use the post-coercion type when
4046 4053
/// choosing how to compare or store that value.
4047 -
fn effectiveType(self: *mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
4054 +
unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
4048 4055
    let ty = try typeOf(self, node);
4049 -
    if let coerce = resolver::coercionFor(self.low.resolver, node) {
4056 +
    if let coerce = resolver::coercionFor(&*self.low.resolver, node) {
4050 4057
        if let case resolver::Coercion::OptionalLift(optTy) = coerce {
4051 4058
            return optTy;
4052 4059
        }
4053 4060
    }
4054 4061
    return ty;
4104 4111
}
4105 4112
4106 4113
/// Check if a node is a void union variant literal (e.g. `Color::Red`).
4107 4114
/// If so, returns the variant's tag index. This enables optimized comparisons
4108 4115
/// that only check the tag instead of doing full aggregate comparison.
4109 -
fn voidVariantIndex(res: *resolver::Resolver, node: *ast::Node) -> ?i64 {
4116 +
fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 {
4110 4117
    let data = resolver::nodeData(res, node);
4111 4118
    let sym = data.sym else {
4112 4119
        return nil;
4113 4120
    };
4114 4121
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else {
4120 4127
    }
4121 4128
    return index as i64;
4122 4129
}
4123 4130
4124 4131
/// Reserve stack storage for a value of the given type.
4125 -
fn emitReserve(self: *mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) {
4132 +
unsafe fn emitReserve(self: &mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) {
4126 4133
    let layout = resolver::getTypeLayout(typ);
4127 4134
    return emitReserveLayout(self, layout);
4128 4135
}
4129 4136
4130 4137
/// Reserve stack storage with an explicit layout.
4131 -
fn emitReserveLayout(self: *mut FnLowerer, layout: resolver::Layout) -> il::Reg {
4138 +
unsafe fn emitReserveLayout(self: &mut FnLowerer, layout: resolver::Layout) -> il::Reg {
4132 4139
    let dst = nextReg(self);
4133 4140
4134 4141
    emit(self, il::Instr::Reserve {
4135 4142
        dst,
4136 4143
        size: il::Val::Imm(layout.size as i64),
4138 4145
    });
4139 4146
    return dst;
4140 4147
}
4141 4148
4142 4149
/// Store a value into an address.
4143 -
fn emitStore(self: *mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) {
4150 +
unsafe fn emitStore(self: &mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) {
4144 4151
    // `undefined` values need no store.
4145 4152
    if let case il::Val::Undef = src {
4146 4153
        return;
4147 4154
    }
4148 4155
    if isAggregateType(typ) {
4151 4158
        let layout = resolver::getTypeLayout(typ);
4152 4159
4153 4160
        emit(self, il::Instr::Blit { dst, src, size: il::Val::Imm(layout.size as i64) });
4154 4161
    } else {
4155 4162
        emit(self, il::Instr::Store {
4156 -
            typ: ilType(self.low, typ),
4163 +
            typ: ilType(&mut *self.low, typ),
4157 4164
            src,
4158 4165
            dst: base,
4159 4166
            offset,
4160 4167
        });
4161 4168
    }
4162 4169
}
4163 4170
4164 4171
/// Allocate stack space for a value and store it. Returns a pointer to the value.
4165 -
fn emitStackVal(self: *mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) {
4172 +
unsafe fn emitStackVal(self: &mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) {
4166 4173
    let ptr = try emitReserve(self, typ);
4167 4174
    try emitStore(self, ptr, 0, typ, val);
4168 4175
    return il::Val::Reg(ptr);
4169 4176
}
4170 4177
4171 4178
/// Generic helper to build any tagged aggregate.
4172 4179
/// Reserves space based on the provided layout, stores the tag, and optionally
4173 4180
/// stores the payload value at `valOffset`.
4174 -
fn buildTagged(
4175 -
    self: *mut FnLowerer,
4181 +
unsafe fn buildTagged(
4182 +
    self: &mut FnLowerer,
4176 4183
    layout: resolver::Layout,
4177 4184
    tag: i64,
4178 4185
    payload: ?il::Val,
4179 4186
    payloadType: resolver::Type,
4180 4187
    tagSize: u32,
4203 4210
/// Wrap a value in an optional type.
4204 4211
///
4205 4212
/// For optional pointers (`?*T`), the value is returned as-is since pointers
4206 4213
/// use zero to represent `nil`. For other optionals, builds a tagged aggregate.
4207 4214
/// with the tag set to `1`, and the value as payload.
4208 -
fn wrapInOptional(self: *mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) {
4215 +
unsafe fn wrapInOptional(self: &mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) {
4209 4216
    let case resolver::Type::Optional(inner) = optType else {
4210 4217
        throw LowerError::ExpectedOptional;
4211 4218
    };
4212 4219
    // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null.
4213 4220
    if resolver::isNullableType(*inner) {
4221 4228
4222 4229
/// Build a `nil` value for an optional type.
4223 4230
///
4224 4231
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4225 4232
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4226 -
fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4233 +
unsafe fn buildNilOptional(self: &mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4227 4234
    let case resolver::Type::Optional(inner) = optType
4228 4235
        else throw LowerError::ExpectedOptional;
4229 4236
    if let case resolver::Type::Pointer { .. } = *inner {
4230 4237
        return il::Val::Imm(0);
4231 4238
    }
4237 4244
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4238 4245
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4239 4246
}
4240 4247
4241 4248
/// Build a result value for throwing functions.
4242 -
fn buildResult(
4243 -
    self: *mut FnLowerer,
4249 +
unsafe fn buildResult(
4250 +
    self: &mut FnLowerer,
4244 4251
    tag: i64,
4245 4252
    payload: ?il::Val,
4246 4253
    payloadType: resolver::Type
4247 4254
) -> il::Val throws (LowerError) {
4248 4255
    let successType = *self.fnType.returnType;
4251 4258
    );
4252 4259
    return try buildTagged(self, layout, tag, payload, payloadType, resolver::PTR_SIZE as i32, RESULT_VAL_OFFSET);
4253 4260
}
4254 4261
4255 4262
/// Build a slice aggregate from a data pointer, length and capacity.
4256 -
fn buildSliceValue(
4257 -
    self: *mut FnLowerer,
4263 +
unsafe fn buildSliceValue(
4264 +
    self: &mut FnLowerer,
4258 4265
    elemTy: *resolver::Type,
4259 4266
    mutable: bool,
4260 4267
    ptrVal: il::Val,
4261 4268
    lenVal: il::Val,
4262 4269
    capVal: il::Val
4279 4286
4280 4287
    return il::Val::Reg(dst);
4281 4288
}
4282 4289
4283 4290
/// Build a trait object fat pointer from a data pointer and a v-table.
4284 -
fn buildTraitObject(
4285 -
    self: *mut FnLowerer,
4291 +
unsafe fn buildTraitObject(
4292 +
    self: &mut FnLowerer,
4286 4293
    dataVal: il::Val,
4287 4294
    traitInfo: *resolver::TraitType,
4288 -
    inst: *resolver::InstanceEntry
4295 +
    inst: &resolver::InstanceEntry
4289 4296
) -> il::Val throws (LowerError) {
4290 -
    let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name);
4297 +
    let vName = vtableName(&mut *self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name);
4291 4298
4292 4299
    // Reserve space for the trait object on the stack.
4293 4300
    let slot = emitReserveLayout(self, resolver::Layout {
4294 4301
        size: resolver::PTR_SIZE * 2,
4295 4302
        alignment: resolver::PTR_SIZE,
4312 4319
    });
4313 4320
    return il::Val::Reg(slot);
4314 4321
}
4315 4322
4316 4323
/// Compute a field pointer by adding a byte offset to a base address.
4317 -
fn emitPtrOffset(self: *mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg {
4324 +
unsafe fn emitPtrOffset(self: &mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg {
4318 4325
    if offset == 0 {
4319 4326
        return base;
4320 4327
    }
4321 4328
    let dst = nextReg(self);
4322 4329
4330 4337
    return dst;
4331 4338
}
4332 4339
4333 4340
/// Emit an element address computation for array/slice indexing.
4334 4341
/// Computes: `base + idx * stride`.
4335 -
fn emitElem(self: *mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg {
4342 +
unsafe fn emitElem(self: &mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg {
4336 4343
    // If index is zero, return base directly.
4337 4344
    if idx == il::Val::Imm(0) {
4338 4345
        return base;
4339 4346
    }
4340 4347
    // If stride is `1`, skip the multiply.
4371 4378
    });
4372 4379
    return dst;
4373 4380
}
4374 4381
4375 4382
/// Emit a typed binary operation, returning the result as a value.
4376 -
fn emitTypedBinOp(self: *mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val {
4383 +
unsafe fn emitTypedBinOp(self: &mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val {
4377 4384
    let dst = nextReg(self);
4378 4385
    emit(self, il::Instr::BinOp { op, typ, dst, a, b });
4379 4386
    return il::Val::Reg(dst);
4380 4387
}
4381 4388
4382 4389
/// Emit a tag comparison for void variant equality/inequality.
4383 -
fn emitTagCmp(self: *mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val
4390 +
unsafe fn emitTagCmp(self: &mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val
4384 4391
    throws (LowerError)
4385 4392
{
4386 4393
    let reg = emitValToReg(self, val);
4387 4394
4388 4395
    // For all-void unions, the value *is* the tag, not a pointer.
4395 4402
    let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne;
4396 4403
    return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx));
4397 4404
}
4398 4405
4399 4406
/// Logical "and" between two values. Returns the result in a register.
4400 -
fn emitLogicalAnd(self: *mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val {
4407 +
unsafe fn emitLogicalAnd(self: &mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val {
4401 4408
    let prev = left else {
4402 4409
        return right;
4403 4410
    };
4404 4411
    return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, prev, right);
4405 4412
}
4407 4414
//////////////////////////
4408 4415
// Aggregate Comparison //
4409 4416
//////////////////////////
4410 4417
4411 4418
/// Emit an equality test for values at an offset of the given base registers.
4412 -
fn emitEqAtOffset(
4413 -
    self: *mut FnLowerer,
4419 +
unsafe fn emitEqAtOffset(
4420 +
    self: &mut FnLowerer,
4414 4421
    left: il::Reg,
4415 4422
    right: il::Reg,
4416 4423
    offset: i32,
4417 4424
    fieldType: resolver::Type
4418 4425
) -> il::Val throws (LowerError) {
4422 4429
    }
4423 4430
    // For scalar types, load and compare directly.
4424 4431
    let a = emitLoad(self, left, offset, fieldType);
4425 4432
    let b = emitLoad(self, right, offset, fieldType);
4426 4433
    let dst = nextReg(self);
4427 -
    emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(self.low, fieldType), dst, a, b });
4434 +
    emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(&mut *self.low, fieldType), dst, a, b });
4428 4435
4429 4436
    return il::Val::Reg(dst);
4430 4437
}
4431 4438
4432 4439
/// Compare two record values for equality.
4433 -
fn lowerRecordEq(
4434 -
    self: *mut FnLowerer,
4440 +
unsafe fn lowerRecordEq(
4441 +
    self: &mut FnLowerer,
4435 4442
    recInfo: resolver::RecordType,
4436 4443
    a: il::Reg,
4437 4444
    b: il::Reg,
4438 4445
    offset: i32
4439 4446
) -> il::Val throws (LowerError) {
4449 4456
    }
4450 4457
    return il::Val::Imm(1);
4451 4458
}
4452 4459
4453 4460
/// Compare two slice values for equality.
4454 -
fn lowerSliceEq(
4455 -
    self: *mut FnLowerer,
4461 +
unsafe fn lowerSliceEq(
4462 +
    self: &mut FnLowerer,
4456 4463
    elemTy: *resolver::Type,
4457 4464
    mutable: bool,
4458 4465
    a: il::Reg,
4459 4466
    b: il::Reg,
4460 4467
    offset: i32
4479 4486
/// branchless formulation: `tagEq AND (tagNil OR payloadEq)`
4480 4487
///
4481 4488
/// For inner types that may contain uninitialized data when `nil` (unions,
4482 4489
/// nested optionals), the payload comparison is guarded behind a branch
4483 4490
/// so that `nil` payloads are never inspected.
4484 -
fn lowerOptionalEq(
4485 -
    self: *mut FnLowerer,
4491 +
unsafe fn lowerOptionalEq(
4492 +
    self: &mut FnLowerer,
4486 4493
    inner: resolver::Type,
4487 4494
    a: il::Reg,
4488 4495
    b: il::Reg,
4489 4496
    offset: i32
4490 4497
) -> il::Val throws (LowerError) {
4571 4578
/// For all-void unions, we skip the control flow entirely and just compare
4572 4579
/// the tags directly.
4573 4580
///
4574 4581
/// TODO: Could be optimized to branchless when all non-void variants share
4575 4582
/// the same payload type: `tagEq AND (isVoidVariant OR payloadEq)`.
4576 -
fn lowerUnionEq(
4577 -
    self: *mut FnLowerer,
4583 +
unsafe fn lowerUnionEq(
4584 +
    self: &mut FnLowerer,
4578 4585
    unionInfo: resolver::UnionType,
4579 4586
    a: il::Reg,
4580 4587
    b: il::Reg,
4581 4588
    offset: i32
4582 4589
) -> il::Val throws (LowerError) {
4615 4622
4616 4623
    // Create comparison blocks for each non-void variant and build switch cases.
4617 4624
    // Void variants jump directly to merge with `true`.
4618 4625
    let trueArgs = try allocVal(self, il::Val::Imm(1));
4619 4626
    let cases = try! alloc::allocSlice(
4620 -
        self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4627 +
        &mut *self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4621 4628
    ) as *mut [il::SwitchCase];
4622 4629
4623 4630
    let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined;
4624 4631
    for variant, i in unionInfo.variants {
4625 4632
        if variant.valueType == resolver::Type::Void {
4678 4685
    try switchToAndSeal(self, mergeBlock);
4679 4686
    return il::Val::Reg(resultReg);
4680 4687
}
4681 4688
4682 4689
/// Compare two array values for equality, element by element.
4683 -
fn lowerArrayEq(
4684 -
    self: *mut FnLowerer,
4690 +
unsafe fn lowerArrayEq(
4691 +
    self: &mut FnLowerer,
4685 4692
    arr: resolver::ArrayType,
4686 4693
    a: il::Reg,
4687 4694
    b: il::Reg,
4688 4695
    offset: i32
4689 4696
) -> il::Val throws (LowerError) {
4702 4709
    // Empty arrays are always equal.
4703 4710
    return il::Val::Imm(1);
4704 4711
}
4705 4712
4706 4713
/// Compare two aggregate values for equality.
4707 -
fn lowerAggregateEq(
4708 -
    self: *mut FnLowerer,
4714 +
unsafe fn lowerAggregateEq(
4715 +
    self: &mut FnLowerer,
4709 4716
    typ: resolver::Type,
4710 4717
    a: il::Reg,
4711 4718
    b: il::Reg,
4712 4719
    offset: i32
4713 4720
) -> il::Val throws (LowerError) {
4736 4743
    }
4737 4744
}
4738 4745
4739 4746
/// Lower a record literal expression. Handles both plain records and union variant
4740 4747
/// record literals like `Union::Variant { field: value }`.
4741 -
fn lowerRecordLit(self: *mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) {
4748 +
unsafe fn lowerRecordLit(self: &mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) {
4742 4749
    let typ = try typeOf(self, node);
4743 4750
    match typ {
4744 4751
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
4745 4752
            let dst = try emitReserve(self, typ);
4746 4753
            try lowerRecordFields(self, dst, &recInfo, lit.fields, 0);
4767 4774
            emitStoreW8At(self, il::Val::Imm(index as i64), dst, TVAL_TAG_OFFSET);
4768 4775
            try lowerRecordFields(self, dst, &recInfo, lit.fields, valOffset);
4769 4776
4770 4777
            return il::Val::Reg(dst);
4771 4778
        }
4772 -
        else => throw LowerError::UnexpectedType(&typ),
4779 +
        else => throw LowerError::UnexpectedType(typ),
4773 4780
    }
4774 4781
}
4775 4782
4776 4783
/// Lower fields of a record literal into a destination register.
4777 4784
/// The `offset` is added to each field's offset when storing.
4778 -
fn lowerRecordFields(
4779 -
    self: *mut FnLowerer,
4785 +
unsafe fn lowerRecordFields(
4786 +
    self: &mut FnLowerer,
4780 4787
    dst: il::Reg,
4781 -
    recInfo: *resolver::RecordType,
4788 +
    recInfo: &resolver::RecordType,
4782 4789
    fields: *mut [*ast::Node],
4783 4790
    offset: i32
4784 4791
) throws (LowerError) {
4785 4792
    for fieldNode, i in fields {
4786 4793
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
4787 4794
            throw LowerError::UnexpectedNodeValue(fieldNode);
4788 4795
        };
4789 4796
        let mut fieldIdx: u32 = i;
4790 4797
        if recInfo.labeled {
4791 -
            let idx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) else {
4798 +
            let idx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) else {
4792 4799
                throw LowerError::MissingMetadata;
4793 4800
            };
4794 4801
            set fieldIdx = idx;
4795 4802
        }
4796 4803
        // Skip `undefined` fields, they need no initialization.
4803 4810
        }
4804 4811
    }
4805 4812
}
4806 4813
4807 4814
/// Lower an unlabeled record constructor call.
4808 -
fn lowerRecordCtor(self: *mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
4815 +
unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
4809 4816
    let case resolver::NominalType::Record(recInfo) = *nominal else {
4810 4817
        throw LowerError::ExpectedRecord;
4811 4818
    };
4812 4819
    let typ = resolver::Type::Nominal(nominal);
4813 4820
    let dst = try emitReserve(self, typ);
4822 4829
    }
4823 4830
    return il::Val::Reg(dst);
4824 4831
}
4825 4832
4826 4833
/// Lower an array literal expression like `[1, 2, 3]`.
4827 -
fn lowerArrayLit(self: *mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val
4834 +
unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val
4828 4835
    throws (LowerError)
4829 4836
{
4830 4837
    let typ = try typeOf(self, node);
4831 4838
    let case resolver::Type::Array(arrInfo) = typ else {
4832 4839
        throw LowerError::ExpectedArray;
4845 4852
}
4846 4853
4847 4854
/// Lower an array repeat literal expression like `[42; 3]`.
4848 4855
/// Unrolls the initialization at compile time.
4849 4856
// TODO: Beyond a certain length, lower this to a loop.
4850 -
fn lowerArrayRepeatLit(self: *mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val
4857 +
unsafe fn lowerArrayRepeatLit(self: &mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val
4851 4858
    throws (LowerError)
4852 4859
{
4853 4860
    let typ = try typeOf(self, node);
4854 4861
    let case resolver::Type::Array(arrInfo) = typ else {
4855 4862
        throw LowerError::ExpectedArray;
4869 4876
    }
4870 4877
    return il::Val::Reg(dst);
4871 4878
}
4872 4879
4873 4880
/// Lower a union constructor call like `Union::Variant(...)`.
4874 -
fn lowerUnionCtor(self: *mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val
4881 +
unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val
4875 4882
    throws (LowerError)
4876 4883
{
4877 4884
    let unionTy = try typeOf(self, node);
4878 4885
    let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else {
4879 4886
        throw LowerError::ExpectedVariant;
4891 4898
    }
4892 4899
    return try buildTagged(self, resolver::getTypeLayout(unionTy), index as i64, payloadVal, payloadType, 1, valOffset);
4893 4900
}
4894 4901
4895 4902
/// Lower a field access into a pointer to the field.
4896 -
fn lowerFieldRef(self: *mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) {
4903 +
unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) {
4897 4904
    let parentTy = try typeOf(self, access.parent);
4898 4905
    let subjectTy = resolver::autoDeref(parentTy);
4899 -
    let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else {
4906 +
    let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, access.child) else {
4900 4907
        throw LowerError::MissingMetadata;
4901 4908
    };
4902 4909
    let fieldInfo = resolver::getRecordField(subjectTy, fieldIdx) else {
4903 4910
        throw LowerError::FieldNotFound;
4904 4911
    };
4911 4918
        fieldType: fieldInfo.fieldType,
4912 4919
    };
4913 4920
}
4914 4921
4915 4922
/// Lower a field access expression.
4916 -
fn lowerFieldAccess(self: *mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) {
4923 +
unsafe fn lowerFieldAccess(self: &mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) {
4917 4924
    let fieldRef = try lowerFieldRef(self, access);
4918 4925
    return emitRead(self, fieldRef.base, fieldRef.offset, fieldRef.fieldType);
4919 4926
}
4920 4927
4921 4928
/// Compute data pointer and element count for a range into a container.
4922 4929
/// Used by both slice range expressions (`&a[start..end]`) and slice
4923 4930
/// assignments (`a[start..end] = value`).
4924 -
fn resolveSliceRangePtr(
4925 -
    self: *mut FnLowerer,
4931 +
unsafe fn resolveSliceRangePtr(
4932 +
    self: &mut FnLowerer,
4926 4933
    container: *ast::Node,
4927 4934
    range: ast::Range,
4928 4935
    info: resolver::SliceRangeInfo
4929 4936
) -> SliceRangeResult throws (LowerError) {
4930 4937
    let baseVal = try lowerExpr(self, container);
4985 4992
    }
4986 4993
    return SliceRangeResult { dataReg, count };
4987 4994
}
4988 4995
4989 4996
/// Lower a slice range expression into a slice header value.
4990 -
fn lowerSliceRange(
4991 -
    self: *mut FnLowerer,
4997 +
unsafe fn lowerSliceRange(
4998 +
    self: &mut FnLowerer,
4992 4999
    container: *ast::Node,
4993 5000
    range: ast::Range,
4994 5001
    sliceNode: *ast::Node
4995 5002
) -> il::Val throws (LowerError) {
4996 -
    let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else {
5003 +
    let info = resolver::sliceRangeInfoFor(&*self.low.resolver, sliceNode) else {
4997 5004
        throw LowerError::MissingMetadata;
4998 5005
    };
4999 5006
    let r = try resolveSliceRangePtr(self, container, range, info);
5000 5007
    return try buildSliceValue(
5001 5008
        self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count
5002 5009
    );
5003 5010
}
5004 5011
5005 5012
/// Lower an address-of (`&x`) expression.
5006 -
fn lowerAddressOf(self: *mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) {
5013 +
unsafe fn lowerAddressOf(self: &mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) {
5007 5014
    // Handle subscript: `&ary[i]` or `&ary[start..end]`.
5008 5015
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
5009 5016
        if let case ast::NodeValue::Range(range) = index.value {
5010 5017
            return try lowerSliceRange(self, container, range, node);
5011 5018
        }
5035 5042
                // Already address-taken; return existing stack pointer.
5036 5043
                return val;
5037 5044
            }
5038 5045
            // Materialize a stack slot using the declaration's resolved
5039 5046
            // layout so `align(N)` on locals is honored.
5040 -
            let layout = resolver::getLayout(self.low.resolver, addr.target, typ);
5047 +
            let layout = resolver::getLayout(&*self.low.resolver, addr.target, typ);
5041 5048
            let slot = emitReserveLayout(self, layout);
5042 5049
            try emitStore(self, slot, 0, typ, val);
5043 5050
            let stackVal = il::Val::Reg(slot);
5044 5051
5045 5052
            set self.vars[*v].addressTaken = true;
5046 5053
            defVar(self, v, stackVal);
5047 5054
5048 5055
            return stackVal;
5049 5056
        }
5050 5057
        // Fall back to symbol lookup for constants/statics.
5051 -
        if let sym = resolver::nodeData(self.low.resolver, addr.target).sym {
5058 +
        if let sym = resolver::nodeData(&*self.low.resolver, addr.target).sym {
5052 5059
            return il::Val::Reg(emitDataAddr(self, sym));
5053 5060
        } else {
5054 5061
            throw LowerError::MissingSymbol(node);
5055 5062
        }
5056 5063
    }
5069 5076
    }
5070 5077
    throw LowerError::UnexpectedNodeValue(addr.target);
5071 5078
}
5072 5079
5073 5080
/// Lower an addressed array literal as a slice.
5074 -
fn lowerArrayLiteralSlice(
5075 -
    self: *mut FnLowerer,
5081 +
unsafe fn lowerArrayLiteralSlice(
5082 +
    self: &mut FnLowerer,
5076 5083
    sliceNode: *ast::Node,
5077 5084
    arrayNode: *ast::Node
5078 5085
) -> il::Val throws (LowerError) {
5079 5086
    let sliceTy = try typeOf(self, sliceNode);
5080 5087
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
5081 -
        throw LowerError::UnexpectedType(&sliceTy);
5088 +
        throw LowerError::UnexpectedType(sliceTy);
5082 5089
    };
5083 5090
    let arrayTy = try typeOf(self, arrayNode);
5084 5091
    let case resolver::Type::Array(arrayInfo) = arrayTy else {
5085 5092
        throw LowerError::ExpectedArray;
5086 5093
    };
5088 5095
    if length == 0 {
5089 5096
        return try buildSliceValue(
5090 5097
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
5091 5098
        );
5092 5099
    }
5093 -
    if resolver::isConstExpr(self.low.resolver, arrayNode) {
5100 +
    if resolver::isConstExpr(&*self.low.resolver, arrayNode) {
5101 +
        let fnName = self.fnName;
5094 5102
        let mut b = dataBuilder(self.low.allocator);
5095 5103
        match arrayNode.value {
5096 5104
            case ast::NodeValue::ArrayLit(elements) =>
5097 -
                try lowerConstArrayLitInto(self.low, elements, arrayTy, self.fnName, &mut b),
5105 +
                try lowerConstArrayLitInto(&mut *self.low, elements, arrayTy, fnName, &mut b),
5098 5106
            case ast::NodeValue::ArrayRepeatLit(repeat) =>
5099 -
                try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, self.fnName, &mut b),
5107 +
                try lowerConstArrayRepeatInto(&mut *self.low, repeat, arrayTy, fnName, &mut b),
5100 5108
            else => throw LowerError::UnexpectedNodeValue(arrayNode),
5101 5109
        }
5102 5110
        let result = dataBuilderFinish(&b);
5103 5111
        let alignment = resolver::getTypeLayout(*item).alignment;
5104 5112
        return try lowerConstDataAsSlice(
5113 5121
5114 5122
/// Lower the common element pointer computation for subscript operations.
5115 5123
/// Handles both arrays and slices by resolving the container type, extracting
5116 5124
/// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute
5117 5125
/// the element address.
5118 -
fn lowerElemPtr(
5119 -
    self: *mut FnLowerer, container: *ast::Node, index: *ast::Node
5126 +
unsafe fn lowerElemPtr(
5127 +
    self: &mut FnLowerer, container: *ast::Node, index: *ast::Node
5120 5128
) -> ElemPtrResult throws (LowerError) {
5121 5129
    let containerTy = try typeOf(self, container);
5122 5130
    let subjectTy = resolver::autoDeref(containerTy);
5123 5131
    let baseVal = try lowerExpr(self, container);
5124 5132
    let indexVal = try lowerExpr(self, index);
5139 5147
        case resolver::Type::Array(arrInfo) => {
5140 5148
            set elemType = *arrInfo.item;
5141 5149
            // Runtime safety check: index must be strictly less than array length.
5142 5150
            // Skip when the index is a compile-time constant, since we check
5143 5151
            // that in the resolver.
5144 -
            if not resolver::isConstExpr(self.low.resolver, index) {
5152 +
            if not resolver::isConstExpr(&*self.low.resolver, index) {
5145 5153
                let arrLen = il::Val::Imm(arrInfo.length as i64);
5146 5154
                try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen);
5147 5155
            }
5148 5156
        }
5149 5157
        else => throw LowerError::ExpectedSliceOrArray,
5155 5163
}
5156 5164
5157 5165
/// Lower a dereference expression.
5158 5166
/// Handles both pointer deref (`*ptr`) and record deref (`*r` on single-field
5159 5167
/// unlabeled record). Both read at offset 0 using the resolver-assigned type.
5160 -
fn lowerDeref(self: *mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) {
5168 +
unsafe fn lowerDeref(self: &mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) {
5161 5169
    let type = try typeOf(self, node);
5162 5170
    let ptrVal = try lowerExpr(self, target);
5163 5171
    let ptrReg = emitValToReg(self, ptrVal);
5164 5172
5165 5173
    return emitRead(self, ptrReg, 0, type);
5166 5174
}
5167 5175
5168 5176
/// Lower a subscript expression.
5169 -
fn lowerSubscript(self: *mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val
5177 +
unsafe fn lowerSubscript(self: &mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val
5170 5178
    throws (LowerError)
5171 5179
{
5172 5180
    if let case ast::NodeValue::Range(_) = index.value {
5173 5181
        panic "lowerSubscript: range subscript must use address-of (&)";
5174 5182
    }
5176 5184
5177 5185
    return emitRead(self, result.elemReg, 0, result.elemType);
5178 5186
}
5179 5187
5180 5188
/// Lower a let binding.
5181 -
fn lowerLet(self: *mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) {
5189 +
unsafe fn lowerLet(self: &mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) {
5182 5190
    // Evaluate value.
5183 5191
    let val = try lowerExpr(self, l.value);
5184 5192
    // Handle placeholder pattern: `let _ = expr;`
5185 5193
    if let case ast::NodeValue::Placeholder = l.ident.value {
5186 5194
        return;
5187 5195
    }
5188 5196
    let case ast::NodeValue::Ident(name) = l.ident.value else {
5189 5197
        throw LowerError::ExpectedIdentifier;
5190 5198
    };
5191 5199
    let typ = try typeOf(self, l.value);
5192 -
    let ilType = ilType(self.low, typ);
5200 +
    let ilType = ilType(&mut *self.low, typ);
5193 5201
    let mut varVal = val;
5194 5202
5195 5203
    // Aggregates with persistent storage need a local copy to avoid aliasing.
5196 5204
    // Temporaries such as literals or call results can be adopted directly.
5197 5205
    // This is because aggregates are represented as memory addresses
5201 5209
    // Void variant literals (e.g. `Option::None`) use scope access syntax and
5202 5210
    // are flagged as place expressions, but they are freshly constructed
5203 5211
    // temporaries with no persistent storage.
5204 5212
    if isAggregateType(typ) and
5205 5213
        ast::isPlaceExpr(l.value) and
5206 -
        voidVariantIndex(self.low.resolver, l.value) == nil {
5214 +
        voidVariantIndex(&*self.low.resolver, l.value) == nil {
5207 5215
        set varVal = try emitStackVal(self, typ, val);
5208 5216
    }
5209 5217
5210 5218
    // If the resolver determined that this variable's address is taken
5211 5219
    // anywhere in the function, allocate a stack slot immediately so the
5212 5220
    // SSA value is always a pointer. This avoids mixing integer and pointer
5213 5221
    // values in loop phis when `&var` or `&mut var` appears inside a loop.
5214 5222
    if not isAggregateType(typ) {
5215 -
        if let sym = resolver::nodeData(self.low.resolver, node).sym {
5223 +
        if let sym = resolver::nodeData(&*self.low.resolver, node).sym {
5216 5224
            if let case resolver::SymbolData::Value { addressTaken, .. } = sym.data; addressTaken {
5217 -
                let layout = resolver::getLayout(self.low.resolver, node, typ);
5225 +
                let layout = resolver::getLayout(&*self.low.resolver, node, typ);
5218 5226
                let slot = emitReserveLayout(self, layout);
5219 5227
                try emitStore(self, slot, 0, typ, varVal);
5220 5228
5221 5229
                let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot));
5222 5230
                set self.vars[*v].addressTaken = true;
5240 5248
///
5241 5249
///     @entry -> (true)  @then ---> @end <--.
5242 5250
///         |                                 )
5243 5251
///         `---- (false) -------------------'
5244 5252
///
5245 -
fn lowerIf(self: *mut FnLowerer, i: ast::If) throws (LowerError) {
5253 +
unsafe fn lowerIf(self: &mut FnLowerer, i: ast::If) throws (LowerError) {
5246 5254
    let thenBlock = try createBlock(self, "then");
5247 5255
5248 5256
    if let elseNode = i.elseBranch { // If-else case.
5249 5257
        let elseBlock = try createBlock(self, "else");
5250 5258
        try emitCondBranch(self, i.condition, thenBlock, elseBlock);
5304 5312
}
5305 5313
5306 5314
/// Lower an assignment target that designates a memory location, and return
5307 5315
/// the address to store through. Returns `nil` without emitting anything for
5308 5316
/// targets that aren't memory-backed, such as locals tracked in SSA.
5309 -
fn lowerPlace(self: *mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) {
5317 +
unsafe fn lowerPlace(self: &mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) {
5310 5318
    match target.value {
5311 5319
        case ast::NodeValue::FieldAccess(access) => {
5312 5320
            return try lowerFieldRef(self, access);
5313 5321
        }
5314 5322
        case ast::NodeValue::Deref(pointer) => {
5330 5338
/// whether it was handled. Nothing is emitted when it isn't.
5331 5339
///
5332 5340
/// Compound assignments share their target node with the left operand of the
5333 5341
/// desugared binary expression. Resolve that place once so side effects in a
5334 5342
/// dereference, field parent, or subscript index are not repeated for the store.
5335 -
fn lowerCompoundAssign(
5336 -
    self: *mut FnLowerer, expr: *ast::Node, binop: ast::BinOp
5343 +
unsafe fn lowerCompoundAssign(
5344 +
    self: &mut FnLowerer, expr: *ast::Node, binop: ast::BinOp
5337 5345
) -> bool throws (LowerError) {
5338 5346
    let place = try lowerPlace(self, binop.left) else return false;
5339 5347
    let current = emitRead(self, place.base, place.offset, place.fieldType);
5340 5348
    let left = try applyCoercion(self, binop.left, current);
5341 5349
    let right = try lowerExpr(self, binop.right);
5342 5350
    let exprType = try typeOf(self, expr);
5343 5351
    let result = emitScalarBinOp(
5344 -
        self, binop.op, ilType(self.low, exprType), left, right, isUnsignedType(exprType)
5352 +
        self, binop.op, ilType(&mut *self.low, exprType), left, right, isUnsignedType(exprType)
5345 5353
    );
5346 5354
    let assigned = try applyCoercion(self, expr, result);
5347 5355
    try emitStore(self, place.base, place.offset, place.fieldType, assigned);
5348 5356
5349 5357
    return true;
5350 5358
}
5351 5359
5352 5360
/// Lower an assignment statement.
5353 -
fn lowerAssign(self: *mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) {
5361 +
unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) {
5354 5362
    // Slice assignment: `slice[range] = value`.
5355 -
    if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) {
5363 +
    if let info = resolver::sliceRangeInfoFor(&*self.low.resolver, node) {
5356 5364
        let case ast::NodeValue::Subscript { container, index } = a.left.value
5357 5365
            else panic "lowerAssign: slice assign without subscript";
5358 5366
        let case ast::NodeValue::Range(range) = index.value
5359 5367
            else panic "lowerAssign: slice assign without range";
5360 5368
        try lowerSliceAssign(self, a.right, container, range, info);
5405 5413
        }
5406 5414
    }
5407 5415
}
5408 5416
5409 5417
/// Lower `slice[range] = value`.
5410 -
fn lowerSliceAssign(
5411 -
    self: *mut FnLowerer,
5418 +
unsafe fn lowerSliceAssign(
5419 +
    self: &mut FnLowerer,
5412 5420
    rhs: *ast::Node,
5413 5421
    container: *ast::Node,
5414 5422
    range: ast::Range,
5415 5423
    info: resolver::SliceRangeInfo
5416 5424
) throws (LowerError) {
5437 5445
        try emitFillLoop(self, r.dataReg, fillVal, r.count, *info.itemType, elemSize);
5438 5446
    }
5439 5447
}
5440 5448
5441 5449
/// Emit a typed fill loop: `for i in 0..count { dst[i * stride] = value; }`.
5442 -
fn emitFillLoop(
5443 -
    self: *mut FnLowerer,
5450 +
unsafe fn emitFillLoop(
5451 +
    self: &mut FnLowerer,
5444 5452
    dst: il::Reg,
5445 5453
    value: il::Val,
5446 5454
    count: il::Val,
5447 5455
    elemType: resolver::Type,
5448 5456
    elemSize: u32
5487 5495
///
5488 5496
///   @entry -> @loop -> @loop
5489 5497
///               |
5490 5498
///               `----> @end
5491 5499
///
5492 -
fn lowerLoop(self: *mut FnLowerer, body: *ast::Node) throws (LowerError) {
5500 +
unsafe fn lowerLoop(self: &mut FnLowerer, body: *ast::Node) throws (LowerError) {
5493 5501
    let loopBlock = try createBlock(self, "loop");
5494 5502
    let endBlock = try createBlock(self, "merge");
5495 5503
5496 5504
    // Enter the loop with the given break and continue targets.
5497 5505
    // `break` jumps to `endBlock`,
5520 5528
///
5521 5529
///   @entry -> @loop -> (true)  @body -> @loop
5522 5530
///               |
5523 5531
///               `----> (false) @end
5524 5532
///
5525 -
fn lowerWhile(self: *mut FnLowerer, w: ast::While) throws (LowerError) {
5533 +
unsafe fn lowerWhile(self: &mut FnLowerer, w: ast::While) throws (LowerError) {
5526 5534
    let whileBlock = try createBlock(self, "while");
5527 5535
    let bodyBlock = try createBlock(self, "body");
5528 5536
    let endBlock = try createBlock(self, "merge");
5529 5537
5530 5538
    enterLoop(self, endBlock, whileBlock);
5544 5552
    try switchToAndSeal(self, endBlock);
5545 5553
    exitLoop(self);
5546 5554
}
5547 5555
5548 5556
/// Emit an increment of a variable by `1`.
5549 -
fn emitIncrement(self: *mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) {
5557 +
unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) {
5550 5558
    let cur = try useVar(self, v);
5551 5559
    let next = nextReg(self);
5552 5560
5553 5561
    emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) });
5554 5562
    defVar(self, v, il::Val::Reg(next));
5559 5567
/// The step block is created lazily (after the loop body) so that it gets
5560 5568
/// a block index higher than all body blocks. This ensures the register
5561 5569
/// allocator processes definitions before uses in forward block order,
5562 5570
/// avoiding stale assignments when a value defined deep in the body flows
5563 5571
/// through the step block as a block argument.
5564 -
fn lowerForLoop(self: *mut FnLowerer, iter: *ForIter, body: *ast::Node) throws (LowerError) {
5572 +
unsafe fn lowerForLoop(self: &mut FnLowerer, iter: &ForIter, body: *ast::Node) throws (LowerError) {
5565 5573
    let loopBlock = try createBlock(self, "loop");
5566 5574
    let bodyBlock = try createBlock(self, "body");
5567 5575
    let endBlock = try createBlock(self, "merge");
5568 5576
5569 5577
    enterLoop(self, endBlock, nil);
5628 5636
5629 5637
    switchToBlock(self, endBlock);
5630 5638
}
5631 5639
5632 5640
/// Lower a `for` loop over a range, array, or slice.
5633 -
fn lowerFor(self: *mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) {
5641 +
unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) {
5634 5642
    let savedVarsLen = enterVarScope(self);
5635 -
    let info = resolver::forLoopInfoFor(self.low.resolver, node) else {
5643 +
    let info = resolver::forLoopInfoFor(&*self.low.resolver, node) else {
5636 5644
        throw LowerError::MissingMetadata;
5637 5645
    };
5638 5646
    match info {
5639 5647
        case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName } => {
5640 5648
            let endExpr = range.end else {
5643 5651
            let mut startVal = il::Val::Imm(0);
5644 5652
            if let start = range.start {
5645 5653
                set startVal = try lowerExpr(self, start);
5646 5654
            }
5647 5655
            let endVal = try lowerExpr(self, endExpr);
5648 -
            let iterType = ilType(self.low, *valType);
5656 +
            let iterType = ilType(&mut *self.low, *valType);
5649 5657
            let valVar = newVar(self, bindingName, iterType, false, startVal);
5650 5658
5651 5659
            let mut indexVar: ?Var = nil;
5652 5660
            if indexName <> nil { // Optional index always starts at zero.
5653 5661
                set indexVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0));
5678 5686
            let mut valVar: ?Var = nil;
5679 5687
            if bindingName <> nil {
5680 5688
                set valVar = newVar(
5681 5689
                    self,
5682 5690
                    bindingName,
5683 -
                    ilType(self.low, *elemType),
5691 +
                    ilType(&mut *self.low, *elemType),
5684 5692
                    false,
5685 5693
                    il::Val::Undef
5686 5694
                );
5687 5695
            }
5688 5696
            let iter = ForIter::Collection { valVar, idxVar, dataReg, lengthVal, elemType };
5692 5700
    }
5693 5701
    exitVarScope(self, savedVarsLen);
5694 5702
}
5695 5703
5696 5704
/// Lower a break statement.
5697 -
fn lowerBreak(self: *mut FnLowerer) throws (LowerError) {
5705 +
unsafe fn lowerBreak(self: &mut FnLowerer) throws (LowerError) {
5698 5706
    let ctx = currentLoop(self) else {
5699 5707
        throw LowerError::OutsideOfLoop;
5700 5708
    };
5701 5709
    try emitJmp(self, ctx.breakTarget);
5702 5710
}
5703 5711
5704 5712
/// Lower a continue statement.
5705 -
fn lowerContinue(self: *mut FnLowerer) throws (LowerError) {
5713 +
unsafe fn lowerContinue(self: &mut FnLowerer) throws (LowerError) {
5706 5714
    let block = try getOrCreateContinueBlock(self);
5707 5715
    try emitJmp(self, block);
5708 5716
}
5709 5717
5710 5718
/// Emit a return, blitting into the caller's return buffer if needed.
5711 5719
///
5712 5720
/// When the function has a return buffer parameter, the value is blitted
5713 5721
/// into the buffer and the buffer pointer is returned. Otherwise, the value is
5714 5722
/// returned directly.
5715 -
fn emitRetVal(self: *mut FnLowerer, val: il::Val) throws (LowerError) {
5723 +
unsafe fn emitRetVal(self: &mut FnLowerer, val: il::Val) throws (LowerError) {
5716 5724
    if let retReg = self.returnReg {
5717 5725
        let src = emitValToReg(self, val);
5718 5726
        let size = resolver::getResultLayout(*self.fnType.returnType, self.fnType.throwList).size
5719 5727
            if self.fnType.throwList.len > 0
5720 5728
            else resolver::getTypeLayout(*self.fnType.returnType).size;
5731 5739
        emit(self, il::Instr::Ret { val });
5732 5740
    }
5733 5741
}
5734 5742
5735 5743
/// Lower a return statement.
5736 -
fn lowerReturnStmt(self: *mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) {
5744 +
unsafe fn lowerReturnStmt(self: &mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) {
5737 5745
    let mut val = il::Val::Undef;
5738 5746
    if let expr = value {
5739 5747
        set val = try lowerExpr(self, expr);
5740 5748
    }
5741 5749
    set val = try applyCoercion(self, node, val);
5742 5750
    try emitRetVal(self, val);
5743 5751
}
5744 5752
5745 5753
/// Lower a throw statement.
5746 -
fn lowerThrowStmt(self: *mut FnLowerer, expr: *ast::Node) throws (LowerError) {
5754 +
unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) {
5747 5755
    assert self.fnType.throwList.len > 0;
5748 5756
5749 5757
    let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1
5750 5758
        else try typeOf(self, expr);
5751 -
    let tag = getOrAssignErrorTag(self.low, errType) as i64;
5759 +
    let tag = getOrAssignErrorTag(&mut *self.low, errType) as i64;
5752 5760
    let errVal = try lowerExpr(self, expr);
5753 5761
    let resultVal = try buildResult(self, tag, errVal, errType);
5754 5762
5755 5763
    try emitRetVal(self, resultVal);
5756 5764
}
5757 5765
5758 5766
/// Ensure a value is in a register (eg. for branch conditions).
5759 -
fn emitValToReg(self: *mut FnLowerer, val: il::Val) -> il::Reg {
5767 +
unsafe fn emitValToReg(self: &mut FnLowerer, val: il::Val) -> il::Reg {
5760 5768
    match val {
5761 5769
        case il::Val::Reg(r) => return r,
5762 5770
        case il::Val::Imm(_), il::Val::DataSym(_), il::Val::FnAddr(_) => {
5763 5771
            let dst = nextReg(self);
5764 5772
            emit(self, il::Instr::Copy { dst, val });
5806 5814
///       // ...
5807 5815
///       jmp @end(%b);
5808 5816
///     @end(w8 %result)
5809 5817
///       ret %result;
5810 5818
///
5811 -
fn lowerLogicalOp(
5812 -
    self: *mut FnLowerer,
5819 +
unsafe fn lowerLogicalOp(
5820 +
    self: &mut FnLowerer,
5813 5821
    binop: ast::BinOp,
5814 5822
    thenLabel: *[u8],
5815 5823
    elseLabel: *[u8],
5816 5824
    mergeLabel: *[u8],
5817 5825
    op: LogicalOp
5856 5864
    try switchToAndSeal(self, mergeBlock);
5857 5865
    return il::Val::Reg(resultReg);
5858 5866
}
5859 5867
5860 5868
/// Lower a conditional expression (`thenExpr if condition else elseExpr`).
5861 -
fn lowerCondExpr(self: *mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val
5869 +
unsafe fn lowerCondExpr(self: &mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val
5862 5870
    throws (LowerError)
5863 5871
{
5864 5872
    let typ = try typeOf(self, node);
5865 5873
    let thenBlock = try createBlock(self, "cond#then");
5866 5874
    let elseBlock = try createBlock(self, "cond#else");
5887 5895
5888 5896
        return il::Val::Reg(dst);
5889 5897
    } else {
5890 5898
        try emitCondBranch(self, cond.condition, thenBlock, elseBlock);
5891 5899
5892 -
        let resultType = ilType(self.low, typ);
5900 +
        let resultType = ilType(&mut *self.low, typ);
5893 5901
        let resultReg = nextReg(self);
5894 5902
        let mergeBlock = try createBlockWithParam(
5895 5903
            self, "cond#merge", il::Param { value: resultReg, type: resultType }
5896 5904
        );
5897 5905
        try switchToAndSeal(self, thenBlock);
5923 5931
        else => return nil,
5924 5932
    }
5925 5933
}
5926 5934
5927 5935
/// Lower a binary operation.
5928 -
fn lowerBinOp(self: *mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) {
5936 +
unsafe fn lowerBinOp(self: &mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) {
5929 5937
    // Short-circuit logical operators don't evaluate both operands eagerly.
5930 5938
    if binop.op == ast::BinaryOp::And {
5931 5939
        return try lowerLogicalOp(self, binop, "and#then", "and#else", "and#end", LogicalOp::And);
5932 5940
    } else if binop.op == ast::BinaryOp::Or {
5933 5941
        return try lowerLogicalOp(self, binop, "or#then", "or#else", "or#end", LogicalOp::Or);
5958 5966
5959 5967
    if isComparison {
5960 5968
        let leftTy = try effectiveType(self, binop.left);
5961 5969
        let rightTy = try effectiveType(self, binop.right);
5962 5970
        // Optimize: comparing with a void variant just needs tag comparison.
5963 -
        if let idx = voidVariantIndex(self.low.resolver, binop.left) {
5971 +
        if let idx = voidVariantIndex(&*self.low.resolver, binop.left) {
5964 5972
            return try emitTagCmp(self, binop.op, b, idx, rightTy);
5965 -
        } else if let idx = voidVariantIndex(self.low.resolver, binop.right) {
5973 +
        } else if let idx = voidVariantIndex(&*self.low.resolver, binop.right) {
5966 5974
            return try emitTagCmp(self, binop.op, a, idx, leftTy);
5967 5975
        }
5968 5976
        // Aggregate types require element-wise comparison.
5969 5977
        // When comparing `?T` with `T`, wrap the scalar side.
5970 5978
        if isAggregateType(leftTy) {
5978 5986
            let lhs = try wrapInOptional(self, a, rightTy);
5979 5987
            return try emitAggregateEqOp(self, binop.op, rightTy, lhs, b);
5980 5988
        }
5981 5989
        set resultTy = scalarComparisonType(leftTy, rightTy);
5982 5990
    }
5983 -
    return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy));
5991 +
    return emitScalarBinOp(self, binop.op, ilType(&mut *self.low, resultTy), a, b, isUnsignedType(resultTy));
5984 5992
}
5985 5993
5986 5994
/// Emit an aggregate equality or inequality comparison.
5987 -
fn emitAggregateEqOp(
5988 -
    self: *mut FnLowerer,
5995 +
unsafe fn emitAggregateEqOp(
5996 +
    self: &mut FnLowerer,
5989 5997
    op: ast::BinaryOp,
5990 5998
    typ: resolver::Type,
5991 5999
    a: il::Val,
5992 6000
    b: il::Val
5993 6001
) -> il::Val throws (LowerError) {
6000 6008
    }
6001 6009
    return result;
6002 6010
}
6003 6011
6004 6012
/// Emit a scalar binary operation instruction.
6005 -
fn emitScalarBinOp(
6006 -
    self: *mut FnLowerer,
6013 +
unsafe fn emitScalarBinOp(
6014 +
    self: &mut FnLowerer,
6007 6015
    op: ast::BinaryOp,
6008 6016
    typ: il::Type,
6009 6017
    a: il::Val,
6010 6018
    b: il::Val,
6011 6019
    unsigned: bool
6077 6085
    }
6078 6086
    return il::Val::Reg(dst);
6079 6087
}
6080 6088
6081 6089
/// Normalize sub-word values to well-defined high bits.
6082 -
fn normalizeSubword(self: *mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val {
6090 +
unsafe fn normalizeSubword(self: &mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val {
6083 6091
    if typ == il::Type::W8 or typ == il::Type::W16 {
6084 6092
        let extDst: il::Reg = nextReg(self);
6085 6093
        if unsigned {
6086 6094
            emit(self, il::Instr::Zext { typ, dst: extDst, val });
6087 6095
        } else {
6091 6099
    }
6092 6100
    return val;
6093 6101
}
6094 6102
6095 6103
/// Lower a unary operation.
6096 -
fn lowerUnOp(self: *mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) {
6104 +
unsafe fn lowerUnOp(self: &mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) {
6097 6105
    if unop.op == ast::UnaryOp::Neg {
6098 6106
        if let case ast::NodeValue::Number(lit) = unop.value.value {
6099 6107
            return il::Val::Imm((0 - lit.magnitude) as i64);
6100 6108
        }
6101 6109
    }
6102 6110
    let val = try lowerExpr(self, unop.value);
6103 6111
    let t = try typeOf(self, node);
6104 -
    let typ = ilType(self.low, t);
6112 +
    let typ = ilType(&mut *self.low, t);
6105 6113
    let dst = nextReg(self);
6106 6114
    let mut needsExt: bool = false;
6107 6115
6108 6116
    match unop.op {
6109 6117
        case ast::UnaryOp::Not => {
6123 6131
    }
6124 6132
    return il::Val::Reg(dst);
6125 6133
}
6126 6134
6127 6135
/// Lower a cast expression (`x as T`).
6128 -
fn lowerCast(self: *mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) {
6136 +
unsafe fn lowerCast(self: &mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) {
6129 6137
    let val = try lowerExpr(self, cast.value);
6130 6138
6131 6139
    let srcType = try typeOf(self, cast.value);
6132 6140
    let dstType = try typeOf(self, node);
6133 6141
    if resolver::typesEqual(srcType, dstType) {
6155 6163
6156 6164
/// Lower a string literal to a slice value.
6157 6165
///
6158 6166
/// String literals are stored as global data and the result is a slice
6159 6167
/// pointing to the data with the appropriate length.
6160 -
fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) {
6168 +
unsafe fn lowerStringLit(self: &mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) {
6161 6169
    // Get the slice type from the node.
6162 6170
    let sliceTy = try typeOf(self, node);
6163 6171
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6164 6172
        else throw LowerError::ExpectedSliceOrArray;
6165 6173
    // Build the string data value.
6166 6174
    let ptr = try! alloc::alloc(
6167 -
        self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6175 +
        &mut *self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6168 6176
    ) as *mut il::DataValue;
6169 6177
6170 6178
    set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 };
6171 6179
    let result = ConstDataResult { values: @sliceOf(ptr, 1), zeroInit: false };
6172 6180
6174 6182
        self, &result, 1, true, item, mutable, s.len
6175 6183
    );
6176 6184
}
6177 6185
6178 6186
/// Lower a builtin call expression.
6179 -
fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6187 +
unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6180 6188
    match kind {
6181 6189
        case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args),
6182 6190
        case ast::Builtin::SizeOf, ast::Builtin::AlignOf => {
6183 -
            let constVal = resolver::constValueEntry(self.low.resolver, node) else {
6191 +
            let constVal = resolver::constValueEntry(&*self.low.resolver, node) else {
6184 6192
                throw LowerError::MissingConst(node);
6185 6193
            };
6186 6194
            return try constValueToVal(self, constVal, node);
6187 6195
        }
6188 6196
    }
6189 6197
}
6190 6198
6191 6199
/// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call.
6192 -
fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6200 +
unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6193 6201
    if args.len <> 2 and args.len <> 3 {
6194 6202
        throw LowerError::InvalidArgCount;
6195 6203
    }
6196 6204
    let sliceTy = try typeOf(self, node);
6197 6205
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6207 6215
    }
6208 6216
    return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal);
6209 6217
}
6210 6218
6211 6219
/// Lower a `try` expression.
6212 -
fn lowerTry(self: *mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) {
6220 +
unsafe fn lowerTry(self: &mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) {
6213 6221
    let case ast::NodeValue::Call(callExpr) = t.expr.value else {
6214 6222
        throw LowerError::ExpectedCall;
6215 6223
    };
6216 6224
    let calleeTy = try typeOf(self, callExpr.callee);
6217 6225
    let case resolver::Type::Fn(calleeInfo) = calleeTy else {
6222 6230
    // Type of the try expression, which is either the return type of the function
6223 6231
    // if successful, or an optional of it, if using `try?`.
6224 6232
    let tryExprTy = try typeOf(self, node);
6225 6233
    // Check for trait method dispatch or standalone method call.
6226 6234
    let mut resVal: il::Val = undefined;
6227 -
    let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra;
6235 +
    let callNodeExtra = resolver::nodeData(&*self.low.resolver, t.expr).extra;
6228 6236
    if let case resolver::NodeExtra::TraitMethodCall {
6229 6237
        traitInfo, methodIndex
6230 6238
    } = callNodeExtra {
6231 6239
        set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex);
6232 6240
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6233 -
        set resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6241 +
        set resVal = try lowerMethodCall(self, t.expr, callExpr, &*method);
6234 6242
    } else {
6235 6243
        set resVal = try lowerCall(self, t.expr, callExpr);
6236 6244
    }
6237 6245
    let base = emitValToReg(self, resVal); // The result value.
6238 6246
    let tagReg = resultTagReg(self, base); // The result tag.
6300 6308
                let case ast::NodeValue::Ident(name) = binding.value else {
6301 6309
                    throw LowerError::ExpectedIdentifier;
6302 6310
                };
6303 6311
                let errTy = *calleeInfo.throwList[0];
6304 6312
                let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET);
6305 -
                let _ = newVar(self, name, ilType(self.low, errTy), false, errVal);
6313 +
                let _ = newVar(self, name, ilType(&mut *self.low, errTy), false, errVal);
6306 6314
            }
6307 6315
            try lowerBlock(self, first.body);
6308 6316
            try emitMergeIfUnterminated(self, &mut mergeBlock);
6309 6317
            exitVarScope(self, savedVarsLen);
6310 6318
        }
6351 6359
/// Lower typed multi-catch clauses.
6352 6360
///
6353 6361
/// Emits a switch on the global error tag to dispatch to the correct catch
6354 6362
/// clause. Each typed clause extracts the error payload for its specific type
6355 6363
/// and binds it to the clause's identifier.
6356 -
fn lowerMultiCatch(
6357 -
    self: *mut FnLowerer,
6364 +
unsafe fn lowerMultiCatch(
6365 +
    self: &mut FnLowerer,
6358 6366
    catches: *mut [*ast::Node],
6359 6367
    calleeInfo: *resolver::FnType,
6360 6368
    base: il::Reg,
6361 6369
    tagReg: il::Reg,
6362 -
    mergeBlock: *mut ?BlockId
6370 +
    mergeBlock: &mut ?BlockId
6363 6371
) throws (LowerError) {
6364 6372
    let entry = currentBlock(self);
6365 6373
6366 6374
    // First pass: create blocks, resolve error types, and build switch cases.
6367 6375
    let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined;
6379 6387
        if let typeNode = clause.typeNode {
6380 6388
            let errTy = try typeOf(self, typeNode);
6381 6389
            set errTypes[i] = errTy;
6382 6390
6383 6391
            cases.append(il::SwitchCase {
6384 -
                value: getOrAssignErrorTag(self.low, errTy) as i64,
6392 +
                value: getOrAssignErrorTag(&mut *self.low, errTy) as i64,
6385 6393
                target: *blocks[i],
6386 6394
                args: &mut []
6387 6395
            }, self.allocator);
6388 6396
        } else {
6389 6397
            set errTypes[i] = nil;
6419 6427
                throw LowerError::ExpectedIdentifier;
6420 6428
            };
6421 6429
            let errTy = errTypes[i] else panic "lowerMultiCatch: catch-all with binding";
6422 6430
            let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET);
6423 6431
6424 -
            newVar(self, name, ilType(self.low, errTy), false, errVal);
6432 +
            newVar(self, name, ilType(&mut *self.low, errTy), false, errVal);
6425 6433
        }
6426 6434
        try lowerBlock(self, clause.body);
6427 6435
        try emitMergeIfUnterminated(self, mergeBlock);
6428 6436
6429 6437
        exitVarScope(self, savedVarsLen);
6439 6447
/// Emit a byte-copy loop: `for i in 0..size { dst[i] = src[i]; }`.
6440 6448
///
6441 6449
/// Used when `blit` cannot be used because the copy size is dynamic.
6442 6450
/// Terminates the current block and leaves the builder positioned
6443 6451
/// after the loop.
6444 -
fn emitByteCopyLoop(
6445 -
    self: *mut FnLowerer,
6452 +
unsafe fn emitByteCopyLoop(
6453 +
    self: &mut FnLowerer,
6446 6454
    dst: il::Reg,
6447 6455
    src: il::Reg,
6448 6456
    size: il::Val,
6449 6457
    label: *[u8]
6450 6458
) throws (LowerError) {
6498 6506
///
6499 6507
///     @store:
6500 6508
///       store element at ptr + len * stride
6501 6509
///       increment len
6502 6510
///
6503 -
fn lowerSliceAppend(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) {
6511 +
unsafe fn lowerSliceAppend(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) {
6504 6512
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6505 6513
        else throw LowerError::MissingMetadata;
6506 6514
6507 6515
    // Get the address of the slice header.
6508 6516
    let sliceVal = try lowerExpr(self, access.parent);
6584 6592
6585 6593
/// Lower `slice.delete(index)`.
6586 6594
///
6587 6595
/// Bounds-check the index, shift elements after it by one stride
6588 6596
/// via a byte-copy loop, and decrement `len`.
6589 -
fn lowerSliceDelete(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) {
6597 +
unsafe fn lowerSliceDelete(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) {
6590 6598
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6591 6599
        else throw LowerError::MissingMetadata;
6592 6600
6593 6601
    let elemLayout = resolver::getTypeLayout(*elemType);
6594 6602
    let stride = elemLayout.size;
6624 6632
6625 6633
    emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET);
6626 6634
}
6627 6635
6628 6636
/// Lower a call expression, which may be a function call or type constructor.
6629 -
fn lowerCallOrCtor(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6630 -
    let nodeData = resolver::nodeData(self.low.resolver, node).extra;
6637 +
unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6638 +
    let nodeData = resolver::nodeData(&*self.low.resolver, node).extra;
6631 6639
6632 6640
    // Check for slice method dispatch.
6633 6641
    if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData {
6634 6642
        return try lowerSliceAppend(self, call, elemType);
6635 6643
    }
6641 6649
    if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData {
6642 6650
        return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex);
6643 6651
    }
6644 6652
    // Check for standalone method call.
6645 6653
    if let case resolver::NodeExtra::MethodCall { method } = nodeData {
6646 -
        return try lowerMethodCall(self, node, call, method);
6654 +
        return try lowerMethodCall(self, node, call, &*method);
6647 6655
    }
6648 -
    if let sym = resolver::nodeData(self.low.resolver, call.callee).sym {
6656 +
    if let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym {
6649 6657
        if let case resolver::SymbolData::Type(nominal) = sym.data {
6650 6658
            let case resolver::NominalType::Record(_) = *nominal else {
6651 6659
                throw LowerError::ExpectedRecord;
6652 6660
            };
6653 6661
            return try lowerRecordCtor(self, nominal, call.args);
6666 6674
///     load w64 %data %obj 0          // data pointer
6667 6675
///     load w64 %vtable %obj 8        // v-table pointer
6668 6676
///     load w64 %fn %vtable <slot>    // function pointer
6669 6677
///     call <retTy> %ret %fn(%data, args...)
6670 6678
///
6671 -
fn lowerTraitMethodCall(
6672 -
    self: *mut FnLowerer,
6679 +
unsafe fn lowerTraitMethodCall(
6680 +
    self: &mut FnLowerer,
6673 6681
    node: *ast::Node,
6674 6682
    call: ast::Call,
6675 6683
    traitInfo: *resolver::TraitType,
6676 6684
    methodIndex: u32
6677 6685
) -> il::Val throws (LowerError) {
6728 6736
6729 6737
/// Lower a call argument, snapshotting aggregate place expressions before
6730 6738
/// evaluating later arguments. Aggregates are represented by addresses in the
6731 6739
/// IL, so retaining the original address would let a later argument mutation
6732 6740
/// change the value already supplied for this argument.
6733 -
fn lowerCallArg(self: *mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val
6741 +
unsafe fn lowerCallArg(self: &mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val
6734 6742
    throws (LowerError)
6735 6743
{
6736 6744
    let val = try lowerExpr(self, arg);
6737 6745
6738 6746
    if hasLater and ast::isPlaceExpr(arg) {
6748 6756
///
6749 6757
/// All call lowering paths (regular, trait method, standalone method) converge
6750 6758
/// here after preparing the callee value, function type, and argument array.
6751 6759
/// The `args` slice must already include a slot at index zero for the hidden
6752 6760
/// return parameter; that slot is filled by this function.
6753 -
fn emitCallValue(
6754 -
    self: *mut FnLowerer,
6761 +
unsafe fn emitCallValue(
6762 +
    self: &mut FnLowerer,
6755 6763
    callee: il::Val,
6756 6764
    fnInfo: *resolver::FnType,
6757 6765
    args: *mut [il::Val],
6758 6766
) -> il::Val throws (LowerError) {
6759 6767
    let retTy = *fnInfo.returnType;
6778 6786
    let mut dst: ?il::Reg = nil;
6779 6787
    if retTy <> resolver::Type::Void {
6780 6788
        set dst = nextReg(self);
6781 6789
    }
6782 6790
    emit(self, il::Instr::Call {
6783 -
        retTy: ilType(self.low, retTy),
6791 +
        retTy: ilType(&mut *self.low, retTy),
6784 6792
        dst,
6785 6793
        func: callee,
6786 6794
        args,
6787 6795
    });
6788 6796
6807 6815
6808 6816
/// Lower a method receiver expression to a pointer value.
6809 6817
///
6810 6818
/// If the parent is already a pointer type, the value is used directly.
6811 6819
/// If the parent is a value type (eg. a local record), its address is taken.
6812 -
fn lowerReceiver(self: *mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val
6820 +
unsafe fn lowerReceiver(self: &mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val
6813 6821
    throws (LowerError)
6814 6822
{
6815 6823
    if let case resolver::Type::Pointer { .. } = parentTy {
6816 6824
        // Already a pointer: lower and use directly.
6817 6825
        return try lowerExpr(self, parent);
6821 6829
    let val = try lowerExpr(self, parent);
6822 6830
    if isAggregateType(parentTy) {
6823 6831
        return val;
6824 6832
    }
6825 6833
    // Scalar value: store to a stack slot and return the slot pointer.
6826 -
    let layout = resolver::getLayout(self.low.resolver, parent, parentTy);
6834 +
    let layout = resolver::getLayout(&*self.low.resolver, parent, parentTy);
6827 6835
    let slot = emitReserveLayout(self, layout);
6828 6836
    try emitStore(self, slot, 0, parentTy, val);
6829 6837
6830 6838
    return il::Val::Reg(slot);
6831 6839
}
6835 6843
/// Given `obj.method(args)` where `method` is a standalone method on a concrete type,
6836 6844
/// emits a direct call with the receiver address as the first argument:
6837 6845
///
6838 6846
///     call <retTy> %ret @Type::method(&obj, args...)
6839 6847
///
6840 -
fn lowerMethodCall(
6841 -
    self: *mut FnLowerer,
6848 +
unsafe fn lowerMethodCall(
6849 +
    self: &mut FnLowerer,
6842 6850
    node: *ast::Node,
6843 6851
    call: ast::Call,
6844 -
    method: *resolver::MethodEntry,
6852 +
    method: &resolver::MethodEntry,
6845 6853
) -> il::Val throws (LowerError) {
6846 6854
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6847 6855
        else throw LowerError::MissingMetadata;
6848 6856
6849 6857
    // Get the receiver as a pointer.
6850 6858
    let parentTy = try typeOf(self, access.parent);
6851 6859
    let receiverVal = try lowerReceiver(self, access.parent, parentTy);
6852 6860
6853 -
    let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name);
6861 +
    let qualName = instanceMethodName(&mut *self.low, nil, method.concreteTypeName, method.name);
6854 6862
    let case resolver::SymbolData::Value { type: resolver::Type::Fn(fnInfo), .. } = method.symbol.data
6855 6863
        else panic "lowerMethodCall: expected Fn type on method symbol";
6856 6864
6857 6865
    // Build args: optional return param slot + receiver + user args.
6858 6866
    let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
6865 6873
    }
6866 6874
    return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args);
6867 6875
}
6868 6876
6869 6877
/// Check if a call is to a compiler intrinsic and lower it directly.
6870 -
fn lowerIntrinsicCall(self: *mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) {
6878 +
unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) {
6871 6879
    // Get the callee symbol and check if it's marked as an intrinsic.
6872 -
    let sym = resolver::nodeData(self.low.resolver, call.callee).sym else {
6880 +
    let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym else {
6873 6881
        // Expressions or function pointers may not have an associated symbol.
6874 6882
        return nil;
6875 6883
    };
6876 6884
    if not ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic) {
6877 6885
        return nil;
6887 6895
        throw LowerError::UnknownIntrinsic;
6888 6896
    }
6889 6897
}
6890 6898
6891 6899
/// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`.
6892 -
fn lowerEcall(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6900 +
unsafe fn lowerEcall(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6893 6901
    if call.args.len <> 5 {
6894 6902
        throw LowerError::InvalidArgCount;
6895 6903
    }
6896 6904
    let num = try lowerExpr(self, call.args[0]);
6897 6905
    let a0 = try lowerExpr(self, call.args[1]);
6904 6912
6905 6913
    return il::Val::Reg(dst);
6906 6914
}
6907 6915
6908 6916
/// Lower an ebreak intrinsic: `ebreak()`.
6909 -
fn lowerEbreak(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6917 +
unsafe fn lowerEbreak(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6910 6918
    if call.args.len <> 0 {
6911 6919
        throw LowerError::InvalidArgCount;
6912 6920
    }
6913 6921
    emit(self, il::Instr::Ebreak);
6914 6922
6915 6923
    return il::Val::Undef;
6916 6924
}
6917 6925
6918 6926
/// Lower `memoryFence()`.
6919 -
fn lowerMemoryFence(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6927 +
unsafe fn lowerMemoryFence(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) {
6920 6928
    if call.args.len <> 0 {
6921 6929
        throw LowerError::InvalidArgCount;
6922 6930
    }
6923 6931
    emit(self, il::Instr::MemoryFence);
6924 6932
    return il::Val::Undef;
6925 6933
}
6926 6934
6927 6935
/// Resolve callee to an IL value. For direct function calls, use the symbol name.
6928 6936
/// For variables holding function pointers or complex expressions (eg. `array[i]()`),
6929 6937
/// lower the callee expression.
6930 -
fn lowerCallee(self: *mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) {
6931 -
    if let sym = resolver::nodeData(self.low.resolver, callee).sym {
6938 +
unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) {
6939 +
    if let sym = resolver::nodeData(&*self.low.resolver, callee).sym {
6932 6940
        if let case ast::NodeValue::FnDecl(_) = sym.node.value {
6933 6941
            // First try to look up the symbol in our registered functions.
6934 6942
            // This handles cross-package calls correctly, since packages are
6935 6943
            // lowered in dependency order.
6936 -
            if let qualName = lookupFnSym(self.low, sym) {
6944 +
            if let qualName = lookupFnSym(&mut *self.low, sym) {
6937 6945
                return il::Val::FnAddr(qualName);
6938 6946
            }
6939 6947
            // Fall back to computing the qualified name from the module graph.
6940 6948
            // This works for functions in the current package.
6941 -
            let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else {
6949 +
            let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym) else {
6942 6950
                throw LowerError::MissingMetadata;
6943 6951
            };
6944 -
            return il::Val::FnAddr(qualifyName(self.low, modId, sym.name));
6952 +
            return il::Val::FnAddr(qualifyName(&mut *self.low, modId, sym.name));
6945 6953
        }
6946 6954
    }
6947 6955
    return try lowerExpr(self, callee);
6948 6956
}
6949 6957
6950 6958
/// Lower a function call expression.
6951 -
fn lowerCall(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6959 +
unsafe fn lowerCall(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) {
6952 6960
    // Check for intrinsic calls before normal call lowering.
6953 6961
    if let intrinsicVal = try lowerIntrinsicCall(self, call) {
6954 6962
        return intrinsicVal;
6955 6963
    }
6956 6964
    let calleeTy = try typeOf(self, call.callee);
6968 6976
6969 6977
    return try emitCallValue(self, callee, fnInfo, args);
6970 6978
}
6971 6979
6972 6980
/// Apply coercions requested by the resolver.
6973 -
fn applyCoercion(self: *mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) {
6974 -
    let coerce = resolver::coercionFor(self.low.resolver, node) else {
6981 +
unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) {
6982 +
    let coerce = resolver::coercionFor(&*self.low.resolver, node) else {
6975 6983
        return val;
6976 6984
    };
6977 6985
    match coerce {
6978 6986
        case resolver::Coercion::OptionalLift(optType) => {
6979 6987
            if let case ast::NodeValue::Nil = node.value {
6987 6995
        case resolver::Coercion::ResultWrap => {
6988 6996
            let payloadType = *self.fnType.returnType;
6989 6997
            return try buildResult(self, 0, val, payloadType);
6990 6998
        }
6991 6999
        case resolver::Coercion::TraitObject { traitInfo, inst } => {
6992 -
            return try buildTraitObject(self, val, traitInfo, inst);
7000 +
            return try buildTraitObject(self, val, traitInfo, &*inst);
6993 7001
        }
6994 7002
        case resolver::Coercion::Identity => return val,
6995 7003
    }
6996 7004
}
6997 7005
6998 7006
/// Lower an implicit numeric cast coercion.
6999 7007
///
7000 7008
/// Handles widening conversions between integer types. Uses sign-extension
7001 7009
/// for signed source types and zero-extension for unsigned source types.
7002 -
fn lowerNumericCast(self: *mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val {
7010 +
unsafe fn lowerNumericCast(self: &mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val {
7003 7011
    let srcLayout = resolver::getTypeLayout(srcType);
7004 7012
    let dstLayout = resolver::getTypeLayout(dstType);
7005 7013
7006 7014
    if srcLayout.size == dstLayout.size {
7007 7015
        // Same size: bit pattern is unchanged, value is returned as-is.
7008 7016
        return val;
7009 7017
    }
7010 7018
    // Widening: extend based on source signedness.
7011 7019
    // Narrowing: truncate and normalize to destination width.
7012 7020
    let widening = srcLayout.size < dstLayout.size;
7013 -
    let extType = ilType(self.low, srcType) if widening else ilType(self.low, dstType);
7021 +
    let extType = ilType(&mut *self.low, srcType) if widening else ilType(&mut *self.low, dstType);
7014 7022
    let signed = isSignedType(srcType) if widening else isSignedType(dstType);
7015 7023
    let dst = nextReg(self);
7016 7024
7017 7025
    if signed {
7018 7026
        emit(self, il::Instr::Sext { typ: extType, dst, val });
7021 7029
    }
7022 7030
    return il::Val::Reg(dst);
7023 7031
}
7024 7032
7025 7033
/// Lower a global value symbol.
7026 -
fn lowerGlobalValue(self: *mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val {
7034 +
unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val {
7027 7035
    // Function pointer reference: return the function's address directly.
7028 7036
    // Functions have no separate storage cell in the data section.
7029 7037
    if let case resolver::Type::Fn(_) = ty {
7030 7038
        return il::Val::Reg(emitFnAddr(self, sym));
7031 7039
    }
7033 7041
7034 7042
    return emitRead(self, src, 0, ty);
7035 7043
}
7036 7044
7037 7045
/// Lower an identifier that refers to a global symbol.
7038 -
fn lowerGlobalSymbol(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7046 +
unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7039 7047
    // First try to get a compile-time constant value.
7040 -
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7048 +
    if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7041 7049
        return try constValueToVal(self, constVal, node);
7042 7050
    }
7043 7051
    // Otherwise get the symbol.
7044 7052
    let sym = try symOf(self, node);
7045 7053
7054 7062
        else => throw LowerError::UnexpectedNodeValue(node),
7055 7063
    }
7056 7064
}
7057 7065
7058 7066
/// Lower an assignment to a static variable.
7059 -
fn lowerStaticAssign(self: *mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) {
7067 +
unsafe fn lowerStaticAssign(self: &mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) {
7060 7068
    let sym = try symOf(self, target);
7061 7069
    let case resolver::SymbolData::Value { type, .. } = sym.data else {
7062 7070
        throw LowerError::ImmutableAssignment;
7063 7071
    };
7064 7072
    let dst = emitDataAddr(self, sym);
7066 7074
    try emitStore(self, dst, 0, type, val);
7067 7075
}
7068 7076
7069 7077
/// Lower a scope access expression like `Module::Const` or `Union::Variant`.
7070 7078
/// This doesn't handle record literal variants.
7071 -
fn lowerScopeAccess(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7079 +
unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7072 7080
    // First try to get a compile-time constant value.
7073 -
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7081 +
    if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7074 7082
        return try constValueToVal(self, constVal, node);
7075 7083
    }
7076 7084
    // Otherwise get the associated symbol.
7077 -
    let data = resolver::nodeData(self.low.resolver, node);
7085 +
    let data = resolver::nodeData(&*self.low.resolver, node);
7078 7086
    let sym = data.sym else {
7079 7087
        throw LowerError::MissingSymbol(node);
7080 7088
    };
7081 7089
    match sym.data {
7082 7090
        case resolver::SymbolData::Variant { index, .. } => {
7083 7091
            let mut indexValue = index as i64;
7084 -
            if let idx = voidVariantIndex(self.low.resolver, node) {
7092 +
            if let idx = voidVariantIndex(&*self.low.resolver, node) {
7085 7093
                set indexValue = idx;
7086 7094
            }
7087 7095
            // Void union variant like `Option::None`.
7088 7096
            if data.ty == resolver::Type::Unknown {
7089 7097
                throw LowerError::MissingType(node);
7123 7131
    }
7124 7132
}
7125 7133
7126 7134
/// Lower an expression AST node to an IL value.
7127 7135
/// This is the main expression dispatch, all expression nodes go through here.
7128 -
fn lowerExpr(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7136 +
unsafe fn lowerExpr(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
7129 7137
    if self.low.options.debug {
7130 7138
        set self.srcLoc.offset = node.span.offset;
7131 7139
    }
7132 7140
    let mut val: il::Val = undefined;
7133 7141
7197 7205
        case ast::NodeValue::Try(t) => {
7198 7206
            set val = try lowerTry(self, node, t);
7199 7207
        }
7200 7208
        case ast::NodeValue::FieldAccess(access) => {
7201 7209
            // Check for compile-time constant (e.g., `arr.len` on fixed-size arrays).
7202 -
            if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7210 +
            if let constVal = resolver::constValueEntry(&*self.low.resolver, node) {
7203 7211
                match constVal {
7204 7212
                    // TODO: Handle `u32` values that don't fit in an `i32`.
7205 7213
                    //       Perhaps just store the `ConstInt`.
7206 7214
                    case resolver::ConstValue::Int(i) => set val = il::Val::Imm(constIntToI64(i)),
7207 7215
                    else => set val = try lowerFieldAccess(self, access),
7256 7264
            set val = il::Val::Undef;
7257 7265
        }
7258 7266
        // Lower these as statements.
7259 7267
        case ast::NodeValue::ConstDecl(decl) => {
7260 7268
            try registerLocalDataDeclName(self, node);
7261 -
            try lowerDataDecl(self.low, node, decl.value, true);
7269 +
            try lowerDataDecl(&mut *self.low, node, decl.value, true);
7262 7270
            set val = il::Val::Undef;
7263 7271
        }
7264 7272
        case ast::NodeValue::StaticDecl(decl) => {
7265 7273
            try registerLocalDataDeclName(self, node);
7266 -
            try lowerDataDecl(self.low, node, decl.value, false);
7274 +
            try lowerDataDecl(&mut *self.low, node, decl.value, false);
7267 7275
            set val = il::Val::Undef;
7268 7276
        }
7269 7277
        case ast::NodeValue::Throw { .. },
7270 7278
             ast::NodeValue::Return { .. },
7271 7279
             ast::NodeValue::Continue,
7286 7294
/// are used. If a Radiance type doesn't fit in a machine word, it is passed
7287 7295
/// by reference.
7288 7296
///
7289 7297
/// The IL doesn't track signedness - that's encoded in the instructions
7290 7298
/// (e.g., Slt vs Ult).
7291 -
fn ilType(self: *mut Lowerer, typ: resolver::Type) -> il::Type {
7299 +
fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type {
7292 7300
    match typ {
7293 7301
        case resolver::Type::Bool,
7294 7302
             resolver::Type::I8,
7295 7303
             resolver::Type::U8 => return il::Type::W8,
7296 7304
        case resolver::Type::I16,
lib/std/lang/module.rad +30 -26
84 84
    source: ?*[u8],
85 85
}
86 86
87 87
/// Dense storage for all modules referenced by the compilation unit.
88 88
export record ModuleGraph: Copy {
89 +
    /// Permanent storage for module entries.
89 90
    entries: *mut [ModuleEntry],
91 +
    /// Number of initialized entries.
90 92
    entriesLen: u32,
93 +
    /// Permanent storage for interned names.
91 94
    pool: *mut strings::Pool,
92 -
    /// Arena used for all AST node allocations.
93 -
    arena: ?*ast::NodeArena,
95 +
    /// AST arena. It must outlive the graph.
96 +
    arena: ?*unsafe ast::NodeArena,
94 97
}
95 98
96 99
/// Initialize an empty module graph backed by the provided storage.
97 -
export fn moduleGraph(
100 +
/// The AST arena must outlive the returned graph.
101 +
export unsafe fn moduleGraph(
98 102
    storage: *mut [ModuleEntry],
99 103
    pool: *mut strings::Pool,
100 -
    arena: *mut ast::NodeArena
104 +
    arena: &mut ast::NodeArena
101 105
) -> ModuleGraph {
102 106
    return ModuleGraph {
103 107
        entries: storage,
104 108
        entriesLen: 0,
105 109
        pool,
106 -
        arena,
110 +
        arena: arena as *unsafe ast::NodeArena,
107 111
    };
108 112
}
109 113
110 114
/// Register a root module residing at `path` for a package.
111 -
export fn registerRoot(graph: *mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
115 +
export fn registerRoot(graph: &mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) {
112 116
    let name = try basenameSlice(filePath);
113 117
    return try registerRootWithName(graph, packageId, name, filePath);
114 118
}
115 119
116 120
/// Register a root module with an explicit name and file path for a package.
117 121
export fn registerRootWithName(
118 -
    graph: *mut ModuleGraph,
122 +
    graph: &mut ModuleGraph,
119 123
    packageId: u16,
120 124
    name: *[u8],
121 125
    filePath: *[u8]
122 126
) -> u16 throws (ModuleError) {
123 127
    let m = try allocModule(graph, packageId, name, filePath);
129 133
}
130 134
131 135
/// Register a child module.
132 136
/// Returns the module identifier.
133 137
export fn registerChild(
134 -
    graph: *mut ModuleGraph,
138 +
    graph: &mut ModuleGraph,
135 139
    parentId: u16,
136 140
    name: *[u8],
137 141
    filePath: *[u8]
138 142
) -> u16 throws (ModuleError) {
139 143
    assert name.len > 0, "registerChild: name must not be empty";
160 164
161 165
    return try addChild(parent, m.id);
162 166
}
163 167
164 168
/// Fetch a read-only view of the module identified by `id`.
165 -
export fn get(graph: *ModuleGraph, id: u16) -> ?*ModuleEntry {
169 +
export fn get(graph: &ModuleGraph, id: u16) -> ?*ModuleEntry {
166 170
    if not isValidId(graph, id) {
167 171
        return nil;
168 172
    }
169 173
    return &graph.entries[id as u32];
170 174
}
171 175
172 176
/// Access a mutable entry by identifier.
173 -
fn getMut(graph: *mut ModuleGraph, id: u16) -> ?*mut ModuleEntry {
177 +
fn getMut(graph: &mut ModuleGraph, id: u16) -> ?*mut ModuleEntry {
174 178
    if not isValidId(graph, id) {
175 179
        return nil;
176 180
    }
177 181
    return &mut graph.entries[id as u32];
178 182
}
193 197
    assert m.pathDepth > 0, "moduleQualifiedPath: path must not be empty";
194 198
    return &m.path[..m.pathDepth];
195 199
}
196 200
197 201
/// Retrieve the lifecycle state for `id`.
198 -
export fn state(graph: *ModuleGraph, id: u16) -> ModuleState throws (ModuleError) {
202 +
export fn state(graph: &ModuleGraph, id: u16) -> ModuleState throws (ModuleError) {
199 203
    let m = get(graph, id) else {
200 204
        throw ModuleError::NotFound(id);
201 205
    };
202 206
    return m.state;
203 207
}
204 208
205 209
/// Record the parsed AST root for `id`.
206 -
export fn setAst(graph: *mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) {
210 +
export fn setAst(graph: &mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) {
207 211
    let m = getMut(graph, id) else throw ModuleError::NotFound(id);
208 212
    set m.ast = root;
209 213
    set m.state = ModuleState::Parsed;
210 214
}
211 215
212 216
/// Set the source text for a module.
213 -
export fn setSource(graph: *mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) {
217 +
export fn setSource(graph: &mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) {
214 218
    let m = getMut(graph, id) else throw ModuleError::NotFound(id);
215 219
    set m.source = source;
216 220
}
217 221
218 222
/// Look up a child module by name under the given parent.
219 -
export fn findChild(graph: *ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry {
223 +
export fn findChild(graph: &ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry {
220 224
    assert isValidId(graph, parentId), "findChild: parent identifier is valid";
221 225
222 226
    let parent = &graph.entries[parentId as u32];
223 227
    for i in 0..parent.childrenLen {
224 228
        let childId = parent.children[i];
231 235
}
232 236
233 237
/// Parse a file path into components by splitting on '/'.
234 238
/// Expects and removes the '.rad' extension from the last component.
235 239
/// Returns the number of components extracted, or `nil` if the path is invalid.
236 -
export fn parsePath(filePath: *[u8], components: *mut [*[u8]]) -> ?u32 {
240 +
export fn parsePath(filePath: *[u8], components: &mut [*[u8]]) -> ?u32 {
237 241
    let mut count: u32 = 0;
238 242
    let mut last: u32 = 0;
239 243
240 244
    // Split on '/' to extract all but the last component.
241 245
    for i in 0..filePath.len {
266 270
267 271
/// Register a module from a file path, creating the full hierarchy as needed.
268 272
/// The path is split into components and the module hierarchy is built accordingly.
269 273
/// If `rootId` is `nil`, registers a new root for the given package.
270 274
/// Returns the module ID of the last component.
271 -
export fn registerFromPath(graph: *mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
275 +
export fn registerFromPath(graph: &mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) {
272 276
    let root = rootId else {
273 277
        return try registerRoot(graph, packageId, filePath);
274 278
    };
275 279
    let rootEntry = get(graph, root) else {
276 280
        panic "registerFromPath: root is missing from storage";
291 295
        throw ModuleError::InvalidPath;
292 296
    }
293 297
294 298
    // Strip the root's qualified path from the parsed components.
295 299
    let rootPath = moduleQualifiedPath(rootEntry);
296 -
    let childPath = stripPathPrefix(rootPath, &parts[..partsLen]) else {
300 +
    let prefixLen = stripPathPrefix(rootPath, &parts[..partsLen]) else {
297 301
        throw ModuleError::InvalidPath;
298 302
    };
299 -
    if childPath.len == 0 {
303 +
    if prefixLen == partsLen {
300 304
        throw ModuleError::InvalidPath;
301 305
    }
302 -
    let childName = childPath[childPath.len - 1];
306 +
    let childName = parts[partsLen - 1];
303 307
304 308
    // Navigate through all but the last segment to find the parent.
305 309
    let mut parentId = root;
306 -
    for part in &childPath[..childPath.len - 1] {
310 +
    for part in &parts[prefixLen..partsLen - 1] {
307 311
        let child = findChild(graph, part, parentId) else {
308 312
            throw ModuleError::MissingParent;
309 313
        };
310 314
        set parentId = child.id;
311 315
    }
312 316
    return try registerChild(graph, parentId, childName, filePath);
313 317
}
314 318
315 319
/// Allocate a fresh entry in the graph.
316 -
fn allocModule(graph: *mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
320 +
fn allocModule(graph: &mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) {
317 321
    if graph.entriesLen >= graph.entries.len {
318 322
        throw ModuleError::CapacityExceeded;
319 323
    }
320 324
    let idx = graph.entriesLen;
321 325
    set graph.entriesLen += 1;
361 365
362 366
    return childId;
363 367
}
364 368
365 369
/// Check if `id` points at an allocated entry.
366 -
fn isValidId(graph: *ModuleGraph, id: u16) -> bool {
370 +
fn isValidId(graph: &ModuleGraph, id: u16) -> bool {
367 371
    return (id as u32) < graph.entriesLen;
368 372
}
369 373
370 374
/// Return the length of the directory prefix for `path`.
371 375
/// Return zero if the path has no separator.
401 405
        }
402 406
    }
403 407
    return &path[..extStart];
404 408
}
405 409
406 -
/// Strip prefix from path, and return the suffix.
407 -
fn stripPathPrefix(prefix: *[*[u8]], path: *[*[u8]]) -> ?*[*[u8]] {
410 +
/// Check a path prefix and return its segment count.
411 +
fn stripPathPrefix(prefix: &[*[u8]], path: &[*[u8]]) -> ?u32 {
408 412
    if prefix.len == 0 {
409 -
        return path;
413 +
        return 0;
410 414
    }
411 415
    if prefix.len > path.len {
412 416
        return nil;
413 417
    }
414 418
    for segment, i in prefix {
415 419
        if not mem::eq(segment, path[i]) {
416 420
            return nil;
417 421
        }
418 422
    }
419 -
    return &path[prefix.len..];
423 +
    return prefix.len;
420 424
}
lib/std/lang/module/printer.rad +7 -7
5 5
use std::io;
6 6
use std::lang::sexpr;
7 7
use std::lang::alloc;
8 8
9 9
/// Format a u32 and allocate the result in the arena.
10 -
fn formatId(a: *mut alloc::Arena, id: u32) -> *[u8] {
10 +
fn formatId(a: &mut alloc::Arena, id: u32) -> *[u8] {
11 11
    let mut digits: [u8; 10] = undefined;
12 -
    let text = fmt::formatU32(id, &mut digits[..]);
13 -
    let ptr = try alloc::allocSlice(a, 1, 1, text.len) catch { return "?"; };
12 +
    let start = fmt::formatU32(id, &mut digits[..]);
13 +
    let ptr = try alloc::allocSlice(a, 1, 1, digits.len - start) catch { return "?"; };
14 14
    let slice = ptr as *mut [u8];
15 -
    try mem::copy(slice, text) catch { return "?"; };
15 +
    try mem::copy(slice, &digits[start..]) catch { return "?"; };
16 16
    return slice;
17 17
}
18 18
19 19
/// Convert a module state into an S-expression symbol.
20 20
fn stateToExpr(state: super::ModuleState) -> sexpr::Expr {
28 28
    }
29 29
}
30 30
31 31
/// Recursively convert a module entry and its descendants to an S-expression.
32 32
fn subtreeToExpr(
33 -
    a: *mut alloc::Arena,
34 -
    graph: *super::ModuleGraph,
33 +
    a: &mut alloc::Arena,
34 +
    graph: &super::ModuleGraph,
35 35
    entry: *super::ModuleEntry
36 36
) -> sexpr::Expr {
37 37
    let idText = formatId(a, entry.id as u32);
38 38
    let path = super::moduleQualifiedPath(entry);
39 39
    let mut pathBuf: *[sexpr::Expr] = &[];
62 62
        sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false }
63 63
    ], childBuf);
64 64
}
65 65
66 66
/// Print the entire module graph in S-expression format.
67 -
export fn printGraph(graph: *super::ModuleGraph, arena: *mut alloc::Arena) {
67 +
export unsafe fn printGraph(graph: &super::ModuleGraph, arena: &mut alloc::Arena) {
68 68
    // Print all root modules.
69 69
    for i in 0..graph.entriesLen {
70 70
        let entry = &graph.entries[i];
71 71
        if entry.parent == nil {
72 72
            sexpr::print(subtreeToExpr(arena, graph, entry), 0);
lib/std/lang/module/tests.rad +14 -14
26 26
    for i in 0..expected.len {
27 27
        try expectSliceEq(actual[i], expected[i]);
28 28
    }
29 29
}
30 30
31 -
@test fn testRegisterChildren() throws (testing::TestError) {
32 -
    let mut storage: [super::ModuleEntry; 4] = undefined;
31 +
@test unsafe fn testRegisterChildren() throws (testing::TestError) {
32 +
    static storage: [super::ModuleEntry; 4] = undefined;
33 33
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
34 34
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
35 35
36 36
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/root.rad") catch {
37 37
        throw testing::TestError::Failed;
70 70
    try testing::expect(parent.childrenLen == 2);
71 71
    try testing::expect(super::childAt(parent, 0) == firstId);
72 72
    try testing::expect(super::childAt(parent, 1) == secondId);
73 73
}
74 74
75 -
@test fn testRegisterChildReusesExisting() throws (testing::TestError) {
76 -
    let mut storage: [super::ModuleEntry; 4] = undefined;
75 +
@test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) {
76 +
    static storage: [super::ModuleEntry; 4] = undefined;
77 77
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
78 78
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
79 79
80 80
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/main.rad") catch {
81 81
        throw testing::TestError::Failed;
141 141
    let mut components: [*[u8]; 8] = undefined;
142 142
    let result = super::parsePath(path, &mut components[..]);
143 143
    try testing::expect(result == nil);
144 144
}
145 145
146 -
@test fn testRegisterFromPathHierarchy() throws (testing::TestError) {
147 -
    let mut storage: [super::ModuleEntry; 8] = undefined;
146 +
@test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) {
147 +
    static storage: [super::ModuleEntry; 8] = undefined;
148 148
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
149 149
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
150 150
151 151
    // Register root module.
152 152
    let stdId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch {
190 190
    try testing::expect(lang.childrenLen == 1);
191 191
    try testing::expect(super::childAt(lang, 0) == parserId);
192 192
    try testing::expect(parser.childrenLen == 0);
193 193
}
194 194
195 -
@test fn testRegisterFromPathMissingParent() throws (testing::TestError) {
196 -
    let mut storage: [super::ModuleEntry; 8] = undefined;
195 +
@test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) {
196 +
    static storage: [super::ModuleEntry; 8] = undefined;
197 197
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
198 198
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
199 199
200 200
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch {
201 201
        throw testing::TestError::Failed;
205 205
        return;
206 206
    };
207 207
    throw testing::TestError::Failed;
208 208
}
209 209
210 -
@test fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) {
211 -
    let mut storage: [super::ModuleEntry; 8] = undefined;
210 +
@test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) {
211 +
    static storage: [super::ModuleEntry; 8] = undefined;
212 212
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
213 213
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
214 214
215 215
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch {
216 216
        throw testing::TestError::Failed;
220 220
        return;
221 221
    };
222 222
    throw testing::TestError::Failed;
223 223
}
224 224
225 -
@test fn testRegisterFromPathRegistersRoot() throws (testing::TestError) {
226 -
    let mut storage: [super::ModuleEntry; 8] = undefined;
225 +
@test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) {
226 +
    static storage: [super::ModuleEntry; 8] = undefined;
227 227
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
228 228
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
229 229
230 230
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch {
231 231
        throw testing::TestError::Failed;
239 239
    try expectSliceEq(root.name, "std");
240 240
    try expectSliceEq(root.filePath, "lib/std.rad");
241 241
    try expectPathSegments(root, &["std"]);
242 242
}
243 243
244 -
@test fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) {
245 -
    let mut storage: [super::ModuleEntry; 8] = undefined;
244 +
@test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) {
245 +
    static storage: [super::ModuleEntry; 8] = undefined;
246 246
    let mut arena = ast::nodeArena(&mut TEST_ARENA[..]);
247 247
    let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena);
248 248
249 249
    let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/pkg/root.rad") catch {
250 250
        throw testing::TestError::Failed;
lib/std/lang/package.rad +3 -4
19 19
    rootModuleId: ?u16,
20 20
}
21 21
22 22
/// Initialize `pkg` with the provided name and ID.
23 23
export fn init(
24 -
    pkg: *mut Package,
24 +
    pkg: &mut Package,
25 25
    id: u16,
26 26
    name: *[u8],
27 27
    pool: *mut strings::Pool
28 -
) -> *mut Package {
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 -
    return pkg;
34 33
}
35 34
36 35
/// Register a module described by the file path.
37 -
export fn registerModule(pkg: *mut Package, graph: *mut module::ModuleGraph, filePath: *[u8]) -> u16
36 +
export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, filePath: *[u8]) -> u16
38 37
    throws (module::ModuleError)
39 38
{
40 39
    let modId = try module::registerFromPath(graph, pkg.id, pkg.rootModuleId, filePath);
41 40
    // First registered module becomes the root.
42 41
    if pkg.rootModuleId == nil {
lib/std/lang/parser.rad +132 -127
151 151
    current: scanner::Token,
152 152
    /// The most recently consumed token.
153 153
    previous: scanner::Token,
154 154
    /// Collection of errors encountered during parsing.
155 155
    errors: ErrorList,
156 -
    /// Arena for all node allocations.
157 -
    arena: *mut ast::NodeArena,
156 +
    /// Arena for node allocations. It must outlive the parser.
157 +
    arena: *unsafe mut ast::NodeArena,
158 158
    /// Allocator backed by the node arena.
159 159
    allocator: alloc::Allocator,
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 -
export fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> Parser {
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 {
166 167
    return Parser {
167 168
        scanner: scanner::scanner(sourceLoc, source, pool),
168 169
        current: scanner::invalid(0, ""),
169 170
        previous: scanner::invalid(0, ""),
170 171
        errors: ErrorList { list: undefined, count: 0 },
171 -
        arena,
172 +
        arena: arena as *unsafe mut ast::NodeArena,
172 173
        allocator: alloc::arenaAllocator(&mut arena.arena),
173 174
        context: Context::Normal,
174 175
    };
175 176
}
176 177
177 178
/// Emit a `true` or `false` literal node.
178 -
fn nodeBool(p: *mut Parser, value: bool) -> *ast::Node {
179 +
unsafe fn nodeBool(p: &mut Parser, value: bool) -> *ast::Node {
179 180
    return node(p, ast::NodeValue::Bool(value));
180 181
}
181 182
182 183
/// Parse an integer literal while mapping shared errors into parser diagnostics.
183 -
fn parseIntLiteral(p: *mut Parser, text: *[u8]) -> fmt::IntLiteral
184 +
fn parseIntLiteral(p: &mut Parser, text: *[u8]) -> fmt::IntLiteral
184 185
    throws (ParseError)
185 186
{
186 187
    let literal = try fmt::parseInt(text) catch err {
187 188
        match err {
188 189
            case fmt::ParseError::Invalid =>
195 196
    };
196 197
    return literal;
197 198
}
198 199
199 200
/// Emit an integer type node.
200 -
fn nodeTypeInt(p: *mut Parser, width: u8, sign: ast::Signedness) -> *ast::Node {
201 +
unsafe fn nodeTypeInt(p: &mut Parser, width: u8, sign: ast::Signedness) -> *ast::Node {
201 202
    return node(p, ast::NodeValue::TypeSig(
202 203
        ast::TypeSig::Integer { width, sign }
203 204
    ));
204 205
}
205 206
206 207
/// Emit a number literal node with the provided literal metadata.
207 -
fn nodeNumber(p: *mut Parser, literal: fmt::IntLiteral) -> *ast::Node {
208 +
unsafe fn nodeNumber(p: &mut Parser, literal: fmt::IntLiteral) -> *ast::Node {
208 209
    return node(p, ast::NodeValue::Number(literal));
209 210
}
210 211
211 212
/// Emit a `super` node.
212 -
fn nodeSuper(p: *mut Parser) -> *ast::Node {
213 +
unsafe fn nodeSuper(p: &mut Parser) -> *ast::Node {
213 214
    return node(p, ast::NodeValue::Super);
214 215
}
215 216
216 217
/// Emit a single attribute node.
217 -
fn nodeAttribute(p: *mut Parser, attr: ast::Attribute) -> *ast::Node {
218 +
unsafe fn nodeAttribute(p: &mut Parser, attr: ast::Attribute) -> *ast::Node {
218 219
    return node(p, ast::NodeValue::Attribute(attr));
219 220
}
220 221
221 222
/// Emit a unary operator node.
222 -
fn nodeUnary(p: *mut Parser, op: ast::UnaryOp, value: *ast::Node) -> *ast::Node {
223 +
unsafe fn nodeUnary(p: &mut Parser, op: ast::UnaryOp, value: *ast::Node) -> *ast::Node {
223 224
    return node(p, ast::NodeValue::UnOp({ op, value }));
224 225
}
225 226
226 227
/// Parse one expression without inheriting a surrounding condition or pattern context.
227 -
fn parseNormalExpr(p: *mut Parser) -> *ast::Node throws (ParseError) {
228 +
unsafe fn parseNormalExpr(p: &mut Parser) -> *ast::Node throws (ParseError) {
228 229
    let saved = p.context;
229 230
    set p.context = Context::Normal;
230 231
    let expr = try parseExpr(p);
231 232
    set p.context = saved;
232 233
    return expr;
233 234
}
234 235
235 236
/// Parse a parenthesized expression without applying postfix operators.
236 -
fn parseParenthesized(p: *mut Parser) -> *ast::Node
237 +
unsafe fn parseParenthesized(p: &mut Parser) -> *ast::Node
237 238
    throws (ParseError)
238 239
{
239 240
    try expect(p, scanner::TokenKind::LParen, "expected `(`");
240 241
241 242
    let expr = try parseNormalExpr(p);
244 245
245 246
    return expr;
246 247
}
247 248
248 249
/// Parse an array literal: `[a, b, c]` or `[item; count]`.
249 -
fn parseArrayLiteral(p: *mut Parser) -> *ast::Node
250 +
unsafe fn parseArrayLiteral(p: &mut Parser) -> *ast::Node
250 251
    throws (ParseError)
251 252
{
252 253
    try expect(p, scanner::TokenKind::LBracket, "expected `[`");
253 254
    if consume(p, scanner::TokenKind::RBracket) { // Empty array: `[]`.
254 255
        let empty: *mut [*ast::Node] = &mut [];
264 265
        return node(p, ast::NodeValue::ArrayRepeatLit(
265 266
            ast::ArrayRepeatLit { item: firstExpr, count }
266 267
        ));
267 268
    }
268 269
    // Regular array literal: `[a, b, ...]`.
269 -
    let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator);
270 +
    let mut items = ast::nodeSlice(&mut *p.arena, 64).append(firstExpr, p.allocator);
270 271
271 272
    while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) {
272 273
        let elem = try parseNormalExpr(p);
273 274
        items.append(elem, p.allocator);
274 275
    }
276 277
277 278
    return node(p, ast::NodeValue::ArrayLit(items));
278 279
}
279 280
280 281
/// Parse a function call expression.
281 -
fn parseCall(p: *mut Parser, callee: *ast::Node) -> *ast::Node
282 +
unsafe fn parseCall(p: &mut Parser, callee: *ast::Node) -> *ast::Node
282 283
    throws (ParseError)
283 284
{
284 285
    let args = try parseList(
285 286
        p,
286 287
        scanner::TokenKind::LParen,
291 292
        ast::Call { callee, args }
292 293
    ));
293 294
}
294 295
295 296
/// Parse zero or more trailing `as` casts applied to `expr`.
296 -
fn parseAsCast(p: *mut Parser, expr: *ast::Node) -> *ast::Node
297 +
unsafe fn parseAsCast(p: &mut Parser, expr: *ast::Node) -> *ast::Node
297 298
    throws (ParseError)
298 299
{
299 300
    let mut result = expr;
300 301
301 302
    while consume(p, scanner::TokenKind::As) {
311 312
/// Parse an optional conditional expression suffix.
312 313
///
313 314
///   `<thenExpr> if <condition> else <elseExpr>`
314 315
///
315 316
/// If no `if` keyword follows, returns the input expression unchanged.
316 -
fn parseCondExpr(p: *mut Parser, thenExpr: *ast::Node) -> *ast::Node
317 +
unsafe fn parseCondExpr(p: &mut Parser, thenExpr: *ast::Node) -> *ast::Node
317 318
    throws (ParseError)
318 319
{
319 320
    // Only parse conditional expressions in normal context.
320 321
    // In conditional context, `if` is used for guards.
321 322
    if p.context <> Context::Normal {
332 333
        ast::CondExpr { condition, thenExpr, elseExpr }
333 334
    ));
334 335
}
335 336
336 337
/// Parse array subscript or slice expression after `[`.
337 -
fn parseSubscriptOrSlice(p: *mut Parser, container: *ast::Node) -> *ast::Node
338 +
unsafe fn parseSubscriptOrSlice(p: &mut Parser, container: *ast::Node) -> *ast::Node
338 339
    throws (ParseError)
339 340
{
340 341
    try expect(p, scanner::TokenKind::LBracket, "expected `[`");
341 342
342 343
    let mut index: *ast::Node = undefined;
372 373
373 374
    return node(p, ast::NodeValue::Subscript { container, index });
374 375
}
375 376
376 377
/// Parse postfix operators (eg. field access, function call etc.)
377 -
fn parsePostfix(p: *mut Parser, expr: *ast::Node) -> *ast::Node
378 +
unsafe fn parsePostfix(p: &mut Parser, expr: *ast::Node) -> *ast::Node
378 379
    throws (ParseError)
379 380
{
380 381
    let mut result = expr;
381 382
382 383
    loop {
413 414
    }
414 415
    return result;
415 416
}
416 417
417 418
/// Parse a conditional expression.
418 -
export fn parseCond(p: *mut Parser) -> *ast::Node throws (ParseError) {
419 +
export unsafe fn parseCond(p: &mut Parser) -> *ast::Node throws (ParseError) {
419 420
    let saved = p.context;
420 421
    set p.context = Context::Condition;
421 422
    let expr = try parseExpr(p);
422 423
    set p.context = saved;
423 424
424 425
    return expr;
425 426
}
426 427
427 428
/// Parse unary expression followed by optional `as` cast.
428 429
/// `as` has higher precedence than binary ops but lower than unary.
429 -
fn parseUnary(p: *mut Parser) -> *ast::Node throws (ParseError) {
430 +
unsafe fn parseUnary(p: &mut Parser) -> *ast::Node throws (ParseError) {
430 431
    let unary = try parseUnaryExpr(p);
431 432
    return try parseAsCast(p, unary);
432 433
}
433 434
434 435
/// Parse prefix unary expressions and defer to primary expressions otherwise.
435 -
fn parseUnaryExpr(p: *mut Parser) -> *ast::Node
436 +
unsafe fn parseUnaryExpr(p: &mut Parser) -> *ast::Node
436 437
    throws (ParseError)
437 438
{
438 439
    match p.current.kind {
439 440
        case scanner::TokenKind::Not => {
440 441
            advance(p);
496 497
            return false,
497 498
    }
498 499
}
499 500
500 501
/// Build a range expression node with an optional start and end expression.
501 -
fn parseRangeExpr(p: *mut Parser, start: ?*ast::Node) -> *ast::Node
502 +
unsafe fn parseRangeExpr(p: &mut Parser, start: ?*ast::Node) -> *ast::Node
502 503
    throws (ParseError)
503 504
{
504 505
    let mut endExpr: ?*ast::Node = nil;
505 506
506 507
    if not isRangeTerminator(p.current.kind) {
511 512
        ast::Range { start, end: endExpr }
512 513
    ));
513 514
}
514 515
515 516
/// Parse binary expressions using precedence climbing.
516 -
fn parseBinary(p: *mut Parser, left: *ast::Node, minPrec: i32) -> *ast::Node
517 +
unsafe fn parseBinary(p: &mut Parser, left: *ast::Node, minPrec: i32) -> *ast::Node
517 518
    throws (ParseError)
518 519
{
519 520
    let mut result = left;
520 521
521 522
    loop {
579 580
        else => return true,
580 581
    }
581 582
}
582 583
583 584
/// Parse a primary leaf expression without postfix operators.
584 -
fn parseLeaf(p: *mut Parser) -> *ast::Node
585 +
unsafe fn parseLeaf(p: &mut Parser) -> *ast::Node
585 586
    throws (ParseError)
586 587
{
587 588
    match p.current.kind {
588 589
        case scanner::TokenKind::True => {
589 590
            advance(p);
601 602
            advance(p);
602 603
            return nodeSuper(p);
603 604
        }
604 605
        case scanner::TokenKind::Number => {
605 606
            advance(p);
606 -
            let literal = try parseIntLiteral(p, p.previous.source);
607 +
            let source = p.previous.source;
608 +
            let literal = try parseIntLiteral(p, source);
607 609
            return nodeNumber(p, literal);
608 610
        }
609 611
        case scanner::TokenKind::LParen => {
610 612
            return try parseParenthesized(p);
611 613
        }
666 668
        }
667 669
    }
668 670
}
669 671
670 672
/// Parse a primary expression (leaf nodes followed by postfix operators).
671 -
fn parsePrimary(p: *mut Parser) -> *ast::Node
673 +
unsafe fn parsePrimary(p: &mut Parser) -> *ast::Node
672 674
    throws (ParseError)
673 675
{
674 676
    let leaf = try parseLeaf(p);
675 677
    return try parsePostfix(p, leaf);
676 678
}
677 679
678 680
/// Parse a builtin function call like `@sizeOf(T)` or `@alignOf(T)`.
679 -
fn parseBuiltin(p: *mut Parser) -> *ast::Node
681 +
unsafe fn parseBuiltin(p: &mut Parser) -> *ast::Node
680 682
    throws (ParseError)
681 683
{
682 684
    // Skip the '@' to get the name.
683 685
    let ident = p.current.source;
684 686
    advance(p);
696 698
    }
697 699
    try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name");
698 700
699 701
    // Parse arguments into a list. Use capacity 4 to handle any valid argument count
700 702
    // plus some extra for error recovery.
701 -
    let mut args = ast::nodeSlice(p.arena, 4);
703 +
    let mut args = ast::nodeSlice(&mut *p.arena, 4);
702 704
703 705
    if kind == ast::Builtin::SliceOf {
704 706
        // Parse comma-separated expressions until closing paren.
705 707
        // Argument count validation is done in semantic analysis.
706 708
        while not check(p, scanner::TokenKind::RParen) {
719 721
720 722
/// Parse a single expression.
721 723
///
722 724
/// Parses unary and binary operators using precedence climbing.
723 725
/// Conditional expressions (`x if cond else y`) have lowest precedence.
724 -
export fn parseExpr(p: *mut Parser) -> *ast::Node
726 +
export unsafe fn parseExpr(p: &mut Parser) -> *ast::Node
725 727
    throws (ParseError)
726 728
{
727 729
    let left = try parseUnary(p);
728 730
    let expr = try parseBinary(p, left, -1);
729 731
    return try parseCondExpr(p, expr);
730 732
}
731 733
732 734
/// Try to consume a compound assignment operator and return its binary op.
733 -
fn tryCompoundAssignOp(p: *mut Parser) -> ?ast::BinaryOp {
735 +
fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp {
734 736
    match p.current.kind {
735 737
        case scanner::TokenKind::PlusEqual =>    { advance(p); return ast::BinaryOp::Add; }
736 738
        case scanner::TokenKind::MinusEqual =>   { advance(p); return ast::BinaryOp::Sub; }
737 739
        case scanner::TokenKind::StarEqual =>    { advance(p); return ast::BinaryOp::Mul; }
738 740
        case scanner::TokenKind::SlashEqual =>   { advance(p); return ast::BinaryOp::Div; }
745 747
        else => return nil,
746 748
    }
747 749
}
748 750
749 751
/// Parse an expression statement.
750 -
export fn parseExprStmt(p: *mut Parser) -> *ast::Node
752 +
export unsafe fn parseExprStmt(p: &mut Parser) -> *ast::Node
751 753
    throws (ParseError)
752 754
{
753 755
    let expr = try parseExpr(p);
754 756
    return node(p, ast::NodeValue::ExprStmt(expr));
755 757
}
756 758
757 759
/// Parse a `set` statement assignment.
758 -
fn parseSetStmt(p: *mut Parser) -> *ast::Node
760 +
unsafe fn parseSetStmt(p: &mut Parser) -> *ast::Node
759 761
    throws (ParseError)
760 762
{
761 763
    let target = try parseUnary(p);
762 764
    if not ast::isPlaceExpr(target) {
763 765
        throw failParsing(p, "invalid assignment target");
781 783
    }
782 784
    throw failParsing(p, "expected assignment after `set`");
783 785
}
784 786
785 787
/// Parse leading attributes and declaration modifiers.
786 -
fn parseAttributes(p: *mut Parser) -> ?ast::Attributes {
787 -
    let mut attrs = ast::nodeSlice(p.arena, 4);
788 +
unsafe fn parseAttributes(p: &mut Parser) -> ?ast::Attributes {
789 +
    let mut attrs = ast::nodeSlice(&mut *p.arena, 4);
788 790
789 791
    if let attr = tryParseAnnotation(p) {
790 792
        attrs.append(attr, p.allocator);
791 793
    }
792 794
    if consume(p, scanner::TokenKind::Export) {
803 805
804 806
/// Try to parse an annotation like `@default`.
805 807
///
806 808
/// Returns `nil` if not a known annotation (e.g. `@sizeOf` or `@alignOf` which are builtins).
807 809
/// Only consumes tokens if a valid annotation is found.
808 -
fn tryParseAnnotation(p: *mut Parser) -> ?*ast::Node {
810 +
unsafe fn tryParseAnnotation(p: &mut Parser) -> ?*ast::Node {
809 811
    if not check(p, scanner::TokenKind::AtIdent) {
810 812
        return nil;
811 813
    }
812 814
    // Token is @identifier, skip the '@' to get the name.
813 815
    let ident = &p.current.source[..];
827 829
}
828 830
829 831
/// Parse a single statement.
830 832
///
831 833
/// Dispatches to the appropriate statement parser based on the current token.
832 -
export fn parseStmt(p: *mut Parser) -> *ast::Node
834 +
export unsafe fn parseStmt(p: &mut Parser) -> *ast::Node
833 835
    throws (ParseError)
834 836
{
835 837
    // TODO: Why is `parseStmt` checking for attributes?
836 838
    // We should have a `parseDecl` which is top-level, and `parseStmt` which
837 839
    // is inside functions.
838 840
    let attrs = parseAttributes(p);
839 841
    if let list = attrs {
840 842
        if ast::attributesContains(&list, ast::Attribute::Unsafe)
841 843
            and p.current.kind <> scanner::TokenKind::Fn
842 -
            and p.current.kind <> scanner::TokenKind::Mod
843 844
        {
844 -
            throw failParsing(p, "`unsafe` is only allowed on functions and modules");
845 +
            throw failParsing(p, "`unsafe` is only allowed on functions");
845 846
        }
846 847
        let allowed: bool =
847 848
            p.current.kind == scanner::TokenKind::Fn or
848 849
            p.current.kind == scanner::TokenKind::Union or
849 850
            p.current.kind == scanner::TokenKind::Record or
945 946
}
946 947
947 948
/// Parse statements until the specified ending token is encountered.
948 949
///
949 950
/// Adds each parsed statement to the given block's statement list.
950 -
export fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block)
951 +
export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, blk: &mut ast::Block)
951 952
    throws (ParseError)
952 953
{
953 954
    while not check(p, end) {
954 955
        let stmt = try parseStmt(p);
955 956
        blk.statements.append(stmt, p.allocator);
965 966
        }
966 967
    }
967 968
}
968 969
969 970
/// Parse a block of statements enclosed in curly braces.
970 -
export fn parseBlock(p: *mut Parser) -> *ast::Node
971 +
export unsafe fn parseBlock(p: &mut Parser) -> *ast::Node
971 972
    throws (ParseError)
972 973
{
973 974
    let start = p.current;
974 975
    let mut blk = mkBlock(p, 64);
975 976
981 982
982 983
    return node(p, ast::NodeValue::Block(blk));
983 984
}
984 985
985 986
/// Create an empty block with no statements.
986 -
fn mkBlock(p: *mut Parser, cap: u32) -> ast::Block {
987 -
    return ast::Block { statements: ast::nodeSlice(p.arena, cap) };
987 +
unsafe fn mkBlock(p: &mut Parser, cap: u32) -> ast::Block {
988 +
    return ast::Block { statements: ast::nodeSlice(&mut *p.arena, cap) };
988 989
}
989 990
990 991
/// Create a block containing a single statement node.
991 -
fn mkBlockWith(p: *mut Parser, node: *ast::Node) -> ast::Block {
992 -
    let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator);
992 +
unsafe fn mkBlockWith(p: &mut Parser, node: *ast::Node) -> ast::Block {
993 +
    let stmts = ast::nodeSlice(&mut *p.arena, 1).append(node, p.allocator);
993 994
    return ast::Block { statements: stmts };
994 995
}
995 996
996 997
/// Parse the branch that follows `else` in let-else style constructs.
997 998
///
998 999
/// Allows either a block, a single statement like `return`,
999 1000
/// or a standalone expression which is returned directly.
1000 -
fn parseLetElseBranch(p: *mut Parser) -> *ast::Node
1001 +
unsafe fn parseLetElseBranch(p: &mut Parser) -> *ast::Node
1001 1002
    throws (ParseError)
1002 1003
{
1003 1004
    if check(p, scanner::TokenKind::LBrace) {
1004 1005
        return try parseBlock(p);
1005 1006
    }
1010 1011
    }
1011 1012
    return branch;
1012 1013
}
1013 1014
1014 1015
/// Allocate a new node from the parser's arena.
1015 -
fn node(p: *mut Parser, value: ast::NodeValue) -> *mut ast::Node {
1016 +
unsafe fn node(p: &mut Parser, value: ast::NodeValue) -> *mut ast::Node {
1016 1017
    let span = ast::Span {
1017 1018
        offset: p.previous.offset,
1018 1019
        length: p.previous.source.len,
1019 1020
    };
1020 -
    let n = ast::allocNode(p.arena, span, value);
1021 +
    let n = ast::allocNode(&mut *p.arena, span, value);
1021 1022
    finishSpan(p, n);
1022 1023
1023 1024
    return n;
1024 1025
}
1025 1026
1026 1027
/// Update the span of `node` using the most recently consumed token.
1027 -
fn finishSpan(p: *mut Parser, node: *mut ast::Node) {
1028 +
fn finishSpan(p: &mut Parser, node: *mut ast::Node) {
1028 1029
    let start: u32 = node.span.offset;
1029 1030
    let mut end: u32 = p.previous.offset + p.previous.source.len;
1030 1031
1031 1032
    if end >= start {
1032 1033
        set node.span.length = end - start;
1034 1035
        set node.span.length = 0;
1035 1036
    }
1036 1037
}
1037 1038
1038 1039
/// Save parser state for speculative parsing.
1039 -
fn saveState(p: *Parser) -> SavedState {
1040 +
unsafe fn saveState(p: &Parser) -> SavedState {
1040 1041
    return SavedState {
1041 1042
        parser: *p,
1042 1043
        arena: alloc::save(&p.arena.arena),
1043 1044
        nextId: p.arena.nextId,
1044 1045
    };
1045 1046
}
1046 1047
1047 1048
/// Restore parser state from a snapshot, fully undoing any
1048 1049
/// side effects of a failed speculative parse.
1049 -
fn restoreState(p: *mut Parser, s: *SavedState) {
1050 +
unsafe fn restoreState(p: &mut Parser, s: &SavedState) {
1050 1051
    set *p = s.parser;
1051 1052
    alloc::restore(&mut p.arena.arena, s.arena);
1052 1053
    set p.arena.nextId = s.nextId;
1053 1054
}
1054 1055
1055 1056
/// Report a parser error.
1056 -
fn reportError(p: *mut Parser, token: scanner::Token, message: *[u8]) {
1057 +
fn reportError(p: &mut Parser, token: scanner::Token, message: *[u8]) {
1057 1058
    assert message.len > 0;
1058 1059
1059 1060
    // Ignore errors once the error list is full.
1060 1061
    if p.errors.count < p.errors.list.len {
1061 1062
        set p.errors.list[p.errors.count] = Error { message, token };
1062 1063
        set p.errors.count += 1;
1063 1064
    }
1064 1065
}
1065 1066
1066 1067
/// Fail the parsing process with the given error.
1067 -
fn failParsing(p: *mut Parser, err: *[u8]) -> ParseError {
1068 -
    reportError(p, p.current, err);
1068 +
fn failParsing(p: &mut Parser, err: *[u8]) -> ParseError {
1069 +
    let token = p.current;
1070 +
    reportError(p, token, err);
1069 1071
    return ParseError::UnexpectedToken;
1070 1072
}
1071 1073
1072 1074
/// Print all errors that have been collected during parsing.
1073 -
export fn printErrors(p: *Parser) {
1075 +
export fn printErrors(p: &Parser) {
1074 1076
    for i in 0..p.errors.count {
1075 1077
        let e = p.errors.list[i];
1076 1078
        if let loc = scanner::getLocation(
1077 1079
            p.scanner.sourceLoc, p.scanner.source, e.token.offset
1078 1080
        ) {
1099 1101
        io::print("\n");
1100 1102
    }
1101 1103
}
1102 1104
1103 1105
/// Check whether the current token matches the expected kind.
1104 -
export fn check(p: *Parser, kind: scanner::TokenKind) -> bool {
1106 +
export fn check(p: &Parser, kind: scanner::TokenKind) -> bool {
1105 1107
    return p.current.kind == kind;
1106 1108
}
1107 1109
1108 1110
/// Advance the parser by one token.
1109 -
export fn advance(p: *mut Parser) {
1111 +
export fn advance(p: &mut Parser) {
1110 1112
    set p.previous = p.current;
1111 1113
    set p.current = scanner::next(&mut p.scanner);
1112 1114
}
1113 1115
1114 1116
/// Parse an `if let` pattern matching statement.
1115 1117
///
1116 1118
/// Syntax: `if let binding = scrutinee { ... }`
1117 1119
/// Syntax: `if let mut binding = scrutinee { ... }`
1118 -
fn parseIfLet(p: *mut Parser) -> *ast::Node throws (ParseError) {
1120 +
unsafe fn parseIfLet(p: &mut Parser) -> *ast::Node throws (ParseError) {
1119 1121
    try expect(p, scanner::TokenKind::Let, "expected `let`");
1120 1122
1121 1123
    // Parse pattern: either `case <pattern>`, `mut <ident>`, or simple `<ident>`.
1122 1124
    let mut pattern: *ast::Node = undefined;
1123 1125
    let mut kind = ast::PatternKind::Binding;
1157 1159
        elseBranch,
1158 1160
    }));
1159 1161
}
1160 1162
1161 1163
/// Parse a `while let` statement.
1162 -
fn parseWhileLet(p: *mut Parser) -> *ast::Node
1164 +
unsafe fn parseWhileLet(p: &mut Parser) -> *ast::Node
1163 1165
    throws (ParseError)
1164 1166
{
1165 1167
    try expect(p, scanner::TokenKind::Let, "expected `let`");
1166 1168
1167 1169
    // Parse pattern: either `case <pattern>`, `mut <ident>`, or simple `<ident>`.
1195 1197
        elseBranch,
1196 1198
    }));
1197 1199
}
1198 1200
1199 1201
/// Parse a `while` statement.
1200 -
fn parseWhile(p: *mut Parser) -> *ast::Node
1202 +
unsafe fn parseWhile(p: &mut Parser) -> *ast::Node
1201 1203
    throws (ParseError)
1202 1204
{
1203 1205
    try expect(p, scanner::TokenKind::While, "expected `while`");
1204 1206
1205 1207
    // Check for `while let` or `while let case` syntax.
1217 1219
        condition, body, elseBranch,
1218 1220
    }));
1219 1221
}
1220 1222
1221 1223
/// Parse a `loop` statement.
1222 -
fn parseLoop(p: *mut Parser) -> *ast::Node
1224 +
unsafe fn parseLoop(p: &mut Parser) -> *ast::Node
1223 1225
    throws (ParseError)
1224 1226
{
1225 1227
    try expect(p, scanner::TokenKind::Loop, "expected `loop`");
1226 1228
    let body = try parseBlock(p);
1227 1229
1228 1230
    return node(p, ast::NodeValue::Loop { body });
1229 1231
}
1230 1232
1231 1233
/// Parse a `for` statement.
1232 -
fn parseFor(p: *mut Parser) -> *ast::Node
1234 +
unsafe fn parseFor(p: &mut Parser) -> *ast::Node
1233 1235
    throws (ParseError)
1234 1236
{
1235 1237
    try expect(p, scanner::TokenKind::For, "expected `for`");
1236 1238
1237 1239
    let binding = try parseIdentOrPlaceholder(p, "expected identifier or `_`");
1253 1255
        binding, index, iterable, body, elseBranch,
1254 1256
    }));
1255 1257
}
1256 1258
1257 1259
/// Parse a `return` statement.
1258 -
fn parseReturn(p: *mut Parser) -> *ast::Node
1260 +
unsafe fn parseReturn(p: &mut Parser) -> *ast::Node
1259 1261
    throws (ParseError)
1260 1262
{
1261 1263
    try expect(p, scanner::TokenKind::Return, "expected `return`");
1262 1264
1263 1265
    // Speculatively try to parse a return value expression.
1268 1270
    }
1269 1271
    return node(p, ast::NodeValue::Return { value });
1270 1272
}
1271 1273
1272 1274
/// Parse a `throw` statement.
1273 -
fn parseThrow(p: *mut Parser) -> *ast::Node
1275 +
unsafe fn parseThrow(p: &mut Parser) -> *ast::Node
1274 1276
    throws (ParseError)
1275 1277
{
1276 1278
    try expect(p, scanner::TokenKind::Throw, "expected `throw`");
1277 1279
    let expr = try parseExpr(p);
1278 1280
1279 1281
    return node(p, ast::NodeValue::Throw { expr });
1280 1282
}
1281 1283
1282 1284
/// Parse a `panic` statement.
1283 -
fn parsePanic(p: *mut Parser) -> *ast::Node
1285 +
unsafe fn parsePanic(p: &mut Parser) -> *ast::Node
1284 1286
    throws (ParseError)
1285 1287
{
1286 1288
    try expect(p, scanner::TokenKind::Panic, "expected `panic`");
1287 1289
1288 1290
    // `panic { expr }`.
1304 1306
///
1305 1307
/// Forms:
1306 1308
///   `assert <expr>`
1307 1309
///   `assert <expr>, "message"`
1308 1310
///   `assert { <expr> }, "message"`
1309 -
fn parseAssert(p: *mut Parser) -> *ast::Node
1311 +
unsafe fn parseAssert(p: &mut Parser) -> *ast::Node
1310 1312
    throws (ParseError)
1311 1313
{
1312 1314
    try expect(p, scanner::TokenKind::Assert, "expected `assert`");
1313 1315
1314 1316
    // `assert { expr }` block form or `assert <expr>`.
1325 1327
    }
1326 1328
    return node(p, ast::NodeValue::Assert { condition, message });
1327 1329
}
1328 1330
1329 1331
/// Parse a `try` expression with optional `catch` clause(s).
1330 -
fn parseTryExpr(p: *mut Parser) -> *ast::Node
1332 +
unsafe fn parseTryExpr(p: &mut Parser) -> *ast::Node
1331 1333
    throws (ParseError)
1332 1334
{
1333 1335
    try expect(p, scanner::TokenKind::Try, "expected `try`");
1334 1336
1335 1337
    let shouldPanic = consume(p, scanner::TokenKind::Bang);
1336 1338
    let returnsOptional = consume(p, scanner::TokenKind::Question);
1337 1339
    let expr = try parseUnaryExpr(p);
1338 -
    let mut catches = ast::nodeSlice(p.arena, 4);
1340 +
    let mut catches = ast::nodeSlice(&mut *p.arena, 4);
1339 1341
1340 1342
    while consume(p, scanner::TokenKind::Catch) {
1341 1343
        let mut binding: ?*ast::Node = nil;
1342 1344
        let mut typeNode: ?*ast::Node = nil;
1343 1345
1398 1400
///               d
1399 1401
///           }
1400 1402
///       }
1401 1403
///   }
1402 1404
///
1403 -
fn parseIf(p: *mut Parser) -> *ast::Node throws (ParseError) {
1405 +
unsafe fn parseIf(p: &mut Parser) -> *ast::Node throws (ParseError) {
1404 1406
    try expect(p, scanner::TokenKind::If, "expected `if`");
1405 1407
1406 1408
    // Check for `if let` or `if let case` syntax.
1407 1409
    if check(p, scanner::TokenKind::Let) {
1408 1410
        return try parseIfLet(p);
1428 1430
        condition: cond, thenBranch, elseBranch,
1429 1431
    }));
1430 1432
}
1431 1433
1432 1434
/// Parse a `match` statement.
1433 -
fn parseMatch(p: *mut Parser) -> *ast::Node
1435 +
unsafe fn parseMatch(p: &mut Parser) -> *ast::Node
1434 1436
    throws (ParseError)
1435 1437
{
1436 1438
    try expect(p, scanner::TokenKind::Match, "expected `match`");
1437 1439
1438 1440
    let subject = try parseCond(p);
1439 1441
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs");
1440 1442
1441 -
    let mut prongs = ast::nodeSlice(p.arena, 128);
1443 +
    let mut prongs = ast::nodeSlice(&mut *p.arena, 128);
1442 1444
    while not check(p, scanner::TokenKind::RBrace) and
1443 1445
          not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF.
1444 1446
    {
1445 1447
        let prongNode = try parseMatchProng(p);
1446 1448
        prongs.append(prongNode, p.allocator);
1452 1454
        ast::Match { subject, prongs }
1453 1455
    ));
1454 1456
}
1455 1457
1456 1458
/// Parse a single `match` prong.
1457 -
fn parseMatchProng(p: *mut Parser) -> *ast::Node
1459 +
unsafe fn parseMatchProng(p: &mut Parser) -> *ast::Node
1458 1460
    throws (ParseError)
1459 1461
{
1460 1462
    let mut guard: ?*ast::Node = nil;
1461 1463
1462 1464
    // Case prong: `case <pattern>, ... if <guard> => <body>`.
1463 1465
    if consume(p, scanner::TokenKind::Case) {
1464 -
        let mut patterns = ast::nodeSlice(p.arena, 16);
1466 +
        let mut patterns = ast::nodeSlice(&mut *p.arena, 16);
1465 1467
        loop {
1466 1468
            let pattern = try parseMatchPattern(p);
1467 1469
            patterns.append(pattern, p.allocator);
1468 1470
1469 1471
            if not consume(p, scanner::TokenKind::Comma) {
1511 1513
    ));
1512 1514
}
1513 1515
1514 1516
/// Parse a pattern expression used by `case` constructs.
1515 1517
/// Uses `Pattern` context to allow record literals but not conditional expressions.
1516 -
fn parseMatchPattern(p: *mut Parser) -> *ast::Node
1518 +
unsafe fn parseMatchPattern(p: &mut Parser) -> *ast::Node
1517 1519
    throws (ParseError)
1518 1520
{
1519 1521
    let saved = p.context;
1520 1522
    set p.context = Context::Pattern;
1521 1523
    let pattern = try parseExpr(p);
1523 1525
1524 1526
    return pattern;
1525 1527
}
1526 1528
1527 1529
/// Parse an identifier.
1528 -
fn parseIdent(p: *mut Parser, err: *[u8]) -> *ast::Node
1530 +
unsafe fn parseIdent(p: &mut Parser, err: *[u8]) -> *ast::Node
1529 1531
    throws (ParseError)
1530 1532
{
1531 1533
    let source = try expect(p, scanner::TokenKind::Ident, err);
1532 1534
    return node(p, ast::NodeValue::Ident(source));
1533 1535
}
1534 1536
1535 1537
/// Parse either an identifier or a placeholder (`_`).
1536 -
fn parseIdentOrPlaceholder(p: *mut Parser, err: *[u8]) -> *ast::Node
1538 +
unsafe fn parseIdentOrPlaceholder(p: &mut Parser, err: *[u8]) -> *ast::Node
1537 1539
    throws (ParseError)
1538 1540
{
1539 1541
    if consume(p, scanner::TokenKind::Underscore) {
1540 1542
        return node(p, ast::NodeValue::Placeholder);
1541 1543
    }
1543 1545
}
1544 1546
1545 1547
/// Parse an alignment specifier.
1546 1548
///
1547 1549
/// Syntax: `align(N)` where N is a power of 2.
1548 -
fn parseAlign(p: *mut Parser) -> *ast::Node
1550 +
unsafe fn parseAlign(p: &mut Parser) -> *ast::Node
1549 1551
    throws (ParseError)
1550 1552
{
1551 1553
    try expect(p, scanner::TokenKind::Align, "expected `align`");
1552 1554
    let value = try parseParenthesized(p);
1553 1555
    return node(p, ast::NodeValue::Align { value });
1555 1557
1556 1558
/// Parse a comma-separated list of record fields.
1557 1559
/// The opening delimiter should already be consumed.
1558 1560
/// For labeled fields: `{ name: T, ... }`.
1559 1561
/// For unlabeled fields: `(T, T, ...)`.
1560 -
fn parseRecordFields(
1561 -
    p: *mut Parser,
1562 +
unsafe fn parseRecordFields(
1563 +
    p: &mut Parser,
1562 1564
    mode: RecordFieldMode
1563 1565
) -> *mut [*ast::Node]
1564 1566
    throws (ParseError)
1565 1567
{
1566 1568
    let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled
1567 1569
        else scanner::TokenKind::RParen;
1568 -
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1570 +
    let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS);
1569 1571
    while not check(p, terminator) {
1570 1572
        let mut recordField: ast::NodeValue = undefined;
1571 1573
        match mode {
1572 1574
            case RecordFieldMode::Labeled => {
1573 1575
                // Allow optional `let` keyword before field name.
1604 1606
1605 1607
    return fields;
1606 1608
}
1607 1609
1608 1610
/// Parse an optional derives list (`: Trait + Trait`).
1609 -
fn parseDerives(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1610 -
    let mut derives = ast::nodeSlice(p.arena, 4);
1611 +
unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) {
1612 +
    let mut derives = ast::nodeSlice(&mut *p.arena, 4);
1611 1613
1612 1614
    if not consume(p, scanner::TokenKind::Colon) {
1613 1615
        return derives;
1614 1616
    }
1615 1617
    loop {
1623 1625
    return derives;
1624 1626
}
1625 1627
1626 1628
/// Parse a single record literal field.
1627 1629
/// Can be either labeled, or shorthand.
1628 -
fn parseRecordLitField(p: *mut Parser) -> *ast::Node
1630 +
unsafe fn parseRecordLitField(p: &mut Parser) -> *ast::Node
1629 1631
    throws (ParseError)
1630 1632
{
1631 1633
    let name = try parseIdent(p, "expected field name");
1632 1634
    if consume(p, scanner::TokenKind::Colon) {
1633 1635
        // Labeled field: `name: value`.
1643 1645
}
1644 1646
1645 1647
/// Parse a record literal body.
1646 1648
/// Eg. `{ x: 1, y: 2 }`
1647 1649
/// Eg. `{ x: 1, .. }`
1648 -
fn parseRecordLit(p: *mut Parser, typeName: ?*ast::Node) -> *ast::Node
1650 +
unsafe fn parseRecordLit(p: &mut Parser, typeName: ?*ast::Node) -> *ast::Node
1649 1651
    throws (ParseError)
1650 1652
{
1651 -
    let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS);
1653 +
    let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS);
1652 1654
    let mut ignoreRest = false;
1653 1655
    try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal");
1654 1656
1655 1657
    while not check(p, scanner::TokenKind::RBrace) {
1656 1658
        // Check for `..` to ignore remaining fields.
1672 1674
    ));
1673 1675
}
1674 1676
1675 1677
/// Parse a named record declaration.
1676 1678
/// `record Point { x: i32, y: i32 }`, or `record Pair(i32, i32);`
1677 -
fn parseRecordDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1679 +
unsafe fn parseRecordDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1678 1680
    throws (ParseError)
1679 1681
{
1680 1682
    try expect(p, scanner::TokenKind::Record, "expected `record`");
1681 1683
1682 1684
    let name = try parseIdent(p, "expected record name");
1697 1699
    }
1698 1700
}
1699 1701
1700 1702
/// Parse a union declaration.
1701 1703
/// Example: `union Color { Red, Green, Blue = 5 }`
1702 -
fn parseUnionDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1704 +
unsafe fn parseUnionDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1703 1705
    throws (ParseError)
1704 1706
{
1705 1707
    try expect(p, scanner::TokenKind::Union, "expected `union`");
1706 1708
1707 1709
    let name = try parseIdent(p, "expected union name");
1708 1710
    let derives = try parseDerives(p);
1709 1711
1710 1712
    try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body");
1711 1713
1712 -
    let mut variants = ast::nodeSlice(p.arena, 128);
1714 +
    let mut variants = ast::nodeSlice(&mut *p.arena, 128);
1713 1715
    while not check(p, scanner::TokenKind::RBrace) {
1714 1716
        // Allow optional `case` keyword before variant name.
1715 1717
        consume(p, scanner::TokenKind::Case);
1716 1718
1717 1719
        let variantName = try parseIdent(p, "expected variant name");
1729 1731
                ast::TypeSig::Record { fields, labeled: true }
1730 1732
            ));
1731 1733
        } else if consume(p, scanner::TokenKind::Equal) {
1732 1734
            // TODO: Support constant expressions.
1733 1735
            try expect(p, scanner::TokenKind::Number, "expected integer literal after `=`");
1734 -
            let literal = try parseIntLiteral(p, p.previous.source);
1736 +
            let source = p.previous.source;
1737 +
            let literal = try parseIntLiteral(p, source);
1735 1738
            set explicitValue = nodeNumber(p, literal);
1736 1739
        }
1737 1740
1738 1741
        let variant = node(p, ast::NodeValue::UnionDeclVariant(
1739 1742
            ast::UnionDeclVariant {
1752 1755
        ast::UnionDecl { name, variants, attrs, derives }
1753 1756
    ));
1754 1757
}
1755 1758
1756 1759
/// Parse a function parameter.
1757 -
fn parseFnParam(p: *mut Parser) -> *ast::Node
1760 +
unsafe fn parseFnParam(p: &mut Parser) -> *ast::Node
1758 1761
    throws (ParseError)
1759 1762
{
1760 1763
    let ntv = try parseNameTypeValue(p);
1761 1764
    let type = ntv.type
1762 1765
        else throw failParsing(p, "missing type in function parameter");
1765 1768
        ast::FnParam { name: ntv.name, type }
1766 1769
    ));
1767 1770
}
1768 1771
1769 1772
/// Parse an optional `throws` clause and return the collected type list.
1770 -
fn parseThrowList(p: *mut Parser) -> *mut [*ast::Node]
1773 +
unsafe fn parseThrowList(p: &mut Parser) -> *mut [*ast::Node]
1771 1774
    throws (ParseError)
1772 1775
{
1773 1776
    if not consume(p, scanner::TokenKind::Throws) {
1774 -
        return ast::nodeSlice(p.arena, 0);
1777 +
        return ast::nodeSlice(&mut *p.arena, 0);
1775 1778
    }
1776 1779
    return try parseList(
1777 1780
        p,
1778 1781
        scanner::TokenKind::LParen,
1779 1782
        scanner::TokenKind::RParen,
1780 1783
        parseType
1781 1784
    );
1782 1785
}
1783 1786
1784 1787
/// Parse a function type signature.
1785 -
fn parseFnType(p: *mut Parser) -> *ast::Node
1788 +
unsafe fn parseFnType(p: &mut Parser) -> *ast::Node
1786 1789
    throws (ParseError)
1787 1790
{
1791 +
    let isUnsafe = consume(p, scanner::TokenKind::Unsafe);
1788 1792
    try expect(p, scanner::TokenKind::Fn, "expected `fn`");
1789 1793
    let params = try parseList(
1790 1794
        p,
1791 1795
        scanner::TokenKind::LParen,
1792 1796
        scanner::TokenKind::RParen,
1798 1802
        set returnType = try parseType(p);
1799 1803
    }
1800 1804
    let throwList = try parseThrowList(p);
1801 1805
    let sig = ast::FnSig { params, returnType, throwList };
1802 1806
    return node(p, ast::NodeValue::TypeSig(
1803 -
        ast::TypeSig::Fn(sig)
1807 +
        ast::TypeSig::Fn { sig, isUnsafe }
1804 1808
    ));
1805 1809
}
1806 1810
1807 1811
/// Parse a function signature following the function name.
1808 -
fn parseFnTypeSig(p: *mut Parser) -> ast::FnSig
1812 +
unsafe fn parseFnTypeSig(p: &mut Parser) -> ast::FnSig
1809 1813
    throws (ParseError)
1810 1814
{
1811 1815
    try expect(p, scanner::TokenKind::LParen, "expected `(` after function name");
1812 -
    let mut params = ast::nodeSlice(p.arena, 8);
1816 +
    let mut params = ast::nodeSlice(&mut *p.arena, 8);
1813 1817
1814 1818
    while not check(p, scanner::TokenKind::RParen) {
1815 1819
        let param = try parseFnParam(p);
1816 1820
        params.append(param, p.allocator);
1817 1821
1829 1833
1830 1834
    return ast::FnSig { params, returnType, throwList };
1831 1835
}
1832 1836
1833 1837
/// Parse a function declaration.
1834 -
fn parseFnDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1838 +
unsafe fn parseFnDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1835 1839
    throws (ParseError)
1836 1840
{
1837 1841
    try expect(p, scanner::TokenKind::Fn, "expected `fn`");
1838 1842
1839 1843
    // Method syntax: `fn (recv: *Type) name(params) { body }`.
1847 1851
1848 1852
    if consume(p, scanner::TokenKind::Semicolon) {
1849 1853
        if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) {
1850 1854
            // Keep existing attributes unchanged.
1851 1855
        } else {
1852 -
            let mut list = ast::nodeSlice(p.arena, 4);
1856 +
            let mut list = ast::nodeSlice(&mut *p.arena, 4);
1853 1857
            if let a = attrs {
1854 1858
                for i in 0..a.list.len {
1855 1859
                    list.append(a.list[i], p.allocator);
1856 1860
                }
1857 1861
            }
1866 1870
        ast::FnDecl { name, sig, body, attrs: fnAttrs }
1867 1871
    ));
1868 1872
}
1869 1873
1870 1874
/// Parse a pointer-like type after its ownership prefix.
1871 -
fn parsePointerLikeType(
1872 -
    p: *mut Parser,
1875 +
unsafe fn parsePointerLikeType(
1876 +
    p: &mut Parser,
1873 1877
    class: types::PointerClass,
1874 1878
) -> *ast::Node throws (ParseError) {
1875 1879
    let mutable = consume(p, scanner::TokenKind::Mut);
1876 1880
1877 1881
    if consume(p, scanner::TokenKind::LBracket) {
1902 1906
        ast::TypeSig::Pointer { class, valueType, mutable }
1903 1907
    ));
1904 1908
}
1905 1909
1906 1910
/// Parse an array type.
1907 -
fn parseArrayType(p: *mut Parser) -> *ast::Node
1911 +
unsafe fn parseArrayType(p: &mut Parser) -> *ast::Node
1908 1912
    throws (ParseError)
1909 1913
{
1910 1914
    try expect(p, scanner::TokenKind::LBracket, "expected `[`");
1911 1915
    let itemType = try parseType(p);
1912 1916
1919 1923
    ));
1920 1924
}
1921 1925
1922 1926
/// Parse a type path: an identifier optionally followed by `::` scope access.
1923 1927
/// Returns an identifier node or a scope access chain.
1924 -
fn parseTypePath(p: *mut Parser) -> *ast::Node
1928 +
unsafe fn parseTypePath(p: &mut Parser) -> *ast::Node
1925 1929
    throws (ParseError)
1926 1930
{
1927 1931
    let mut path: *ast::Node = undefined;
1928 1932
    if p.current.kind == scanner::TokenKind::Super {
1929 1933
        advance(p);
1939 1943
    }
1940 1944
    return path;
1941 1945
}
1942 1946
1943 1947
/// Parse a type annotation.
1944 -
export fn parseType(p: *mut Parser) -> *ast::Node
1948 +
export unsafe fn parseType(p: &mut Parser) -> *ast::Node
1945 1949
    throws (ParseError)
1946 1950
{
1947 1951
    match p.current.kind {
1948 1952
        case scanner::TokenKind::Question => {
1949 1953
            advance(p);
2012 2016
        }
2013 2017
        case scanner::TokenKind::Opaque => {
2014 2018
            advance(p);
2015 2019
            return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Opaque));
2016 2020
        }
2017 -
        case scanner::TokenKind::Fn => {
2021 +
        case scanner::TokenKind::Fn, scanner::TokenKind::Unsafe => {
2018 2022
            return try parseFnType(p);
2019 2023
        }
2020 2024
        else => {
2021 2025
            throw failParsing(p, "expected type");
2022 2026
        }
2025 2029
2026 2030
/// Parse a name, optional type, and optional value.
2027 2031
///
2028 2032
/// Used for record field declarations, variable declarations,
2029 2033
/// and record field initializations.
2030 -
fn parseNameTypeValue(p: *mut Parser) -> NameTypeValue
2034 +
unsafe fn parseNameTypeValue(p: &mut Parser) -> NameTypeValue
2031 2035
    throws (ParseError)
2032 2036
{
2033 2037
    let name = try parseIdentOrPlaceholder(p, "expected identifier or `_`");
2034 2038
    let mut type: ?*ast::Node = nil;
2035 2039
    let mut alignment: ?*ast::Node = nil;
2047 2051
    }
2048 2052
    return NameTypeValue { name, type, value, alignment };
2049 2053
}
2050 2054
2051 2055
/// Parse a constant declaration.
2052 -
fn parseConst(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2056 +
unsafe fn parseConst(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2053 2057
    throws (ParseError)
2054 2058
{
2055 2059
    try expect(p, scanner::TokenKind::Constant, "expected `constant`");
2056 2060
2057 2061
    let ident = try parseIdent(p, "expected identifier in constant declaration");
2066 2070
        ast::ConstDecl { ident, type, value, attrs }
2067 2071
    ));
2068 2072
}
2069 2073
2070 2074
/// Parse a static declaration.
2071 -
fn parseStatic(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2075 +
unsafe fn parseStatic(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2072 2076
    throws (ParseError)
2073 2077
{
2074 2078
    try expect(p, scanner::TokenKind::Static, "expected `static`");
2075 2079
2076 2080
    let ident = try parseIdent(p, "expected identifier in static declaration");
2085 2089
        ast::StaticDecl { ident, type, value, attrs }
2086 2090
    ));
2087 2091
}
2088 2092
2089 2093
/// Parse a `use` declaration.
2090 -
fn parseUse(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2094 +
unsafe fn parseUse(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2091 2095
    throws (ParseError)
2092 2096
{
2093 2097
    try expect(p, scanner::TokenKind::Use, "expected `use`");
2094 2098
2095 2099
    // Allow `super` or identifier as the first part of the path.
2115 2119
        ast::Use { path, wildcard: false, attrs }
2116 2120
    ));
2117 2121
}
2118 2122
2119 2123
/// Parse a `mod` declaration.
2120 -
fn parseMod(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2124 +
unsafe fn parseMod(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2121 2125
    throws (ParseError)
2122 2126
{
2123 2127
    try expect(p, scanner::TokenKind::Mod, "expected `mod`");
2124 2128
    let name = try parseIdent(p, "expected module name after `mod`");
2125 2129
2132 2136
///
2133 2137
/// Eg. `let case <pattern> = <expr> else { ... };`
2134 2138
/// Eg. `let case <pattern> = <expr> if <guard> else { ... };`
2135 2139
///
2136 2140
/// Expects `let case` tokens to have already been consumed.
2137 -
fn parseLetCase(p: *mut Parser) -> *ast::Node throws (ParseError) {
2141 +
unsafe fn parseLetCase(p: &mut Parser) -> *ast::Node throws (ParseError) {
2138 2142
    let pattern = try parseMatchPattern(p);
2139 2143
2140 2144
    try expect(p, scanner::TokenKind::Equal, "expected `=` after pattern");
2141 2145
    let expr = try parseCond(p);
2142 2146
2161 2165
/// Eg. `let mut <ident> = <expr> else { ... };`
2162 2166
/// Eg. `let <ident> = <expr> if <guard> else { ... };`
2163 2167
/// Eg. `mut <ident> = <expr>;`
2164 2168
///
2165 2169
/// Expects `let` or `mut` token to have already been consumed.
2166 -
fn parseLet(p: *mut Parser, mutable: bool) -> *ast::Node throws (ParseError) {
2170 +
unsafe fn parseLet(p: &mut Parser, mutable: bool) -> *ast::Node throws (ParseError) {
2167 2171
    let binding = try parseNameTypeValue(p);
2168 2172
    let value = binding.value
2169 2173
        else throw failParsing(p, "expected value initializer");
2170 2174
2171 2175
    // Check for optional `else` clause (let-else).
2181 2185
        ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable,
2182 2186
    }));
2183 2187
}
2184 2188
2185 2189
/// Parse a module from source text using the provided arena for node storage.
2186 -
export fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node
2190 +
export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node
2187 2191
    throws (ParseError)
2188 2192
{
2189 2193
    let mut p = mkParser(sourceLoc, input, arena, pool);
2190 2194
    return try parseModule(&mut p) catch {
2191 2195
        printErrors(&p);
2195 2199
2196 2200
/// Parse a complete module into a block of top-level statements.
2197 2201
///
2198 2202
/// This is the main entry point for parsing an entire Radiance source file.
2199 2203
/// The parser must already be initialized with source code.
2200 -
export fn parseModule(p: *mut Parser) -> *mut ast::Node
2204 +
export unsafe fn parseModule(p: &mut Parser) -> *mut ast::Node
2201 2205
    throws (ParseError)
2202 2206
{
2203 2207
    advance(p); // Set the parser up with a first token.
2204 2208
2205 2209
    let mut blk = mkBlock(p, 512);
2208 2212
2209 2213
    return node(p, ast::NodeValue::Block(blk));
2210 2214
}
2211 2215
2212 2216
/// Consume a token of the given kind if present.
2213 -
export fn consume(p: *mut Parser, kind: scanner::TokenKind) -> bool {
2217 +
export fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool {
2214 2218
    if check(p, kind) {
2215 2219
        advance(p);
2216 2220
        return true;
2217 2221
    }
2218 2222
    return false;
2219 2223
}
2220 2224
2221 2225
/// Expect a token of the given kind or report an error.
2222 -
export fn expect(p: *mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8]
2226 +
export fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8]
2223 2227
    throws (ParseError)
2224 2228
{
2225 2229
    if not consume(p, kind) {
2226 -
        reportError(p, p.current, message);
2230 +
        let token = p.current;
2231 +
        reportError(p, token, message);
2227 2232
        throw ParseError::UnexpectedToken;
2228 2233
    }
2229 2234
    return p.previous.source;
2230 2235
}
2231 2236
2242 2247
    }
2243 2248
}
2244 2249
2245 2250
/// Parse a trait declaration.
2246 2251
/// Syntax: `trait Name { fn (*Trait) method(...) -> T; ... }`
2247 -
fn parseTraitDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2252 +
unsafe fn parseTraitDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2248 2253
    throws (ParseError)
2249 2254
{
2250 2255
    try expect(p, scanner::TokenKind::Trait, "expected `trait`");
2251 2256
    let name = try parseIdent(p, "expected trait name");
2252 2257
    let supertraits = try parseDerives(p);
2253 2258
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name");
2254 2259
2255 -
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2260 +
    let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS);
2256 2261
    while not check(p, scanner::TokenKind::RBrace) and
2257 2262
          not check(p, scanner::TokenKind::Eof)
2258 2263
    {
2259 2264
        let method = try parseTraitMethodSig(p);
2260 2265
        methods.append(method, p.allocator);
2264 2269
    return node(p, ast::NodeValue::TraitDecl { name, supertraits, methods, attrs });
2265 2270
}
2266 2271
2267 2272
/// Parse a trait method signature.
2268 2273
/// Syntax: `fn (*Trait) fnord(<params>) -> ReturnType;`
2269 -
fn parseTraitMethodSig(p: *mut Parser) -> *ast::Node
2274 +
unsafe fn parseTraitMethodSig(p: &mut Parser) -> *ast::Node
2270 2275
    throws (ParseError)
2271 2276
{
2272 2277
    let attrs = parseAttributes(p);
2273 2278
    try expect(p, scanner::TokenKind::Fn, "expected `fn`");
2274 2279
    try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver");
2287 2292
/// Parse an instance block.
2288 2293
/// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }`
2289 2294
///
2290 2295
/// Instance declarations do not accept attributes (e.g. `export`).
2291 2296
/// Visibility is determined by the trait declaration itself.
2292 -
fn parseInstanceDecl(p: *mut Parser) -> *ast::Node
2297 +
unsafe fn parseInstanceDecl(p: &mut Parser) -> *ast::Node
2293 2298
    throws (ParseError)
2294 2299
{
2295 2300
    try expect(p, scanner::TokenKind::Instance, "expected `instance`");
2296 2301
    let traitName = try parseTypePath(p);
2297 2302
    try expect(p, scanner::TokenKind::For, "expected `for` after trait name");
2298 2303
    let targetType = try parseTypePath(p);
2299 2304
    try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type");
2300 2305
2301 -
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2306 +
    let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS);
2302 2307
2303 2308
    while not check(p, scanner::TokenKind::RBrace) and
2304 2309
          not check(p, scanner::TokenKind::Eof)
2305 2310
    {
2306 2311
        let attrs = parseAttributes(p);
2317 2322
/// Parse a method declaration with a receiver.
2318 2323
/// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }`
2319 2324
///
2320 2325
/// Used both inside `instance` blocks and as standalone methods at the top level.
2321 2326
/// Expects the `fn` token to have already been consumed.
2322 -
fn parseMethodDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2327 +
unsafe fn parseMethodDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2323 2328
    throws (ParseError)
2324 2329
{
2325 2330
    try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver");
2326 2331
2327 2332
    let receiverName = try parseIdent(p, "expected receiver name");
2338 2343
        name, receiverName, receiverType, sig, body, attrs,
2339 2344
    });
2340 2345
}
2341 2346
2342 2347
/// Parse a comma-separated list enclosed by the given delimiters.
2343 -
fn parseList(
2344 -
    p: *mut Parser,
2348 +
unsafe fn parseList(
2349 +
    p: &mut Parser,
2345 2350
    open: scanner::TokenKind,
2346 2351
    close: scanner::TokenKind,
2347 -
    parseItem: fn (*mut Parser) -> *ast::Node throws (ParseError)
2352 +
    parseItem: unsafe fn (&mut Parser) -> *ast::Node throws (ParseError)
2348 2353
) -> *mut [*ast::Node] throws (ParseError) {
2349 2354
    try expect(p, open, listExpectMessage(open));
2350 -
    let mut items = ast::nodeSlice(p.arena, 8);
2355 +
    let mut items = ast::nodeSlice(&mut *p.arena, 8);
2351 2356
2352 2357
    while not check(p, close) {
2353 2358
        let item = try parseItem(p);
2354 2359
        items.append(item, p.allocator);
2355 2360
lib/std/lang/parser/tests.rad +182 -177
70 70
        try testing::expect(range.end == nil);
71 71
    }
72 72
}
73 73
74 74
/// Parse multiple statements from a string.
75 -
fn parseStmtsStr(input: *[u8]) -> *ast::Node
75 +
unsafe fn parseStmtsStr(input: *[u8]) -> *ast::Node
76 76
    throws (testing::TestError)
77 77
{
78 78
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
79 79
    let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL);
80 80
    return try super::parseModule(&mut parser) catch {
81 81
        throw testing::TestError::Failed;
82 82
    };
83 83
}
84 84
85 85
/// Parse a single type from a string.
86 -
fn parseTypeStr(input: *[u8]) -> *ast::Node
86 +
unsafe fn parseTypeStr(input: *[u8]) -> *ast::Node
87 87
    throws (super::ParseError)
88 88
{
89 89
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
90 90
    let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL);
91 91
    super::advance(&mut parser);
94 94
95 95
    return root;
96 96
}
97 97
98 98
/// Parse a single expression from a string.
99 -
export fn parseExprStr(input: *[u8]) -> *ast::Node
99 +
export unsafe fn parseExprStr(input: *[u8]) -> *ast::Node
100 100
    throws (super::ParseError)
101 101
{
102 102
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
103 103
    let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL);
104 104
    super::advance(&mut parser);
105 105
    return try super::parseExpr(&mut parser);
106 106
}
107 107
108 108
/// Parse a single statement from a string.
109 -
fn parseStmtStr(input: *[u8]) -> *ast::Node
109 +
unsafe fn parseStmtStr(input: *[u8]) -> *ast::Node
110 110
    throws (super::ParseError)
111 111
{
112 112
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
113 113
    let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL);
114 114
    super::advance(&mut parser);
118 118
119 119
    return root;
120 120
}
121 121
122 122
/// Parse an expression expected to be a number literal and return its payload.
123 -
fn parseNumberLiteral(text: *[u8]) -> fmt::IntLiteral
123 +
unsafe fn parseNumberLiteral(text: *[u8]) -> fmt::IntLiteral
124 124
    throws (testing::TestError)
125 125
{
126 126
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
127 127
    let mut parser = super::mkParser(scanner::SourceLoc::String, text, &mut arena, &mut STRING_POOL);
128 128
    super::advance(&mut parser);
137 137
138 138
    return lit;
139 139
}
140 140
141 141
/// Ensure that parsing the supplied literal source fails.
142 -
fn expectNumberLiteralFail(text: *[u8])
142 +
unsafe fn expectNumberLiteralFail(text: *[u8])
143 143
    throws (testing::TestError)
144 144
{
145 145
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
146 146
    let mut parser = super::mkParser(scanner::SourceLoc::String, text, &mut arena, &mut STRING_POOL);
147 147
    super::advance(&mut parser);
260 260
    }
261 261
    return block.statements[block.statements.len - 1];
262 262
}
263 263
264 264
/// Test parsing boolean literals (`true` and `false`).
265 -
@test fn testParseBool() throws (testing::TestError) {
265 +
@test unsafe fn testParseBool() throws (testing::TestError) {
266 266
    let r1 = try! parseExprStr("true");
267 267
    let case ast::NodeValue::Bool(v1) = r1.value if v1
268 268
        else throw testing::TestError::Failed;
269 269
270 270
    let r2 = try! parseExprStr("false");
271 271
    let case ast::NodeValue::Bool(v2) = r2.value if not v2
272 272
        else throw testing::TestError::Failed;
273 273
}
274 274
275 275
/// Test parsing number literals.
276 -
@test fn testParseNumber() throws (testing::TestError) {
276 +
@test unsafe fn testParseNumber() throws (testing::TestError) {
277 277
    let r1 = try! parseExprStr("4519");
278 278
    try expectNumber(r1, "4519");
279 279
}
280 280
281 281
/// Verify that decimal literals record magnitude and base metadata.
282 -
@test fn testParseDecimalLiteralMetadata() throws (testing::TestError) {
282 +
@test unsafe fn testParseDecimalLiteralMetadata() throws (testing::TestError) {
283 283
    let lit = try parseNumberLiteral("1234");
284 284
    try testing::expect(lit.magnitude == 1234);
285 285
    try testing::expect(lit.radix == fmt::Radix::Decimal);
286 286
}
287 287
288 288
/// Verify that hexadecimal literals record magnitude and radix metadata.
289 -
@test fn testParseNumberMetadata() throws (testing::TestError) {
289 +
@test unsafe fn testParseNumberMetadata() throws (testing::TestError) {
290 290
    let lit = try parseNumberLiteral("0xFF");
291 291
    try testing::expect(lit.magnitude == 0xFF);
292 292
    try testing::expect(lit.radix == fmt::Radix::Hex);
293 293
}
294 294
295 295
/// Verify that binary literals capture their radix.
296 -
@test fn testParseBinaryLiteralMetadata() throws (testing::TestError) {
296 +
@test unsafe fn testParseBinaryLiteralMetadata() throws (testing::TestError) {
297 297
    let lit = try parseNumberLiteral("0b1010");
298 298
    try testing::expect(lit.magnitude == 0b1010);
299 299
    try testing::expect(lit.radix == fmt::Radix::Binary);
300 300
}
301 301
302 302
/// Negative literals parse as unary negation of an unsigned number.
303 -
@test fn testParseNegativeLiteral() throws (testing::TestError) {
303 +
@test unsafe fn testParseNegativeLiteral() throws (testing::TestError) {
304 304
    let node = try! parseExprStr("-99");
305 305
    let case ast::NodeValue::UnOp(neg) = node.value
306 306
        else throw testing::TestError::Failed;
307 307
    try testing::expect(neg.op == ast::UnaryOp::Neg);
308 308
    let case ast::NodeValue::Number(lit) = neg.value.value
309 309
        else throw testing::TestError::Failed;
310 310
    try testing::expect(lit.magnitude == 99);
311 311
}
312 312
313 313
/// Unary plus is not part of the expression grammar.
314 -
@test fn testRejectUnaryPlus() throws (testing::TestError) {
314 +
@test unsafe fn testRejectUnaryPlus() throws (testing::TestError) {
315 315
    try expectNumberLiteralFail("+1");
316 316
    try expectNumberLiteralFail("+value");
317 317
    try expectNumberLiteralFail("+(value)");
318 318
}
319 319
320 320
/// Range expressions parse with explicit start and end bounds.
321 -
@test fn testParseRangeExpr() throws (testing::TestError) {
321 +
@test unsafe fn testParseRangeExpr() throws (testing::TestError) {
322 322
    let node = try! parseExprStr("0..5");
323 323
    try expectRangeNumbers(node, "0", "5");
324 324
}
325 325
326 326
/// Range expressions allow a missing end bound.
327 -
@test fn testParseRangeExprNoEnd() throws (testing::TestError) {
327 +
@test unsafe fn testParseRangeExprNoEnd() throws (testing::TestError) {
328 328
    let node = try! parseExprStr("0..");
329 329
    try expectRangeNumbers(node, "0", nil);
330 330
}
331 331
332 332
/// Range expressions allow a missing start bound.
333 -
@test fn testParseRangeExprNoStart() throws (testing::TestError) {
333 +
@test unsafe fn testParseRangeExprNoStart() throws (testing::TestError) {
334 334
    let node = try! parseExprStr("..5");
335 335
    try expectRangeNumbers(node, nil, "5");
336 336
}
337 337
338 338
/// Literals with out-of-range digits for their base are rejected.
339 -
@test fn testParseInvalidIntLiteral() throws (testing::TestError) {
339 +
@test unsafe fn testParseInvalidIntLiteral() throws (testing::TestError) {
340 340
    // 2^64 overflows u64.
341 341
    try expectNumberLiteralFail("18446744073709551616");
342 342
    try expectNumberLiteralFail("0x10000000000000000");
343 343
    try expectNumberLiteralFail("0x1G");
344 344
    try expectNumberLiteralFail("0b102");
345 345
}
346 346
347 347
/// Test parsing nil literal.
348 -
@test fn testParseNil() throws (testing::TestError) {
348 +
@test unsafe fn testParseNil() throws (testing::TestError) {
349 349
    let r1 = try! parseExprStr("nil");
350 350
    let case ast::NodeValue::Nil = r1.value
351 351
        else throw testing::TestError::Failed;
352 352
}
353 353
354 354
/// Test parsing undefined literal.
355 -
@test fn testParseUndefined() throws (testing::TestError) {
355 +
@test unsafe fn testParseUndefined() throws (testing::TestError) {
356 356
    let r1 = try! parseExprStr("undefined");
357 357
    let case ast::NodeValue::Undef = r1.value
358 358
        else throw testing::TestError::Failed;
359 359
}
360 360
361 361
/// Test parsing character literals.
362 -
@test fn testParseChar() throws (testing::TestError) {
362 +
@test unsafe fn testParseChar() throws (testing::TestError) {
363 363
    let r1 = try! parseExprStr("'a'");
364 364
    let case ast::NodeValue::Char(c1) = r1.value if c1 == 'a'
365 365
        else throw testing::TestError::Failed;
366 366
367 367
    let r2 = try! parseExprStr("'\\n'");
372 372
    let case ast::NodeValue::Char(c3) = r3.value if c3 == '\t'
373 373
        else throw testing::TestError::Failed;
374 374
}
375 375
376 376
/// Test parsing string literals.
377 -
@test fn testParseString() throws (testing::TestError) {
377 +
@test unsafe fn testParseString() throws (testing::TestError) {
378 378
    let r1 = try! parseExprStr("\"hello\"");
379 379
    let case ast::NodeValue::String(s1) = r1.value if mem::eq(s1, "hello")
380 380
        else throw testing::TestError::Failed;
381 381
382 382
    let r2 = try! parseExprStr("\"\"");
383 383
    let case ast::NodeValue::String(s2) = r2.value if s2.len == 0
384 384
        else throw testing::TestError::Failed;
385 385
}
386 386
387 387
/// Test string escape sequence processing.
388 -
@test fn testParseStringEscape() throws (testing::TestError) {
388 +
@test unsafe fn testParseStringEscape() throws (testing::TestError) {
389 389
    // Tab and newline.
390 390
    let r1 = try! parseExprStr("\"hello\\tworld\\n\"");
391 391
    let case ast::NodeValue::String(s1) = r1.value if s1.len == 12
392 392
        else throw testing::TestError::Failed;
393 393
    if s1[5] <> '\t' {
433 433
        throw testing::TestError::Failed;
434 434
    }
435 435
}
436 436
437 437
/// Test parsing placeholder/underscore.
438 -
@test fn testParsePlaceholder() throws (testing::TestError) {
438 +
@test unsafe fn testParsePlaceholder() throws (testing::TestError) {
439 439
    let r1 = try! parseExprStr("_");
440 440
    let case ast::NodeValue::Placeholder = r1.value
441 441
        else throw testing::TestError::Failed;
442 442
}
443 443
444 444
/// Test parsing array literals.
445 -
@test fn testParseArrayLiteral() throws (testing::TestError) {
445 +
@test unsafe fn testParseArrayLiteral() throws (testing::TestError) {
446 446
    let r1 = try! parseExprStr("[]");
447 447
    let case ast::NodeValue::ArrayLit(items1) = r1.value if items1.len == 0
448 448
        else throw testing::TestError::Failed;
449 449
450 450
    let r2 = try! parseExprStr("[1]");
455 455
    let case ast::NodeValue::ArrayLit(items3) = r3.value if items3.len == 3
456 456
        else throw testing::TestError::Failed;
457 457
}
458 458
459 459
/// Test parsing array repeat literals.
460 -
@test fn testParseArrayRepeatLiteral() throws (testing::TestError) {
460 +
@test unsafe fn testParseArrayRepeatLiteral() throws (testing::TestError) {
461 461
    let r1 = try! parseExprStr("[42; 10]");
462 462
    let case ast::NodeValue::ArrayRepeatLit(a) = r1.value
463 463
        else throw testing::TestError::Failed;
464 464
465 465
    try expectNumber(a.item, "42");
466 466
    try expectNumber(a.count, "10");
467 467
}
468 468
469 469
/// Test parsing a simple `if` statement without an `else` clause.
470 -
@test fn testParseIf() throws (testing::TestError) {
470 +
@test unsafe fn testParseIf() throws (testing::TestError) {
471 471
    let r1 = try parseStmtStr("if condition { body; }") catch {
472 472
        throw testing::TestError::Failed;
473 473
    };
474 474
    let case ast::NodeValue::If(n) = r1.value
475 475
        else throw testing::TestError::Failed;
478 478
    try expectBlockExprStmt(n.thenBranch, ast::NodeValue::Ident("body"));
479 479
    try testing::expect(n.elseBranch == nil);
480 480
}
481 481
482 482
/// Test parsing an `if-else` statement.
483 -
@test fn testParseIfElse() throws (testing::TestError) {
483 +
@test unsafe fn testParseIfElse() throws (testing::TestError) {
484 484
    let r1 = try! parseStmtStr("if condition { left; } else { right; }") catch {
485 485
        throw testing::TestError::Failed;
486 486
    };
487 487
    let case ast::NodeValue::If(n) = r1.value
488 488
        else throw testing::TestError::Failed;
495 495
496 496
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("right"));
497 497
}
498 498
499 499
/// Test parsing an `if-else if-else` chain.
500 -
@test fn testParseIfElseIf() throws (testing::TestError) {
500 +
@test unsafe fn testParseIfElseIf() throws (testing::TestError) {
501 501
    let root = try! parseStmtStr("if x { a; } else if y { b; } else { c; }") catch {
502 502
        throw testing::TestError::Failed;
503 503
    };
504 504
    let case ast::NodeValue::If(top) = root.value
505 505
        else throw testing::TestError::Failed;
520 520
        else throw testing::TestError::Failed;
521 521
    try expectBlockExprStmt(innerElse, ast::NodeValue::Ident("c"));
522 522
}
523 523
524 524
/// Test parsing a block with multiple statements where the last lacks a semicolon.
525 -
@test fn testParseBlockMultiStmt() throws (testing::TestError) {
525 +
@test unsafe fn testParseBlockMultiStmt() throws (testing::TestError) {
526 526
    let root = try! parseStmtStr("{ first; second; third }");
527 527
    let case ast::NodeValue::Block(body) = root.value
528 528
        else throw testing::TestError::Failed;
529 529
    try testing::expect(body.statements.len == 3);
530 530
543 543
        else throw testing::TestError::Failed;
544 544
    try expectIdent(thirdExpr, "third");
545 545
}
546 546
547 547
/// Test parsing a block that keeps a trailing `;` delimiter.
548 -
@test fn testParseBlockTrailingSemicolon() throws (testing::TestError) {
548 +
@test unsafe fn testParseBlockTrailingSemicolon() throws (testing::TestError) {
549 549
    let root = try! parseStmtStr("if cond { only; }") catch {
550 550
        throw testing::TestError::Failed;
551 551
    };
552 552
    let case ast::NodeValue::If(node) = root.value
553 553
        else throw testing::TestError::Failed;
561 561
        else throw testing::TestError::Failed;
562 562
    try expectIdent(expr, "only");
563 563
}
564 564
565 565
/// Test that missing delimiters between block statements produce an error.
566 -
@test fn testParseBlockMissingDelimiter() throws (testing::TestError) {
566 +
@test unsafe fn testParseBlockMissingDelimiter() throws (testing::TestError) {
567 567
    let parsed: ?*ast::Node =
568 568
        try? parseStmtStr("if cond { first second }");
569 569
    try testing::expect(parsed == nil);
570 570
}
571 571
572 572
/// Test parsing a `let` binding without a type annotation.
573 -
@test fn testParseLet() throws (testing::TestError) {
573 +
@test unsafe fn testParseLet() throws (testing::TestError) {
574 574
    let r1 = try! parseStmtStr("let x = y;") catch {
575 575
        throw testing::TestError::Failed;
576 576
    };
577 577
    let case ast::NodeValue::Let(n) = r1.value
578 578
        else throw testing::TestError::Failed;
582 582
    try testing::expect(not n.mutable);
583 583
    try testing::expect(n.type == nil);
584 584
}
585 585
586 586
/// Test parsing a `let` binding with a type annotation.
587 -
@test fn testParseLetTyped() throws (testing::TestError) {
587 +
@test unsafe fn testParseLetTyped() throws (testing::TestError) {
588 588
    let r1 = try! parseStmtStr("let x: i32 = y;");
589 589
    let case ast::NodeValue::Let(n) = r1.value
590 590
        else throw testing::TestError::Failed;
591 591
592 592
    try expectIdent(n.ident, "x");
599 599
        width: 4, sign: ast::Signedness::Signed
600 600
    });
601 601
}
602 602
603 603
/// Test parsing a `let` binding with alignment modifier.
604 -
@test fn testParseLetAlign() throws (testing::TestError) {
604 +
@test unsafe fn testParseLetAlign() throws (testing::TestError) {
605 605
    let r1 = try! parseStmtStr("let x: i32 align(16) = 13;");
606 606
    let case ast::NodeValue::Let(n) = r1.value
607 607
        else throw testing::TestError::Failed;
608 608
609 609
    try expectIdent(n.ident, "x");
622 622
        else throw testing::TestError::Failed;
623 623
    try expectNumber(a, "16");
624 624
}
625 625
626 626
/// Test parsing let-else statement.
627 -
@test fn testParseLetElse() throws (testing::TestError) {
627 +
@test unsafe fn testParseLetElse() throws (testing::TestError) {
628 628
    let root = try! parseStmtStr("let x = opt else { return };");
629 629
    let case ast::NodeValue::LetElse(letElse) = root.value
630 630
        else throw testing::TestError::Failed;
631 631
632 632
    try expectIdent(letElse.pattern.pattern, "x");
635 635
        else throw testing::TestError::Failed;
636 636
    try testing::expect(elseBlock.statements.len == 1);
637 637
}
638 638
639 639
/// Test parsing let-else with single statement.
640 -
@test fn testParseLetElseSingleStmt() throws (testing::TestError) {
640 +
@test unsafe fn testParseLetElseSingleStmt() throws (testing::TestError) {
641 641
    let root = try! parseStmtStr("let y = val else return;");
642 642
    let case ast::NodeValue::LetElse(letElse) = root.value
643 643
        else throw testing::TestError::Failed;
644 644
645 645
    try expectIdent(letElse.pattern.pattern, "y");
647 647
    let case ast::NodeValue::Return(_) = letElse.elseBranch.value
648 648
        else throw testing::TestError::Failed;
649 649
}
650 650
651 651
/// Test parsing let-else with expression branch.
652 -
@test fn testParseLetElseExpr() throws (testing::TestError) {
652 +
@test unsafe fn testParseLetElseExpr() throws (testing::TestError) {
653 653
    let root = try! parseStmtStr("let z = opt else y;");
654 654
    let case ast::NodeValue::LetElse(letElse) = root.value
655 655
        else throw testing::TestError::Failed;
656 656
657 657
    try expectIdent(letElse.pattern.pattern, "z");
658 658
    try expectIdent(letElse.elseBranch, "y");
659 659
}
660 660
661 661
/// Test parsing let-case-else statement.
662 -
@test fn testParseLetCaseElse() throws (testing::TestError) {
662 +
@test unsafe fn testParseLetCaseElse() throws (testing::TestError) {
663 663
    let root = try! parseStmtStr("let case Variant(x) = opt else { return };");
664 664
    let case ast::NodeValue::LetElse(letElse) = root.value
665 665
        else throw testing::TestError::Failed;
666 666
667 667
    try testing::expect(letElse.pattern.guard == nil);
668 668
}
669 669
670 670
/// Test parsing let-case-else with guard.
671 -
@test fn testParseLetCaseElseWithGuard() throws (testing::TestError) {
671 +
@test unsafe fn testParseLetCaseElseWithGuard() throws (testing::TestError) {
672 672
    let root = try! parseStmtStr("let case Variant(x) = opt if x > 0 else { return };");
673 673
    let case ast::NodeValue::LetElse(letElse) = root.value
674 674
        else throw testing::TestError::Failed;
675 675
676 676
    try testing::expect(letElse.pattern.guard <> nil);
680 680
        else throw testing::TestError::Failed;
681 681
    try testing::expect(cmp.op == ast::BinaryOp::Gt);
682 682
}
683 683
684 684
/// Test parsing a `let mut` declaration.
685 -
@test fn testParseMut() throws (testing::TestError) {
685 +
@test unsafe fn testParseMut() throws (testing::TestError) {
686 686
    let r1 = try! parseStmtStr("let mut x = 42;");
687 687
    let case ast::NodeValue::Let(n) = r1.value
688 688
        else throw testing::TestError::Failed;
689 689
690 690
    try expectIdent(n.ident, "x");
692 692
    try testing::expect(n.mutable);
693 693
    try testing::expect(n.type == nil);
694 694
}
695 695
696 696
/// Test parsing a `let mut` declaration with type annotation.
697 -
@test fn testParseMutTyped() throws (testing::TestError) {
697 +
@test unsafe fn testParseMutTyped() throws (testing::TestError) {
698 698
    let r1 = try! parseStmtStr("let mut x: i32 = 42;");
699 699
    let case ast::NodeValue::Let(n) = r1.value
700 700
        else throw testing::TestError::Failed;
701 701
702 702
    try expectIdent(n.ident, "x");
709 709
        width: 4, sign: ast::Signedness::Signed
710 710
    });
711 711
}
712 712
713 713
/// Test parsing a `constant` declaration.
714 -
@test fn testParseConst() throws (testing::TestError) {
714 +
@test unsafe fn testParseConst() throws (testing::TestError) {
715 715
    let node = try! parseStmtStr("constant ANSWER: i32 = 42;");
716 716
    let case ast::NodeValue::ConstDecl(decl) = node.value
717 717
        else throw testing::TestError::Failed;
718 718
719 719
    try expectIdent(decl.ident, "ANSWER");
722 722
    });
723 723
    try expectNumber(decl.value, "42");
724 724
}
725 725
726 726
/// Test parsing a `static` declaration.
727 -
@test fn testParseStatic() throws (testing::TestError) {
727 +
@test unsafe fn testParseStatic() throws (testing::TestError) {
728 728
    let node = try! parseStmtStr("static COUNTER: i32 = 0;");
729 729
    let case ast::NodeValue::StaticDecl(decl) = node.value
730 730
        else throw testing::TestError::Failed;
731 731
732 732
    try expectIdent(decl.ident, "COUNTER");
735 735
    });
736 736
    try expectNumber(decl.value, "0");
737 737
}
738 738
739 739
/// Test parsing a `use` declaration.
740 -
@test fn testParseUse() throws (testing::TestError) {
740 +
@test unsafe fn testParseUse() throws (testing::TestError) {
741 741
    let node = try! parseStmtStr("use module::item;");
742 742
    let case ast::NodeValue::Use(decl) = node.value
743 743
        else throw testing::TestError::Failed;
744 744
745 745
    let case ast::NodeValue::ScopeAccess(scope) = decl.path.value
748 748
    try expectIdent(scope.parent, "module");
749 749
    try expectIdent(scope.child, "item");
750 750
}
751 751
752 752
/// Test parsing a `mod` declaration.
753 -
@test fn testParseMod() throws (testing::TestError) {
753 +
@test unsafe fn testParseMod() throws (testing::TestError) {
754 754
    let node = try! parseStmtStr("mod io;");
755 755
    let case ast::NodeValue::Mod(decl) = node.value
756 756
        else throw testing::TestError::Failed;
757 757
758 758
    try expectIdent(decl.name, "io");
759 759
    try testing::expect(decl.attrs == nil);
760 760
}
761 761
762 762
/// Test parsing a module declaration with attributes.
763 -
@test fn testParseModAttributes() throws (testing::TestError) {
763 +
@test unsafe fn testParseModAttributes() throws (testing::TestError) {
764 764
    let node = try! parseStmtStr("export mod io;");
765 765
    let case ast::NodeValue::Mod(decl) = node.value
766 766
        else throw testing::TestError::Failed;
767 767
768 768
    try expectIdent(decl.name, "io");
777 777
    try testing::expect(attr == ast::Attribute::Export);
778 778
    try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Export));
779 779
}
780 780
781 781
/// Test parsing an optional type.
782 -
@test fn testParseTypeOptional() throws (testing::TestError) {
782 +
@test unsafe fn testParseTypeOptional() throws (testing::TestError) {
783 783
    let node = try! parseTypeStr("?i32");
784 784
    let case ast::NodeValue::TypeSig(optional) = node.value
785 785
        else throw testing::TestError::Failed;
786 786
    let case ast::TypeSig::Optional(opt) = optional
787 787
        else throw testing::TestError::Failed;
788 788
789 789
    try expectIntType(opt, 4, ast::Signedness::Signed);
790 790
}
791 791
792 792
/// Parse an `i32` pointer type and verify its class and mutability.
793 -
fn expectI32Pointer(
793 +
unsafe fn expectI32Pointer(
794 794
    source: *[u8],
795 795
    class: types::PointerClass,
796 796
    mutable: bool,
797 797
) throws (testing::TestError) {
798 798
    let node = try! parseTypeStr(source);
806 806
    try expectIntType(valueType, 4, ast::Signedness::Signed);
807 807
    assert actualMutable == mutable;
808 808
}
809 809
810 810
/// Test parsing a mutable owned pointer.
811 -
@test fn testParseTypePointer() throws (testing::TestError) {
811 +
@test unsafe fn testParseTypePointer() throws (testing::TestError) {
812 812
    try expectI32Pointer("*mut i32", types::PointerClass::Owned, true);
813 813
}
814 814
815 815
/// Test parsing an immutable owned pointer.
816 -
@test fn testParseTypePointerImmutable() throws (testing::TestError) {
816 +
@test unsafe fn testParseTypePointerImmutable() throws (testing::TestError) {
817 817
    try expectI32Pointer("*i32", types::PointerClass::Owned, false);
818 818
}
819 819
820 820
/// Test parsing immutable and mutable references.
821 -
@test fn testParseTypeRef() throws (testing::TestError) {
821 +
@test unsafe fn testParseTypeRef() throws (testing::TestError) {
822 822
    try expectI32Pointer("&i32", types::PointerClass::Ref, false);
823 823
    try expectI32Pointer("&mut i32", types::PointerClass::Ref, true);
824 824
}
825 825
826 826
/// Test parsing immutable and mutable unsafe pointers.
827 -
@test fn testParseTypeUnsafePointer() throws (testing::TestError) {
827 +
@test unsafe fn testParseTypeUnsafePointer() throws (testing::TestError) {
828 828
    try expectI32Pointer("*unsafe i32", types::PointerClass::Unsafe, false);
829 829
    try expectI32Pointer("*unsafe mut i32", types::PointerClass::Unsafe, true);
830 830
}
831 831
832 832
/// Test parsing a slice type.
833 -
@test fn testParseTypeSlice() throws (testing::TestError) {
833 +
@test unsafe fn testParseTypeSlice() throws (testing::TestError) {
834 834
    let node = try! parseTypeStr("*[u8]");
835 835
    let case ast::NodeValue::TypeSig(sig) = node.value
836 836
        else throw testing::TestError::Failed;
837 837
    let case ast::TypeSig::Slice { class, itemType, mutable } = sig
838 838
        else throw testing::TestError::Failed;
841 841
    try expectIntType(itemType, 1, ast::Signedness::Unsigned);
842 842
    assert not mutable;
843 843
}
844 844
845 845
/// Test parsing a mutable slice type.
846 -
@test fn testParseTypeSliceMutable() throws (testing::TestError) {
846 +
@test unsafe fn testParseTypeSliceMutable() throws (testing::TestError) {
847 847
    let node = try! parseTypeStr("*mut [u8]");
848 848
    let case ast::NodeValue::TypeSig(sig) = node.value
849 849
        else throw testing::TestError::Failed;
850 850
    let case ast::TypeSig::Slice { class, itemType, mutable } = sig
851 851
        else throw testing::TestError::Failed;
854 854
    try expectIntType(itemType, 1, ast::Signedness::Unsigned);
855 855
    assert mutable;
856 856
}
857 857
858 858
/// Test parsing reference and unsafe slice classes.
859 -
@test fn testParseTypeSliceClasses() throws (testing::TestError) {
859 +
@test unsafe fn testParseTypeSliceClasses() throws (testing::TestError) {
860 860
    let refNode = try! parseTypeStr("&[u8]");
861 861
    let case ast::NodeValue::TypeSig(ast::TypeSig::Slice {
862 862
        class: refClass, ..
863 863
    }) = refNode.value else throw testing::TestError::Failed;
864 864
    assert refClass == types::PointerClass::Ref;
869 869
    }) = unsafeNode.value else throw testing::TestError::Failed;
870 870
    assert unsafeClass == types::PointerClass::Unsafe;
871 871
}
872 872
873 873
/// Test parsing trait object pointer classes.
874 -
@test fn testParseTypeTraitObjectClasses() throws (testing::TestError) {
874 +
@test unsafe fn testParseTypeTraitObjectClasses() throws (testing::TestError) {
875 875
    let ownedNode = try! parseTypeStr("*opaque Read");
876 876
    let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject {
877 877
        class: ownedClass, ..
878 878
    }) = ownedNode.value else throw testing::TestError::Failed;
879 879
    assert ownedClass == types::PointerClass::Owned;
890 890
    }) = unsafeNode.value else throw testing::TestError::Failed;
891 891
    assert unsafeClass == types::PointerClass::Unsafe;
892 892
}
893 893
894 894
/// Test parsing an array type.
895 -
@test fn testParseTypeArray() throws (testing::TestError) {
895 +
@test unsafe fn testParseTypeArray() throws (testing::TestError) {
896 896
    let node = try! parseTypeStr("[i32; 4]");
897 897
    let case ast::NodeValue::TypeSig(sig) = node.value
898 898
        else throw testing::TestError::Failed;
899 899
    let case ast::TypeSig::Array { itemType, length } = sig
900 900
        else throw testing::TestError::Failed;
902 902
    try expectIntType(itemType, 4, ast::Signedness::Signed);
903 903
    try expectNumber(length, "4");
904 904
}
905 905
906 906
/// Test parsing a named record declaration without derives.
907 -
@test fn testParseRecordDecl() throws (testing::TestError) {
907 +
@test unsafe fn testParseRecordDecl() throws (testing::TestError) {
908 908
    let node = try! parseStmtStr("record R { x: bool, y: i32 }");
909 909
    let case ast::NodeValue::RecordDecl(decl) = node.value
910 910
        else throw testing::TestError::Failed;
911 911
912 912
    try expectIdent(decl.name, "R");
919 919
        sign: ast::Signedness::Signed,
920 920
    });
921 921
}
922 922
923 923
/// Test parsing a record declaration with derives.
924 -
@test fn testParseRecordDeclDerives() throws (testing::TestError) {
924 +
@test unsafe fn testParseRecordDeclDerives() throws (testing::TestError) {
925 925
    let node = try! parseStmtStr("record R: Eq + Debug { field: i32 }");
926 926
    let case ast::NodeValue::RecordDecl(decl) = node.value
927 927
        else throw testing::TestError::Failed;
928 928
929 929
    try expectIdent(decl.name, "R");
931 931
    try expectIdent(decl.derives[0], "Eq");
932 932
    try expectIdent(decl.derives[1], "Debug");
933 933
}
934 934
935 935
/// Test parsing a record declaration with field initializers.
936 -
@test fn testParseRecordDeclFieldDefaults() throws (testing::TestError) {
936 +
@test unsafe fn testParseRecordDeclFieldDefaults() throws (testing::TestError) {
937 937
    let node = try! parseStmtStr("record Config { size: opaque = 42, flag: bool = true }");
938 938
    let case ast::NodeValue::RecordDecl(decl) = node.value
939 939
        else throw testing::TestError::Failed;
940 940
941 941
    try expectIdent(decl.name, "Config");
958 958
        else throw testing::TestError::Failed;
959 959
    try testing::expect(flagValue);
960 960
}
961 961
962 962
/// Test parsing an unlabeled record declaration.
963 -
@test fn testParseTupleRecordDecl() throws (testing::TestError) {
963 +
@test unsafe fn testParseTupleRecordDecl() throws (testing::TestError) {
964 964
    let node = try! parseStmtStr("record Pair(bool, i32);");
965 965
    let case ast::NodeValue::RecordDecl(decl) = node.value
966 966
        else throw testing::TestError::Failed;
967 967
968 968
    try expectIdent(decl.name, "Pair");
976 976
        sign: ast::Signedness::Signed,
977 977
    });
978 978
}
979 979
980 980
/// Test parsing a single-field unlabeled record.
981 -
@test fn testParseTupleRecordSingleField() throws (testing::TestError) {
981 +
@test unsafe fn testParseTupleRecordSingleField() throws (testing::TestError) {
982 982
    let node = try! parseStmtStr("record R(bool);");
983 983
    let case ast::NodeValue::RecordDecl(decl) = node.value
984 984
        else throw testing::TestError::Failed;
985 985
986 986
    try expectIdent(decl.name, "R");
989 989
990 990
    try expectFieldSig(decl.fields, 0, nil, ast::TypeSig::Bool);
991 991
}
992 992
993 993
/// Test parsing empty record literals.
994 -
@test fn testParseEmptyRecordLiteral() throws (testing::TestError) {
994 +
@test unsafe fn testParseEmptyRecordLiteral() throws (testing::TestError) {
995 995
    let r1 = try! parseExprStr("{}");
996 996
    let case ast::NodeValue::RecordLit(lit) = r1.value
997 997
        else throw testing::TestError::Failed;
998 998
999 999
    try testing::expect(lit.typeName == nil);
1007 1007
    try expectIdent(typeName, "Point");
1008 1008
    try testing::expect(lit2.fields.len == 0);
1009 1009
}
1010 1010
1011 1011
/// Test parsing a function type with parameters and return type.
1012 -
@test fn testParseTypeFn() throws (testing::TestError) {
1012 +
@test unsafe fn testParseTypeFn() throws (testing::TestError) {
1013 1013
    let node = try! parseTypeStr("fn (i32, *u8) -> bool");
1014 1014
    let case ast::NodeValue::TypeSig(sigValue) = node.value
1015 1015
        else throw testing::TestError::Failed;
1016 -
    let case ast::TypeSig::Fn(sig) = sigValue
1016 +
    let case ast::TypeSig::Fn { sig, .. } = sigValue
1017 1017
        else throw testing::TestError::Failed;
1018 1018
1019 1019
    try testing::expect(sig.params.len == 2);
1020 1020
1021 1021
    let param0 = sig.params[0];
1030 1030
1031 1031
    try testing::expect(sig.returnType <> nil);
1032 1032
}
1033 1033
1034 1034
/// Test parsing a function type with a throws clause.
1035 -
@test fn testParseTypeFnThrows() throws (testing::TestError) {
1035 +
@test unsafe fn testParseTypeFnThrows() throws (testing::TestError) {
1036 1036
    let node = try! parseTypeStr("fn (i32) -> bool throws (Error, Other)");
1037 1037
    let case ast::NodeValue::TypeSig(sigValue) = node.value
1038 1038
        else throw testing::TestError::Failed;
1039 -
    let case ast::TypeSig::Fn(sig) = sigValue
1039 +
    let case ast::TypeSig::Fn { sig, .. } = sigValue
1040 1040
        else throw testing::TestError::Failed;
1041 1041
1042 1042
    try testing::expect(sig.params.len == 1);
1043 1043
    try expectIntType(sig.params[0], 4, ast::Signedness::Signed);
1044 1044
1050 1050
        else throw testing::TestError::Failed;
1051 1051
    try expectType(returnType, ast::TypeSig::Bool);
1052 1052
}
1053 1053
1054 1054
/// Test parsing a function declaration without parameters.
1055 -
@test fn testParseFnDeclEmpty() throws (testing::TestError) {
1055 +
@test unsafe fn testParseFnDeclEmpty() throws (testing::TestError) {
1056 1056
    let node = try! parseStmtStr("fn main() {}");
1057 1057
    let case ast::NodeValue::FnDecl(decl) = node.value
1058 1058
        else throw testing::TestError::Failed;
1059 1059
1060 1060
    try expectIdent(decl.name, "main");
1066 1066
    let case ast::NodeValue::Block(_) = body.value
1067 1067
        else throw testing::TestError::Failed;
1068 1068
}
1069 1069
1070 1070
/// Test parsing a function declaration with parameters and return type.
1071 -
@test fn testParseFnDeclParams() throws (testing::TestError) {
1071 +
@test unsafe fn testParseFnDeclParams() throws (testing::TestError) {
1072 1072
    let node = try! parseStmtStr("fn add(x: i32, y: *u8) -> bool {}");
1073 1073
    let case ast::NodeValue::FnDecl(decl) = node.value
1074 1074
        else throw testing::TestError::Failed;
1075 1075
1076 1076
    try expectIdent(decl.name, "add");
1104 1104
            else throw testing::TestError::Failed;
1105 1105
    }
1106 1106
}
1107 1107
1108 1108
/// Test parsing a function declaration with a throws clause.
1109 -
@test fn testParseFnDeclThrows() throws (testing::TestError) {
1109 +
@test unsafe fn testParseFnDeclThrows() throws (testing::TestError) {
1110 1110
    let node = try! parseStmtStr("fn handle() throws (Error, Crash) {}");
1111 1111
    let case ast::NodeValue::FnDecl(decl) = node.value
1112 1112
        else throw testing::TestError::Failed;
1113 1113
1114 1114
    try expectIdent(decl.name, "handle");
1122 1122
    let case ast::NodeValue::Block(_) = body.value
1123 1123
        else throw testing::TestError::Failed;
1124 1124
}
1125 1125
1126 1126
/// Test scanning source-level `void` produces an identifier, not a type keyword.
1127 -
@test fn testParseTypeVoidRejected() throws (testing::TestError) {
1127 +
@test unsafe fn testParseTypeVoidRejected() throws (testing::TestError) {
1128 1128
    let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]);
1129 1129
    let mut parser = super::mkParser(scanner::SourceLoc::String, "void", &mut arena, &mut STRING_POOL);
1130 1130
    super::advance(&mut parser);
1131 1131
    try testing::expect(super::check(&parser, scanner::TokenKind::Ident));
1132 1132
}
1133 1133
1134 1134
/// Test parsing the unsafe function modifier.
1135 -
@test fn testParseUnsafeFnDecl() throws (testing::TestError) {
1135 +
@test unsafe fn testParseUnsafeFnDecl() throws (testing::TestError) {
1136 1136
    let node = try! parseStmtStr("unsafe fn run() {}");
1137 1137
    let case ast::NodeValue::FnDecl(decl) = node.value
1138 1138
        else throw testing::TestError::Failed;
1139 1139
    let attrs = decl.attrs
1140 1140
        else throw testing::TestError::Failed;
1142 1142
    try testing::expect(attrs.list.len == 1);
1143 1143
    try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe));
1144 1144
}
1145 1145
1146 1146
/// Test rejecting `unsafe` on declarations where it has no semantics.
1147 -
@test fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) {
1147 +
@test unsafe fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) {
1148 1148
    let recordDecl: ?*ast::Node = try? parseStmtStr("unsafe record R {}");
1149 1149
    try testing::expect(recordDecl == nil);
1150 1150
    let constDecl: ?*ast::Node = try? parseStmtStr("unsafe constant X = 1;");
1151 1151
    try testing::expect(constDecl == nil);
1152 1152
}
1153 1153
1154 -
/// Test `unsafe` on the other declaration forms that support it.
1155 -
@test fn testParseUnsafeMethodAndModule() throws (testing::TestError) {
1156 -
    let moduleNode = try! parseStmtStr("unsafe mod io;");
1157 -
    let case ast::NodeValue::Mod(moduleDecl) = moduleNode.value
1158 -
        else throw testing::TestError::Failed;
1159 -
    let moduleAttrs = moduleDecl.attrs else throw testing::TestError::Failed;
1160 -
    assert ast::attributesContains(&moduleAttrs, ast::Attribute::Unsafe);
1161 -
1154 +
/// Test unsafe method declarations.
1155 +
@test unsafe fn testParseUnsafeMethods() throws (testing::TestError) {
1162 1156
    let instanceNode = try! parseStmtStr(
1163 1157
        "instance Read for Value { unsafe fn (value: &Value) get() {} }"
1164 1158
    );
1165 1159
    let case ast::NodeValue::InstanceDecl { methods, .. } = instanceNode.value
1166 1160
        else throw testing::TestError::Failed;
1168 1162
    let case ast::NodeValue::MethodDecl { attrs, .. } = methods[0].value
1169 1163
        else throw testing::TestError::Failed;
1170 1164
    let methodAttrs = attrs else throw testing::TestError::Failed;
1171 1165
    assert ast::attributesContains(&methodAttrs, ast::Attribute::Unsafe);
1172 1166
1173 -
    let mut printStorage: [u8; 4096] = undefined;
1167 +
    static printStorage: [u8; 4096] = undefined;
1174 1168
    let mut printArena = alloc::new(&mut printStorage[..]);
1175 1169
    let methodExpr = printer::toExpr(&mut printArena, methods[0]);
1176 1170
    let case sexpr::Expr::Block { items: methodItems, .. } = methodExpr
1177 1171
        else throw testing::TestError::Failed;
1178 1172
    let case sexpr::Expr::List {
1205 1199
        else throw testing::TestError::Failed;
1206 1200
    assert mem::eq(traitAttr, "@unsafe");
1207 1201
}
1208 1202
1209 1203
/// Test parsing a function declaration with attributes.
1210 -
@test fn testParseFnDeclAttributes() throws (testing::TestError) {
1204 +
@test unsafe fn testParseFnDeclAttributes() throws (testing::TestError) {
1211 1205
    let node = try! parseStmtStr("export fn run();");
1212 1206
    let case ast::NodeValue::FnDecl(decl) = node.value
1213 1207
        else throw testing::TestError::Failed;
1214 1208
1215 1209
    try expectIdent(decl.name, "run");
1231 1225
    try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern));
1232 1226
    try testing::expect(decl.body == nil);
1233 1227
}
1234 1228
1235 1229
/// Test parsing a top-level function declaration with inferred extern from `;`.
1236 -
@test fn testParseFnDeclInferredExtern() throws (testing::TestError) {
1230 +
@test unsafe fn testParseFnDeclInferredExtern() throws (testing::TestError) {
1237 1231
    let node = try! parseStmtStr("fn run();");
1238 1232
    let case ast::NodeValue::FnDecl(decl) = node.value
1239 1233
        else throw testing::TestError::Failed;
1240 1234
1241 1235
    try expectIdent(decl.name, "run");
1252 1246
    try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern));
1253 1247
    try testing::expect(decl.body == nil);
1254 1248
}
1255 1249
1256 1250
/// Test parsing a top-level exported function declaration with inferred extern from `;`.
1257 -
@test fn testParseFnDeclExportInferredExtern() throws (testing::TestError) {
1251 +
@test unsafe fn testParseFnDeclExportInferredExtern() throws (testing::TestError) {
1258 1252
    let node = try! parseStmtStr("export fn run();");
1259 1253
    let case ast::NodeValue::FnDecl(decl) = node.value
1260 1254
        else throw testing::TestError::Failed;
1261 1255
1262 1256
    try expectIdent(decl.name, "run");
1278 1272
    try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern));
1279 1273
    try testing::expect(decl.body == nil);
1280 1274
}
1281 1275
1282 1276
/// Test parsing a scoped identifier type.
1283 -
@test fn testParseTypeScopedIdent() throws (testing::TestError) {
1277 +
@test unsafe fn testParseTypeScopedIdent() throws (testing::TestError) {
1284 1278
    let node = try! parseTypeStr("module::Type");
1285 1279
    let case ast::NodeValue::TypeSig(ts) = node.value
1286 1280
        else throw testing::TestError::Failed;
1287 1281
    let case ast::TypeSig::Nominal(name) = ts
1288 1282
        else throw testing::TestError::Failed;
1292 1286
    try expectIdent(access.parent, "module");
1293 1287
    try expectIdent(access.child, "Type");
1294 1288
}
1295 1289
1296 1290
/// Test parsing the `bool` type.
1297 -
@test fn testParseTypeBool() throws (testing::TestError) {
1291 +
@test unsafe fn testParseTypeBool() throws (testing::TestError) {
1298 1292
    let node = try! parseTypeStr("bool");
1299 1293
    try expectType(node, ast::TypeSig::Bool);
1300 1294
}
1301 1295
1302 1296
/// Test parsing an unsigned integer type.
1303 -
@test fn testParseTypeUnsigned() throws (testing::TestError) {
1297 +
@test unsafe fn testParseTypeUnsigned() throws (testing::TestError) {
1304 1298
    let node = try! parseTypeStr("u8");
1305 1299
    try expectType(node, ast::TypeSig::Integer {
1306 1300
        width: 1,
1307 1301
        sign: ast::Signedness::Unsigned,
1308 1302
    });
1309 1303
}
1310 1304
1311 1305
/// Test parsing a simple `if let` statement without guard or else.
1312 -
@test fn testParseIfLet() throws (testing::TestError) {
1306 +
@test unsafe fn testParseIfLet() throws (testing::TestError) {
1313 1307
    let root = try! parseStmtStr("if let value = opt { body; }");
1314 1308
    let case ast::NodeValue::IfLet(node) = root.value
1315 1309
        else throw testing::TestError::Failed;
1316 1310
1317 1311
    try expectIdent(node.pattern.pattern, "value");
1321 1315
    try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body"));
1322 1316
    try testing::expect(node.elseBranch == nil);
1323 1317
}
1324 1318
1325 1319
/// Test parsing an `if let` statement with guard and else branches.
1326 -
@test fn testParseIfLetGuardElse() throws (testing::TestError) {
1320 +
@test unsafe fn testParseIfLetGuardElse() throws (testing::TestError) {
1327 1321
    let root = try! parseStmtStr(
1328 1322
        "if let value = opt; guard { body; } else { alt; }"
1329 1323
    );
1330 1324
    let case ast::NodeValue::IfLet(node) = root.value
1331 1325
        else throw testing::TestError::Failed;
1343 1337
        else throw testing::TestError::Failed;
1344 1338
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1345 1339
}
1346 1340
1347 1341
/// Test parsing an `if let` statement with an `else if` chain.
1348 -
@test fn testParseIfLetElseIf() throws (testing::TestError) {
1342 +
@test unsafe fn testParseIfLetElseIf() throws (testing::TestError) {
1349 1343
    let root = try! parseStmtStr(
1350 1344
        "if let value = opt { body; } else if cond { alt; }"
1351 1345
    );
1352 1346
    let case ast::NodeValue::IfLet(node) = root.value
1353 1347
        else throw testing::TestError::Failed;
1365 1359
    try expectBlockExprStmt(inner.thenBranch, ast::NodeValue::Ident("alt"));
1366 1360
    try testing::expect(inner.elseBranch == nil);
1367 1361
}
1368 1362
1369 1363
/// Test parsing `if let mut` binding.
1370 -
@test fn testParseIfLetMut() throws (testing::TestError) {
1364 +
@test unsafe fn testParseIfLetMut() throws (testing::TestError) {
1371 1365
    let root = try! parseStmtStr("if let mut value = opt { body; }");
1372 1366
    let case ast::NodeValue::IfLet(node) = root.value
1373 1367
        else throw testing::TestError::Failed;
1374 1368
1375 1369
    try expectIdent(node.pattern.pattern, "value");
1379 1373
    try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body"));
1380 1374
    try testing::expect(node.elseBranch == nil);
1381 1375
}
1382 1376
1383 1377
/// Test parsing `let mut ... else` binding.
1384 -
@test fn testParseLetMutElse() throws (testing::TestError) {
1378 +
@test unsafe fn testParseLetMutElse() throws (testing::TestError) {
1385 1379
    let root = try! parseStmtStr("let mut x = opt else { return };");
1386 1380
    let case ast::NodeValue::LetElse(letElse) = root.value
1387 1381
        else throw testing::TestError::Failed;
1388 1382
1389 1383
    try expectIdent(letElse.pattern.pattern, "x");
1390 1384
    try testing::expect(letElse.pattern.mutable);
1391 1385
}
1392 1386
1393 1387
/// Test that `if let` without `mut` is not mutable.
1394 -
@test fn testParseIfLetNotMutable() throws (testing::TestError) {
1388 +
@test unsafe fn testParseIfLetNotMutable() throws (testing::TestError) {
1395 1389
    let root = try! parseStmtStr("if let value = opt { body; }");
1396 1390
    let case ast::NodeValue::IfLet(node) = root.value
1397 1391
        else throw testing::TestError::Failed;
1398 1392
1399 1393
    try testing::expect(not node.pattern.mutable);
1400 1394
}
1401 1395
1402 1396
/// Test that `let ... else` without `mut` is not mutable.
1403 -
@test fn testParseLetElseNotMutable() throws (testing::TestError) {
1397 +
@test unsafe fn testParseLetElseNotMutable() throws (testing::TestError) {
1404 1398
    let root = try! parseStmtStr("let x = opt else { return };");
1405 1399
    let case ast::NodeValue::LetElse(letElse) = root.value
1406 1400
        else throw testing::TestError::Failed;
1407 1401
1408 1402
    try testing::expect(not letElse.pattern.mutable);
1409 1403
}
1410 1404
1411 1405
/// Test parsing a simple `if let case` statement.
1412 -
@test fn testParseIfCase() throws (testing::TestError) {
1406 +
@test unsafe fn testParseIfCase() throws (testing::TestError) {
1413 1407
    let root = try! parseStmtStr("if let case pat = value { body; }");
1414 1408
    let case ast::NodeValue::IfLet(node) = root.value
1415 1409
        else throw testing::TestError::Failed;
1416 1410
1417 1411
    try expectIdent(node.pattern.pattern, "pat");
1420 1414
    try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body"));
1421 1415
    try testing::expect(node.elseBranch == nil);
1422 1416
}
1423 1417
1424 1418
/// Test parsing an `if let case` statement with guard and else branches.
1425 -
@test fn testParseIfCaseGuardElse() throws (testing::TestError) {
1419 +
@test unsafe fn testParseIfCaseGuardElse() throws (testing::TestError) {
1426 1420
    let root = try! parseStmtStr(
1427 1421
        "if let case pat = value; guard { body; } else { alt; }"
1428 1422
    );
1429 1423
    let case ast::NodeValue::IfLet(node) = root.value
1430 1424
        else throw testing::TestError::Failed;
1442 1436
        else throw testing::TestError::Failed;
1443 1437
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1444 1438
}
1445 1439
1446 1440
/// Test parsing an `if let case` statement with an `else if` chain.
1447 -
@test fn testParseIfCaseElseIf() throws (testing::TestError) {
1441 +
@test unsafe fn testParseIfCaseElseIf() throws (testing::TestError) {
1448 1442
    let root = try! parseStmtStr(
1449 1443
        "if let case pat = value { body; } else if cond { alt; }"
1450 1444
    );
1451 1445
    let case ast::NodeValue::IfLet(node) = root.value
1452 1446
        else throw testing::TestError::Failed;
1462 1456
    try expectBlockExprStmt(inner.thenBranch, ast::NodeValue::Ident("alt"));
1463 1457
    try testing::expect(inner.elseBranch == nil);
1464 1458
}
1465 1459
1466 1460
/// Test parsing a simple `while` loop without an `else` branch.
1467 -
@test fn testParseWhile() throws (testing::TestError) {
1461 +
@test unsafe fn testParseWhile() throws (testing::TestError) {
1468 1462
    let root = try! parseStmtStr("while cond { body; }");
1469 1463
    let case ast::NodeValue::While(loopNode) = root.value
1470 1464
        else throw testing::TestError::Failed;
1471 1465
1472 1466
    try expectIdent(loopNode.condition, "cond");
1473 1467
    try expectBlockExprStmt(loopNode.body, ast::NodeValue::Ident("body"));
1474 1468
    try testing::expect(loopNode.elseBranch == nil);
1475 1469
}
1476 1470
1477 1471
/// Test parsing a `while` loop with an `else` branch.
1478 -
@test fn testParseWhileElse() throws (testing::TestError) {
1472 +
@test unsafe fn testParseWhileElse() throws (testing::TestError) {
1479 1473
    let root = try! parseStmtStr("while cond { body; } else { alt; }");
1480 1474
    let case ast::NodeValue::While(loopNode) = root.value
1481 1475
        else throw testing::TestError::Failed;
1482 1476
1483 1477
    try expectIdent(loopNode.condition, "cond");
1487 1481
        else throw testing::TestError::Failed;
1488 1482
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1489 1483
}
1490 1484
1491 1485
/// Test parsing a simple `while let case` loop.
1492 -
@test fn testParseWhileCase() throws (testing::TestError) {
1486 +
@test unsafe fn testParseWhileCase() throws (testing::TestError) {
1493 1487
    let root = try! parseStmtStr("while let case pat = value { body; }");
1494 1488
    let case ast::NodeValue::WhileLet(node) = root.value
1495 1489
        else throw testing::TestError::Failed;
1496 1490
1497 1491
    try expectIdent(node.pattern.pattern, "pat");
1500 1494
    try expectBlockExprStmt(node.body, ast::NodeValue::Ident("body"));
1501 1495
    try testing::expect(node.elseBranch == nil);
1502 1496
}
1503 1497
1504 1498
/// Test parsing a `while let case` loop with guard and else branches.
1505 -
@test fn testParseWhileCaseGuardElse() throws (testing::TestError) {
1499 +
@test unsafe fn testParseWhileCaseGuardElse() throws (testing::TestError) {
1506 1500
    let root = try! parseStmtStr(
1507 1501
        "while let case pat = value; guard { body; } else { alt; }"
1508 1502
    );
1509 1503
    let case ast::NodeValue::WhileLet(node) = root.value
1510 1504
        else throw testing::TestError::Failed;
1519 1513
        else throw testing::TestError::Failed;
1520 1514
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1521 1515
}
1522 1516
1523 1517
/// Test parsing a simple `try` expression without catch.
1524 -
@test fn testParseTry() throws (testing::TestError) {
1518 +
@test unsafe fn testParseTry() throws (testing::TestError) {
1525 1519
    let root = try! parseExprStr("try value");
1526 1520
    let case ast::NodeValue::Try(node) = root.value
1527 1521
        else throw testing::TestError::Failed;
1528 1522
1529 1523
    try expectIdent(node.expr, "value");
1530 1524
    try testing::expect(node.catches.len == 0);
1531 1525
    try testing::expect(not node.shouldPanic);
1532 1526
}
1533 1527
1534 1528
/// Test that `try?` consumes a unary operand.
1535 -
@test fn testParseTryOptionalUnary() throws (testing::TestError) {
1529 +
@test unsafe fn testParseTryOptionalUnary() throws (testing::TestError) {
1536 1530
    let root = try! parseExprStr("try? -value");
1537 1531
    let case ast::NodeValue::Try(node) = root.value
1538 1532
        else throw testing::TestError::Failed;
1539 1533
    let case ast::NodeValue::UnOp(neg) = node.expr.value
1540 1534
        else throw testing::TestError::Failed;
1542 1536
    try expectIdent(neg.value, "value");
1543 1537
    try testing::expect(node.returnsOptional);
1544 1538
}
1545 1539
1546 1540
/// Test parsing a `try!` expression that panics on error.
1547 -
@test fn testParseTryBang() throws (testing::TestError) {
1541 +
@test unsafe fn testParseTryBang() throws (testing::TestError) {
1548 1542
    let root = try! parseExprStr("try! value");
1549 1543
    let case ast::NodeValue::Try(node) = root.value
1550 1544
        else throw testing::TestError::Failed;
1551 1545
1552 1546
    try expectIdent(node.expr, "value");
1553 1547
    try testing::expect(node.catches.len == 0);
1554 1548
    try testing::expect(node.shouldPanic);
1555 1549
}
1556 1550
1557 1551
/// Test parsing a `try` expression with a `catch` block.
1558 -
@test fn testParseTryCatchBlock() throws (testing::TestError) {
1552 +
@test unsafe fn testParseTryCatchBlock() throws (testing::TestError) {
1559 1553
    let root = try! parseExprStr("try value catch { alt; }");
1560 1554
    let case ast::NodeValue::Try(node) = root.value
1561 1555
        else throw testing::TestError::Failed;
1562 1556
1563 1557
    try expectIdent(node.expr, "value");
1569 1563
    try testing::expect(clause.typeNode == nil);
1570 1564
    try expectBlockExprStmt(clause.body, ast::NodeValue::Ident("alt"));
1571 1565
}
1572 1566
1573 1567
/// Test that `catch` without a block is rejected.
1574 -
@test fn testParseTryCatchExprRejected() throws (testing::TestError) {
1568 +
@test unsafe fn testParseTryCatchExprRejected() throws (testing::TestError) {
1575 1569
    let parsed: ?*ast::Node = try? parseExprStr("try value catch alternate");
1576 1570
    try testing::expect(parsed == nil);
1577 1571
}
1578 1572
1579 1573
/// Test parsing a `break` statement.
1580 -
@test fn testParseBreak() throws (testing::TestError) {
1574 +
@test unsafe fn testParseBreak() throws (testing::TestError) {
1581 1575
    let root = try! parseStmtStr("break");
1582 1576
    let case ast::NodeValue::Break= root.value
1583 1577
        else throw testing::TestError::Failed;
1584 1578
}
1585 1579
1586 1580
/// Test parsing a `continue` statement.
1587 -
@test fn testParseContinue() throws (testing::TestError) {
1581 +
@test unsafe fn testParseContinue() throws (testing::TestError) {
1588 1582
    let root = try! parseStmtStr("continue");
1589 1583
    let case ast::NodeValue::Continue= root.value
1590 1584
        else throw testing::TestError::Failed;
1591 1585
}
1592 1586
1593 1587
/// Test parsing a `return` statement without value.
1594 -
@test fn testParseReturnVoid() throws (testing::TestError) {
1588 +
@test unsafe fn testParseReturnVoid() throws (testing::TestError) {
1595 1589
    let root = try! parseStmtStr("return");
1596 1590
    let case ast::NodeValue::Return(retValue) = root.value
1597 1591
        else throw testing::TestError::Failed;
1598 1592
1599 1593
    try testing::expect(retValue == nil);
1600 1594
}
1601 1595
1602 1596
/// Test parsing a `return` statement with a value.
1603 -
@test fn testParseReturnValue() throws (testing::TestError) {
1597 +
@test unsafe fn testParseReturnValue() throws (testing::TestError) {
1604 1598
    let root = try! parseStmtStr("return result");
1605 1599
    let case ast::NodeValue::Return(retValue) = root.value
1606 1600
        else throw testing::TestError::Failed;
1607 1601
1608 1602
    let value = retValue
1609 1603
        else throw testing::TestError::Failed;
1610 1604
    try expectIdent(value, "result");
1611 1605
}
1612 1606
1613 1607
/// Test parsing a `throw` statement.
1614 -
@test fn testParseThrow() throws (testing::TestError) {
1608 +
@test unsafe fn testParseThrow() throws (testing::TestError) {
1615 1609
    let root = try! parseStmtStr("throw error");
1616 1610
    let case ast::NodeValue::Throw(throwExpr) = root.value
1617 1611
        else throw testing::TestError::Failed;
1618 1612
1619 1613
    try expectIdent(throwExpr, "error");
1620 1614
}
1621 1615
1622 1616
/// Test parsing a `panic` statement without a message.
1623 -
@test fn testParsePanicEmpty() throws (testing::TestError) {
1617 +
@test unsafe fn testParsePanicEmpty() throws (testing::TestError) {
1624 1618
    let root = try! parseStmtStr("panic");
1625 1619
    let case ast::NodeValue::Panic(panicMsg) = root.value
1626 1620
        else throw testing::TestError::Failed;
1627 1621
1628 1622
    try testing::expect(panicMsg == nil);
1629 1623
}
1630 1624
1631 1625
/// Test parsing a `panic` statement with a message.
1632 -
@test fn testParsePanicMessage() throws (testing::TestError) {
1626 +
@test unsafe fn testParsePanicMessage() throws (testing::TestError) {
1633 1627
    let root = try! parseStmtStr("panic \"something went wrong\"");
1634 1628
    let case ast::NodeValue::Panic(panicMsg) = root.value
1635 1629
        else throw testing::TestError::Failed;
1636 1630
1637 1631
    let message = panicMsg
1639 1633
    let case ast::NodeValue::String(msgStr) = message.value if mem::eq(msgStr, "something went wrong")
1640 1634
        else throw testing::TestError::Failed;
1641 1635
}
1642 1636
1643 1637
/// Test parsing a `panic` statement with braces.
1644 -
@test fn testParsePanicBraces() throws (testing::TestError) {
1638 +
@test unsafe fn testParsePanicBraces() throws (testing::TestError) {
1645 1639
    let root = try! parseStmtStr("panic { \"error\" }");
1646 1640
    let case ast::NodeValue::Panic(panicMsg) = root.value
1647 1641
        else throw testing::TestError::Failed;
1648 1642
1649 1643
    let message = panicMsg
1651 1645
    let case ast::NodeValue::String(msgStr) = message.value if mem::eq(msgStr, "error")
1652 1646
        else throw testing::TestError::Failed;
1653 1647
}
1654 1648
1655 1649
/// Test parsing a simple `match` statement with one case.
1656 -
@test fn testParseMatchSingle() throws (testing::TestError) {
1650 +
@test unsafe fn testParseMatchSingle() throws (testing::TestError) {
1657 1651
    let root = try! parseStmtStr("match subject { case pattern => body }");
1658 1652
    let case ast::NodeValue::Match(sw) = root.value
1659 1653
        else throw testing::TestError::Failed;
1660 1654
1661 1655
    try expectIdent(sw.subject, "subject");
1677 1671
        if mem::eq(name, "body")
1678 1672
        else throw testing::TestError::Failed;
1679 1673
}
1680 1674
1681 1675
/// Test parsing a `match` case with guard and multiple patterns.
1682 -
@test fn testParseMatchGuard() throws (testing::TestError) {
1676 +
@test unsafe fn testParseMatchGuard() throws (testing::TestError) {
1683 1677
    let root = try! parseStmtStr(
1684 1678
        "match subject { case left, right if cond => handle }"
1685 1679
    );
1686 1680
    let case ast::NodeValue::Match(sw) = root.value
1687 1681
        else throw testing::TestError::Failed;
1700 1694
        else throw testing::TestError::Failed;
1701 1695
    try expectIdent(guard, "cond");
1702 1696
}
1703 1697
1704 1698
/// Test parsing `match` prongs whose bodies omit trailing semicolons.
1705 -
@test fn testParseMatchReturnNoSemicolon() throws (testing::TestError) {
1699 +
@test unsafe fn testParseMatchReturnNoSemicolon() throws (testing::TestError) {
1706 1700
    let root = try! parseStmtStr(
1707 1701
        "match subject { case First => return, case Second => return }"
1708 1702
    );
1709 1703
    let case ast::NodeValue::Match(sw) = root.value
1710 1704
        else throw testing::TestError::Failed;
1725 1719
        else throw testing::TestError::Failed;
1726 1720
    try testing::expect(secondRetVal == nil);
1727 1721
}
1728 1722
1729 1723
/// Test parsing a `match` statement with multiple branches.
1730 -
@test fn testParseMatchMultipleCases() throws (testing::TestError) {
1724 +
@test unsafe fn testParseMatchMultipleCases() throws (testing::TestError) {
1731 1725
    let root = try! parseStmtStr(
1732 1726
        "match subject { case First => first, case Second => second }"
1733 1727
    );
1734 1728
    let case ast::NodeValue::Match(sw) = root.value
1735 1729
        else throw testing::TestError::Failed;
1763 1757
            else throw testing::TestError::Failed;
1764 1758
    }
1765 1759
}
1766 1760
1767 1761
/// Test parsing a `match` case whose body is a block.
1768 -
@test fn testParseMatchProngBlock() throws (testing::TestError) {
1762 +
@test unsafe fn testParseMatchProngBlock() throws (testing::TestError) {
1769 1763
    let root = try! parseStmtStr("match subject { case Pattern => { body; } }");
1770 1764
    let case ast::NodeValue::Match(sw) = root.value
1771 1765
        else throw testing::TestError::Failed;
1772 1766
1773 1767
    try testing::expect(sw.prongs.len == 1);
1775 1769
        else throw testing::TestError::Failed;
1776 1770
    try expectBlockExprStmt(prong.body, ast::NodeValue::Ident("body"));
1777 1771
}
1778 1772
1779 1773
/// Test parsing a `match` statement with an `else` case.
1780 -
@test fn testParseMatchElse() throws (testing::TestError) {
1774 +
@test unsafe fn testParseMatchElse() throws (testing::TestError) {
1781 1775
    let root = try! parseStmtStr("match subject { else => body }");
1782 1776
    let case ast::NodeValue::Match(sw) = root.value
1783 1777
        else throw testing::TestError::Failed;
1784 1778
1785 1779
    try testing::expect(sw.prongs.len == 1);
1796 1790
        if mem::eq(name, "body")
1797 1791
        else throw testing::TestError::Failed;
1798 1792
}
1799 1793
1800 1794
/// Test parsing a `match` statement with a binding prong.
1801 -
@test fn testParseMatchBinding() throws (testing::TestError) {
1795 +
@test unsafe fn testParseMatchBinding() throws (testing::TestError) {
1802 1796
    let root = try! parseStmtStr("match subject { x => body }");
1803 1797
    let case ast::NodeValue::Match(sw) = root.value
1804 1798
        else throw testing::TestError::Failed;
1805 1799
1806 1800
    try testing::expect(sw.prongs.len == 1);
1816 1810
        else throw testing::TestError::Failed;
1817 1811
    try expectIdent(bodyStmt, "body");
1818 1812
}
1819 1813
1820 1814
/// Test parsing a `match` statement with a guarded binding prong.
1821 -
@test fn testParseMatchBindingGuard() throws (testing::TestError) {
1815 +
@test unsafe fn testParseMatchBindingGuard() throws (testing::TestError) {
1822 1816
    let root = try! parseStmtStr("match subject { x if x > 0 => body }");
1823 1817
    let case ast::NodeValue::Match(sw) = root.value
1824 1818
        else throw testing::TestError::Failed;
1825 1819
1826 1820
    try testing::expect(sw.prongs.len == 1);
1836 1830
        else throw testing::TestError::Failed;
1837 1831
    try testing::expect(binop.op == ast::BinaryOp::Gt);
1838 1832
}
1839 1833
1840 1834
/// Test parsing a `match` statement with a `_` wildcard.
1841 -
@test fn testParseMatchWildcard() throws (testing::TestError) {
1835 +
@test unsafe fn testParseMatchWildcard() throws (testing::TestError) {
1842 1836
    let root = try! parseStmtStr("match subject { _ => body }");
1843 1837
    let case ast::NodeValue::Match(sw) = root.value
1844 1838
        else throw testing::TestError::Failed;
1845 1839
1846 1840
    try testing::expect(sw.prongs.len == 1);
1857 1851
        else throw testing::TestError::Failed;
1858 1852
    try expectIdent(bodyStmt, "body");
1859 1853
}
1860 1854
1861 1855
/// Test parsing a `match` statement with a guarded `_` wildcard.
1862 -
@test fn testParseMatchWildcardGuard() throws (testing::TestError) {
1856 +
@test unsafe fn testParseMatchWildcardGuard() throws (testing::TestError) {
1863 1857
    let root = try! parseStmtStr("match subject { _ if cond => body }");
1864 1858
    let case ast::NodeValue::Match(sw) = root.value
1865 1859
        else throw testing::TestError::Failed;
1866 1860
1867 1861
    try testing::expect(sw.prongs.len == 1);
1879 1873
        else throw testing::TestError::Failed;
1880 1874
    try expectIdent(bodyStmt, "body");
1881 1875
}
1882 1876
1883 1877
/// Test parsing a `while let` loop with guard and else branches.
1884 -
@test fn testParseWhileLet() throws (testing::TestError) {
1878 +
@test unsafe fn testParseWhileLet() throws (testing::TestError) {
1885 1879
    let root = try! parseStmtStr(
1886 1880
        "while let value = opt; guard { body; } else { alt; }"
1887 1881
    );
1888 1882
    let case ast::NodeValue::WhileLet(loopNode) = root.value
1889 1883
        else throw testing::TestError::Failed;
1901 1895
        else throw testing::TestError::Failed;
1902 1896
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1903 1897
}
1904 1898
1905 1899
/// Test parsing a simple `loop` statement.
1906 -
@test fn testParseLoop() throws (testing::TestError) {
1900 +
@test unsafe fn testParseLoop() throws (testing::TestError) {
1907 1901
    let root = try! parseStmtStr("loop { body; }");
1908 1902
    let case ast::NodeValue::Loop(loopBody) = root.value
1909 1903
        else throw testing::TestError::Failed;
1910 1904
1911 1905
    try expectBlockExprStmt(loopBody, ast::NodeValue::Ident("body"));
1912 1906
}
1913 1907
1914 1908
/// Test parsing a `for` loop without index or else branches.
1915 -
@test fn testParseFor() throws (testing::TestError) {
1909 +
@test unsafe fn testParseFor() throws (testing::TestError) {
1916 1910
    let root = try! parseStmtStr("for item in items { body; }");
1917 1911
    let case ast::NodeValue::For(loopNode) = root.value
1918 1912
        else throw testing::TestError::Failed;
1919 1913
1920 1914
    try expectIdent(loopNode.binding, "item");
1924 1918
    try expectBlockExprStmt(loopNode.body, ast::NodeValue::Ident("body"));
1925 1919
    try testing::expect(loopNode.elseBranch == nil);
1926 1920
}
1927 1921
1928 1922
/// Test parsing a `for` loop over a range expression.
1929 -
@test fn testParseForRangeIterable() throws (testing::TestError) {
1923 +
@test unsafe fn testParseForRangeIterable() throws (testing::TestError) {
1930 1924
    let root = try! parseStmtStr("for item in 0..5 {}");
1931 1925
    let case ast::NodeValue::For(loopNode) = root.value
1932 1926
        else throw testing::TestError::Failed;
1933 1927
1934 1928
    try expectIdent(loopNode.binding, "item");
1940 1934
    try testing::expect(body.statements.len == 0);
1941 1935
    try testing::expect(loopNode.elseBranch == nil);
1942 1936
}
1943 1937
1944 1938
/// Test parsing a `for` loop with index and else branches.
1945 -
@test fn testParseForIndexElse() throws (testing::TestError) {
1939 +
@test unsafe fn testParseForIndexElse() throws (testing::TestError) {
1946 1940
    let root = try! parseStmtStr(
1947 1941
        "for value, idx in items { body; } else { alt; }"
1948 1942
    );
1949 1943
    let case ast::NodeValue::For(loopNode) = root.value
1950 1944
        else throw testing::TestError::Failed;
1962 1956
        else throw testing::TestError::Failed;
1963 1957
    try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt"));
1964 1958
}
1965 1959
1966 1960
/// Test parsing field access expression.
1967 -
@test fn testParseFieldAccess() throws (testing::TestError) {
1961 +
@test unsafe fn testParseFieldAccess() throws (testing::TestError) {
1968 1962
    let root = try! parseExprStr("obj.field");
1969 1963
    let case ast::NodeValue::FieldAccess(access) = root.value
1970 1964
        else throw testing::TestError::Failed;
1971 1965
1972 1966
    try expectIdent(access.parent, "obj");
1973 1967
    try expectIdent(access.child, "field");
1974 1968
}
1975 1969
1976 1970
/// Test parsing scope access expression.
1977 -
@test fn testParseScopeAccess() throws (testing::TestError) {
1971 +
@test unsafe fn testParseScopeAccess() throws (testing::TestError) {
1978 1972
    let root = try! parseExprStr("module::item");
1979 1973
    let case ast::NodeValue::ScopeAccess(access) = root.value
1980 1974
        else throw testing::TestError::Failed;
1981 1975
1982 1976
    try expectIdent(access.parent, "module");
1983 1977
    try expectIdent(access.child, "item");
1984 1978
}
1985 1979
1986 1980
/// Test parsing array subscript expression.
1987 -
@test fn testParseArraySubscript() throws (testing::TestError) {
1981 +
@test unsafe fn testParseArraySubscript() throws (testing::TestError) {
1988 1982
    let root = try! parseExprStr("array[index]");
1989 1983
    let case ast::NodeValue::Subscript { container, index } = root.value
1990 1984
        else throw testing::TestError::Failed;
1991 1985
1992 1986
    try expectIdent(container, "array");
1993 1987
    try expectIdent(index, "index");
1994 1988
}
1995 1989
1996 1990
/// Test parsing array slicing expressions.
1997 -
@test fn testParseArraySlicing() throws (testing::TestError) {
1991 +
@test unsafe fn testParseArraySlicing() throws (testing::TestError) {
1998 1992
    // Test `array[start..end]`.
1999 1993
    {
2000 1994
        let expr = try! parseExprStr("array[1..10]");
2001 1995
        let case ast::NodeValue::Subscript { container: subContainer, index: subIndex } = expr.value
2002 1996
            else throw testing::TestError::Failed;
2052 2046
        try testing::expect(range.end == nil);
2053 2047
    }
2054 2048
}
2055 2049
2056 2050
/// Test parsing function call expression.
2057 -
@test fn testParseFunctionCall() throws (testing::TestError) {
2051 +
@test unsafe fn testParseFunctionCall() throws (testing::TestError) {
2058 2052
    let root = try! parseExprStr("func(x, y)");
2059 2053
    let case ast::NodeValue::Call(call) = root.value
2060 2054
        else throw testing::TestError::Failed;
2061 2055
2062 2056
    try expectIdent(call.callee, "func");
2064 2058
    try expectIdent(call.args[0], "x");
2065 2059
    try expectIdent(call.args[1], "y");
2066 2060
}
2067 2061
2068 2062
/// Test parsing chained postfix operators.
2069 -
@test fn testParseChainedPostfix() throws (testing::TestError) {
2063 +
@test unsafe fn testParseChainedPostfix() throws (testing::TestError) {
2070 2064
    let root = try! parseExprStr("obj.method(arg)[0]");
2071 2065
    let case ast::NodeValue::Subscript { container: subContainer, index: subIndex } = root.value
2072 2066
        else throw testing::TestError::Failed;
2073 2067
2074 2068
    let case ast::NodeValue::Call(call) = subContainer.value
2082 2076
    try testing::expect(call.args.len == 1);
2083 2077
    try expectIdent(call.args[0], "arg");
2084 2078
}
2085 2079
2086 2080
/// Test parsing a literal cast using `as`.
2087 -
@test fn testParseAsCastLiteral() throws (testing::TestError) {
2081 +
@test unsafe fn testParseAsCastLiteral() throws (testing::TestError) {
2088 2082
    let root = try! parseExprStr("1 as i32");
2089 2083
    let case ast::NodeValue::As(asExpr) = root.value
2090 2084
        else throw testing::TestError::Failed;
2091 2085
2092 2086
    let case ast::NodeValue::Number(_) = asExpr.value.value
2094 2088
2095 2089
    try expectIntType(asExpr.type, 4, ast::Signedness::Signed);
2096 2090
}
2097 2091
2098 2092
/// Test parsing a cast following chained postfix expressions.
2099 -
@test fn testParseAsCastWithPostfix() throws (testing::TestError) {
2093 +
@test unsafe fn testParseAsCastWithPostfix() throws (testing::TestError) {
2100 2094
    let root = try! parseExprStr("value.method(arg) as u32");
2101 2095
    let case ast::NodeValue::As(asExpr) = root.value
2102 2096
        else throw testing::TestError::Failed;
2103 2097
2104 2098
    let case ast::NodeValue::Call(call) = asExpr.value.value
2113 2107
    try expectIdent(call.args[0], "arg");
2114 2108
    try expectIntType(asExpr.type, 4, ast::Signedness::Unsigned);
2115 2109
}
2116 2110
2117 2111
/// Test parsing @sizeOf builtin.
2118 -
@test fn testParseBuiltinSizeOf() throws (testing::TestError) {
2112 +
@test unsafe fn testParseBuiltinSizeOf() throws (testing::TestError) {
2119 2113
    let expr = try! parseExprStr("@sizeOf(i32)");
2120 2114
    let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value
2121 2115
        else throw testing::TestError::Failed;
2122 2116
2123 2117
    try testing::expect(builtinKind == ast::Builtin::SizeOf);
2127 2121
        sign: ast::Signedness::Signed,
2128 2122
    });
2129 2123
}
2130 2124
2131 2125
/// Test parsing @alignOf builtin.
2132 -
@test fn testParseBuiltinAlignOf() throws (testing::TestError) {
2126 +
@test unsafe fn testParseBuiltinAlignOf() throws (testing::TestError) {
2133 2127
    let expr = try! parseExprStr("@alignOf(i32)");
2134 2128
    let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value
2135 2129
        else throw testing::TestError::Failed;
2136 2130
2137 2131
    try testing::expect(builtinKind == ast::Builtin::AlignOf);
2141 2135
        sign: ast::Signedness::Signed,
2142 2136
    });
2143 2137
}
2144 2138
2145 2139
/// Test parsing @sliceOf with varying argument counts.
2146 -
@test fn testParseBuiltinSliceOf() throws (testing::TestError) {
2140 +
@test unsafe fn testParseBuiltinSliceOf() throws (testing::TestError) {
2147 2141
    // Two arguments.
2148 2142
    {
2149 2143
        let expr = try! parseExprStr("@sliceOf(ptr, len)");
2150 2144
        let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value
2151 2145
            else throw testing::TestError::Failed;
2169 2163
        try testing::expect(builtinArgs.len == 3);
2170 2164
    }
2171 2165
}
2172 2166
2173 2167
/// Test parsing prefix unary operators.
2174 -
@test fn testParseUnaryOperators() throws (testing::TestError) {
2168 +
@test unsafe fn testParseUnaryOperators() throws (testing::TestError) {
2175 2169
    {
2176 2170
        let notExpr = try! parseExprStr("not flag");
2177 2171
        let case ast::NodeValue::UnOp(notNode) = notExpr.value
2178 2172
            else throw testing::TestError::Failed;
2179 2173
        try testing::expect(notNode.op == ast::UnaryOp::Not);
2194 2188
        try expectIdent(bitNotNode.value, "mask");
2195 2189
    }
2196 2190
}
2197 2191
2198 2192
/// Test parsing dereference expressions.
2199 -
@test fn testParseDereference() throws (testing::TestError) {
2193 +
@test unsafe fn testParseDereference() throws (testing::TestError) {
2200 2194
    {
2201 2195
        let derefExpr = try! parseExprStr("*ptr");
2202 2196
        let case ast::NodeValue::Deref(target) = derefExpr.value
2203 2197
            else throw testing::TestError::Failed;
2204 2198
        try expectIdent(target, "ptr");
2213 2207
        try expectIdent(access.child, "field");
2214 2208
    }
2215 2209
}
2216 2210
2217 2211
/// Test parsing reference (address-of) expressions.
2218 -
@test fn testParseRefs() throws (testing::TestError) {
2212 +
@test unsafe fn testParseRefs() throws (testing::TestError) {
2219 2213
    {
2220 2214
        let refExpr = try! parseExprStr("&foo");
2221 2215
        let case ast::NodeValue::AddressOf(refNode) = refExpr.value
2222 2216
            else throw testing::TestError::Failed;
2223 2217
        try testing::expect(refNode.mutable == false);
2251 2245
        try expectIdent(access.child, "field");
2252 2246
    }
2253 2247
}
2254 2248
2255 2249
/// Test unary operator precedence relative to binary and postfix expressions.
2256 -
@test fn testParseUnaryPrecedence() throws (testing::TestError) {
2250 +
@test unsafe fn testParseUnaryPrecedence() throws (testing::TestError) {
2257 2251
    {
2258 2252
        let expr = try! parseExprStr("not a and b");
2259 2253
        let case ast::NodeValue::BinOp(bin) = expr.value
2260 2254
            else throw testing::TestError::Failed;
2261 2255
        try testing::expect(bin.op == ast::BinaryOp::And);
2286 2280
        try expectIdent(call.callee, "func");
2287 2281
    }
2288 2282
}
2289 2283
2290 2284
/// Test parsing assignment expressions.
2291 -
@test fn testParseAssignment() throws (testing::TestError) {
2285 +
@test unsafe fn testParseAssignment() throws (testing::TestError) {
2292 2286
    {
2293 2287
        let assign = try! parseStmtStr("set x = 1;");
2294 2288
        let case ast::NodeValue::Assign(node) = assign.value
2295 2289
            else throw testing::TestError::Failed;
2296 2290
        try expectIdent(node.left, "x");
2334 2328
        try expectIdent(access.child, "VAR");
2335 2329
    }
2336 2330
}
2337 2331
2338 2332
/// Test that assignment syntax requires the `set` statement.
2339 -
@test fn testAssignmentRequiresSetKeyword() throws (testing::TestError) {
2333 +
@test unsafe fn testAssignmentRequiresSetKeyword() throws (testing::TestError) {
2340 2334
    let assign: ?*ast::Node = try? parseStmtStr("x = 1;");
2341 2335
    try testing::expect(assign == nil);
2342 2336
2343 2337
    let compoundAssign: ?*ast::Node = try? parseStmtStr("x += 1;");
2344 2338
    try testing::expect(compoundAssign == nil);
2345 2339
}
2346 2340
2347 2341
/// Test that `set` requires an assignable target.
2348 -
@test fn testSetRequiresAssignableTarget() throws (testing::TestError) {
2342 +
@test unsafe fn testSetRequiresAssignableTarget() throws (testing::TestError) {
2349 2343
    let binTarget: ?*ast::Node = try? parseStmtStr("set x + y = 1;");
2350 2344
    try testing::expect(binTarget == nil);
2351 2345
2352 2346
    let condTarget: ?*ast::Node = try? parseStmtStr("set x if ok else y = 1;");
2353 2347
    try testing::expect(condTarget == nil);
2354 2348
}
2355 2349
2356 2350
/// Test parsing basic arithmetic binary operators (+, -, *, /, %).
2357 -
@test fn testParseBinOpArithmetic() throws (testing::TestError) {
2351 +
@test unsafe fn testParseBinOpArithmetic() throws (testing::TestError) {
2358 2352
    let add = try! parseExprStr("a + b");
2359 2353
    let case ast::NodeValue::BinOp(op1) = add.value
2360 2354
        else throw testing::TestError::Failed;
2361 2355
    try testing::expect(op1.op == ast::BinaryOp::Add);
2362 2356
    try expectIdent(op1.left, "a");
2382 2376
        else throw testing::TestError::Failed;
2383 2377
    try testing::expect(op5.op == ast::BinaryOp::Mod);
2384 2378
}
2385 2379
2386 2380
/// Test parsing comparison binary operators (==, <>).
2387 -
@test fn testParseBinOpEq() throws (testing::TestError) {
2381 +
@test unsafe fn testParseBinOpEq() throws (testing::TestError) {
2388 2382
    let eq = try! parseExprStr("a == b");
2389 2383
    let case ast::NodeValue::BinOp(op1) = eq.value
2390 2384
        else throw testing::TestError::Failed;
2391 2385
    try testing::expect(op1.op == ast::BinaryOp::Eq);
2392 2386
2395 2389
        else throw testing::TestError::Failed;
2396 2390
    try testing::expect(op2.op == ast::BinaryOp::Ne);
2397 2391
}
2398 2392
2399 2393
/// Test parsing comparison binary operators.
2400 -
@test fn testParseBinOpGtLt() throws (testing::TestError) {
2394 +
@test unsafe fn testParseBinOpGtLt() throws (testing::TestError) {
2401 2395
    let lt = try! parseExprStr("a < b");
2402 2396
    let case ast::NodeValue::BinOp(op1) = lt.value
2403 2397
        else throw testing::TestError::Failed;
2404 2398
    try testing::expect(op1.op == ast::BinaryOp::Lt);
2405 2399
2418 2412
        else throw testing::TestError::Failed;
2419 2413
    try testing::expect(op4.op == ast::BinaryOp::Gte);
2420 2414
}
2421 2415
2422 2416
/// Test parsing bitwise binary operators (&, |, ^, <<, >>).
2423 -
@test fn testParseBinOpBitwise() throws (testing::TestError) {
2417 +
@test unsafe fn testParseBinOpBitwise() throws (testing::TestError) {
2424 2418
    let bitAnd = try! parseExprStr("a & b");
2425 2419
    let case ast::NodeValue::BinOp(op1) = bitAnd.value
2426 2420
        else throw testing::TestError::Failed;
2427 2421
    try testing::expect(op1.op == ast::BinaryOp::BitAnd);
2428 2422
2446 2440
        else throw testing::TestError::Failed;
2447 2441
    try testing::expect(op5.op == ast::BinaryOp::Shr);
2448 2442
}
2449 2443
2450 2444
/// Test parsing logical binary operators (and, or).
2451 -
@test fn testParseBinOpLogical() throws (testing::TestError) {
2445 +
@test unsafe fn testParseBinOpLogical() throws (testing::TestError) {
2452 2446
    let andOp = try! parseExprStr("a and b");
2453 2447
    let case ast::NodeValue::BinOp(op1) = andOp.value
2454 2448
        else throw testing::TestError::Failed;
2455 2449
    try testing::expect(op1.op == ast::BinaryOp::And);
2456 2450
    try expectIdent(op1.left, "a");
2463 2457
    try expectIdent(op2.left, "x");
2464 2458
    try expectIdent(op2.right, "y");
2465 2459
}
2466 2460
2467 2461
/// Test operator precedence: multiplication before addition.
2468 -
@test fn testParseBinOpPrecedenceMulAdd() throws (testing::TestError) {
2462 +
@test unsafe fn testParseBinOpPrecedenceMulAdd() throws (testing::TestError) {
2469 2463
    let root = try! parseExprStr("a + b * c");
2470 2464
    let case ast::NodeValue::BinOp(add) = root.value
2471 2465
        else throw testing::TestError::Failed;
2472 2466
2473 2467
    try testing::expect(add.op == ast::BinaryOp::Add);
2480 2474
    try expectIdent(mul.left, "b");
2481 2475
    try expectIdent(mul.right, "c");
2482 2476
}
2483 2477
2484 2478
/// Test operator precedence: shifts before bitwise operations.
2485 -
@test fn testParseBinOpPrecedenceShiftBitwise() throws (testing::TestError) {
2479 +
@test unsafe fn testParseBinOpPrecedenceShiftBitwise() throws (testing::TestError) {
2486 2480
    let root = try! parseExprStr("a & b << c");
2487 2481
    let case ast::NodeValue::BinOp(bitAnd) = root.value
2488 2482
        else throw testing::TestError::Failed;
2489 2483
2490 2484
    try testing::expect(bitAnd.op == ast::BinaryOp::BitAnd);
2497 2491
    try expectIdent(shl.left, "b");
2498 2492
    try expectIdent(shl.right, "c");
2499 2493
}
2500 2494
2501 2495
/// Test operator precedence: comparison before logical AND.
2502 -
@test fn testParseBinOpPrecedenceCompareLogical() throws (testing::TestError) {
2496 +
@test unsafe fn testParseBinOpPrecedenceCompareLogical() throws (testing::TestError) {
2503 2497
    let root = try! parseExprStr("a < b and c > d");
2504 2498
    let case ast::NodeValue::BinOp(andOp) = root.value
2505 2499
        else throw testing::TestError::Failed;
2506 2500
2507 2501
    try testing::expect(andOp.op == ast::BinaryOp::And);
2518 2512
    try expectIdent(gt.left, "c");
2519 2513
    try expectIdent(gt.right, "d");
2520 2514
}
2521 2515
2522 2516
/// Test left associativity of addition.
2523 -
@test fn testParseBinOpAssociativityAdd() throws (testing::TestError) {
2517 +
@test unsafe fn testParseBinOpAssociativityAdd() throws (testing::TestError) {
2524 2518
    let root = try! parseExprStr("a + b + c");
2525 2519
    let case ast::NodeValue::BinOp(add2) = root.value
2526 2520
        else throw testing::TestError::Failed;
2527 2521
2528 2522
    try testing::expect(add2.op == ast::BinaryOp::Add);
2534 2528
    try expectIdent(add1.left, "a");
2535 2529
    try expectIdent(add1.right, "b");
2536 2530
}
2537 2531
2538 2532
/// Test complex expression with multiple operators and precedence.
2539 -
@test fn testParseBinOpComplex() throws (testing::TestError) {
2533 +
@test unsafe fn testParseBinOpComplex() throws (testing::TestError) {
2540 2534
    let root = try! parseExprStr("a + b * c - d / e");
2541 2535
    let case ast::NodeValue::BinOp(sub) = root.value
2542 2536
        else throw testing::TestError::Failed;
2543 2537
2544 2538
    try testing::expect(sub.op == ast::BinaryOp::Sub);
2556 2550
        else throw testing::TestError::Failed;
2557 2551
    try testing::expect(div.op == ast::BinaryOp::Div);
2558 2552
}
2559 2553
2560 2554
/// Test binary operators with parentheses override precedence.
2561 -
@test fn testParseBinOpParentheses() throws (testing::TestError) {
2555 +
@test unsafe fn testParseBinOpParentheses() throws (testing::TestError) {
2562 2556
    let root = try! parseExprStr("(a + b) * c");
2563 2557
    let case ast::NodeValue::BinOp(mul) = root.value
2564 2558
        else throw testing::TestError::Failed;
2565 2559
2566 2560
    try testing::expect(mul.op == ast::BinaryOp::Mul);
2572 2566
    try expectIdent(add.left, "a");
2573 2567
    try expectIdent(add.right, "b");
2574 2568
}
2575 2569
2576 2570
/// Test parsing a simple union without payloads.
2577 -
@test fn testParseEnumSimple() throws (testing::TestError) {
2571 +
@test unsafe fn testParseEnumSimple() throws (testing::TestError) {
2578 2572
    let node = try! parseStmtStr("union Color { Red, Green, Blue }");
2579 2573
    let case ast::NodeValue::UnionDecl(decl) = node.value
2580 2574
        else throw testing::TestError::Failed;
2581 2575
2582 2576
    try expectIdent(decl.name, "Color");
2609 2603
    try testing::expect(var2.type == nil);
2610 2604
    try testing::expect(var2.value == nil);
2611 2605
}
2612 2606
2613 2607
/// Test parsing a union with trailing comma.
2614 -
@test fn testParseEnumTrailingComma() throws (testing::TestError) {
2608 +
@test unsafe fn testParseEnumTrailingComma() throws (testing::TestError) {
2615 2609
    let node = try! parseStmtStr("union Letter { A, B, C, }");
2616 2610
    let case ast::NodeValue::UnionDecl(decl) = node.value
2617 2611
        else throw testing::TestError::Failed;
2618 2612
2619 2613
    try expectIdent(decl.name, "Letter");
2620 2614
    try testing::expect(decl.variants.len == 3);
2621 2615
}
2622 2616
2623 2617
/// Test parsing a union with explicit values.
2624 -
@test fn testParseEnumExplicitValues() throws (testing::TestError) {
2618 +
@test unsafe fn testParseEnumExplicitValues() throws (testing::TestError) {
2625 2619
    let node = try! parseStmtStr("union Status { Ok = 0, Error = 1, Pending = 5 }");
2626 2620
    let case ast::NodeValue::UnionDecl(decl) = node.value
2627 2621
        else throw testing::TestError::Failed;
2628 2622
2629 2623
    try expectIdent(decl.name, "Status");
2653 2647
    try testing::expect(var2.index == 2);
2654 2648
    try testing::expect(var2.value <> nil);
2655 2649
}
2656 2650
2657 2651
/// Test parsing a union with payload and tag-only variants.
2658 -
@test fn testParseEnumWithPayloads() throws (testing::TestError) {
2652 +
@test unsafe fn testParseEnumWithPayloads() throws (testing::TestError) {
2659 2653
    let node = try! parseStmtStr("union Result { Ok(bool), Error }");
2660 2654
    let case ast::NodeValue::UnionDecl(decl) = node.value
2661 2655
        else throw testing::TestError::Failed;
2662 2656
2663 2657
    try expectIdent(decl.name, "Result");
2671 2665
    let errFields = try expectVariant(decl.variants[1], "Error", 1);
2672 2666
    try testing::expect(errFields == nil);
2673 2667
}
2674 2668
2675 2669
/// Test parsing a union with derives.
2676 -
@test fn testParseEnumWithDerives() throws (testing::TestError) {
2670 +
@test unsafe fn testParseEnumWithDerives() throws (testing::TestError) {
2677 2671
    let node = try! parseStmtStr("union Option: Debug + Eq { None, Some(i32) }");
2678 2672
    let case ast::NodeValue::UnionDecl(decl) = node.value
2679 2673
        else throw testing::TestError::Failed;
2680 2674
2681 2675
    try expectIdent(decl.name, "Option");
2698 2692
    try expectIdent(var1.name, "Some");
2699 2693
    try testing::expect(var1.type <> nil);
2700 2694
}
2701 2695
2702 2696
/// Test parsing named record literals with named fields.
2703 -
@test fn testParseNamedRecordLiteralNamed() throws (testing::TestError) {
2697 +
@test unsafe fn testParseNamedRecordLiteralNamed() throws (testing::TestError) {
2704 2698
    let r1 = try! parseExprStr("Point { x: 5, y: 10 }");
2705 2699
    let case ast::NodeValue::RecordLit(lit) = r1.value
2706 2700
        else throw testing::TestError::Failed;
2707 2701
2708 2702
    let typeName = lit.typeName else throw testing::TestError::Failed;
2716 2710
    try expectIdent(label0, "x");
2717 2711
    try expectNumber(arg0.value, "5");
2718 2712
}
2719 2713
2720 2714
/// Test parsing anonymous record literals with named fields.
2721 -
@test fn testParseAnonymousRecordLiteralNamed() throws (testing::TestError) {
2715 +
@test unsafe fn testParseAnonymousRecordLiteralNamed() throws (testing::TestError) {
2722 2716
    let r1 = try! parseExprStr("{ x: 10, y: 20 }");
2723 2717
    let case ast::NodeValue::RecordLit(lit) = r1.value
2724 2718
        else throw testing::TestError::Failed;
2725 2719
2726 2720
    try testing::expect(lit.typeName == nil);
2741 2735
    try expectNumber(arg1.value, "20");
2742 2736
}
2743 2737
2744 2738
/// Test parsing record literals with shorthand field syntax.
2745 2739
/// `{ x, y }` is equivalent to `{ x: x, y: y }`.
2746 -
@test fn testParseRecordLiteralShorthand() throws (testing::TestError) {
2740 +
@test unsafe fn testParseRecordLiteralShorthand() throws (testing::TestError) {
2747 2741
    let r1 = try! parseExprStr("Point { x, y }");
2748 2742
    let case ast::NodeValue::RecordLit(lit) = r1.value
2749 2743
        else throw testing::TestError::Failed;
2750 2744
2751 2745
    let typeName = lit.typeName else throw testing::TestError::Failed;
2771 2765
    try expectIdent(arg1.value, "y");
2772 2766
    try testing::expect(label1 == arg1.value);
2773 2767
}
2774 2768
2775 2769
/// Test parsing record literals with mixed shorthand and explicit fields.
2776 -
@test fn testParseRecordLiteralMixedShorthand() throws (testing::TestError) {
2770 +
@test unsafe fn testParseRecordLiteralMixedShorthand() throws (testing::TestError) {
2777 2771
    let r1 = try! parseExprStr("Point { x, y: 10 }");
2778 2772
    let case ast::NodeValue::RecordLit(lit) = r1.value
2779 2773
        else throw testing::TestError::Failed;
2780 2774
2781 2775
    try testing::expect(lit.fields.len == 2);
2796 2790
    try expectIdent(label1, "y");
2797 2791
    try expectNumber(arg1.value, "10");
2798 2792
}
2799 2793
2800 2794
/// Test that positional brace initializers are rejected: `{ 1, 2 }`.
2801 -
@test fn testParsePositionalBraceInitializerAnonymous() throws (testing::TestError) {
2795 +
@test unsafe fn testParsePositionalBraceInitializerAnonymous() throws (testing::TestError) {
2802 2796
    let parsed: ?*ast::Node = try? parseExprStr("{ 1, 2 }");
2803 2797
    try testing::expect(parsed == nil);
2804 2798
}
2805 2799
2806 2800
/// Test that positional brace initializers are rejected: `Point { 1, 2 }`.
2807 -
@test fn testParsePositionalBraceInitializerNamed() throws (testing::TestError) {
2801 +
@test unsafe fn testParsePositionalBraceInitializerNamed() throws (testing::TestError) {
2808 2802
    let parsed: ?*ast::Node = try? parseExprStr("Point { 1, 2 }");
2809 2803
    try testing::expect(parsed == nil);
2810 2804
}
2811 2805
2812 2806
/// Test that mixed labeled/positional brace initializers are rejected: `Pt { x: 1, 2 }`.
2813 -
@test fn testParseMixedBraceInitializer() throws (testing::TestError) {
2807 +
@test unsafe fn testParseMixedBraceInitializer() throws (testing::TestError) {
2814 2808
    let parsed: ?*ast::Node = try? parseExprStr("Pt { x: 1, 2 }");
2815 2809
    try testing::expect(parsed == nil);
2816 2810
}
2817 2811
2818 -
@test fn testParseModule() throws (testing::TestError) {
2812 +
@test unsafe fn testParseModule() throws (testing::TestError) {
2819 2813
    let r = try! parseStmtsStr("fn f() {} fn g() {}");
2820 2814
2821 2815
    let case ast::NodeValue::Block(module) = r.value
2822 2816
        else throw testing::TestError::Failed;
2823 2817
    try testing::expect(module.statements.len == 2);
2832 2826
        else throw testing::TestError::Failed;
2833 2827
    try expectIdent(gDecl.name, "g");
2834 2828
}
2835 2829
2836 2830
/// Test parsing a simple conditional expression.
2837 -
@test fn testParseCondExpr() throws (testing::TestError) {
2831 +
@test unsafe fn testParseCondExpr() throws (testing::TestError) {
2838 2832
    let r = try! parseExprStr("a if cond else b") catch {
2839 2833
        throw testing::TestError::Failed;
2840 2834
    };
2841 2835
    let case ast::NodeValue::CondExpr(cond) = r.value
2842 2836
        else throw testing::TestError::Failed;
2845 2839
    try expectIdent(cond.condition, "cond");
2846 2840
    try expectIdent(cond.elseExpr, "b");
2847 2841
}
2848 2842
2849 2843
/// Test parsing a conditional expression with `as` casts.
2850 -
@test fn testParseCondExprWithAsCast() throws (testing::TestError) {
2844 +
@test unsafe fn testParseCondExprWithAsCast() throws (testing::TestError) {
2851 2845
    let r = try! parseExprStr("x as i32 if cond else y as i32") catch {
2852 2846
        throw testing::TestError::Failed;
2853 2847
    };
2854 2848
    let case ast::NodeValue::CondExpr(cond) = r.value
2855 2849
        else throw testing::TestError::Failed;
2869 2863
    try expectIdent(elseAs.value, "y");
2870 2864
    try expectIntType(elseAs.type, 4, ast::Signedness::Signed);
2871 2865
}
2872 2866
2873 2867
/// Test parsing a nested conditional expression (right-associative).
2874 -
@test fn testParseCondExprNested() throws (testing::TestError) {
2868 +
@test unsafe fn testParseCondExprNested() throws (testing::TestError) {
2875 2869
    let r = try! parseExprStr("a if x else b if y else c") catch {
2876 2870
        throw testing::TestError::Failed;
2877 2871
    };
2878 2872
    let case ast::NodeValue::CondExpr(outer) = r.value
2879 2873
        else throw testing::TestError::Failed;
2889 2883
    try expectIdent(inner.condition, "y");
2890 2884
    try expectIdent(inner.elseExpr, "c");
2891 2885
}
2892 2886
2893 2887
/// Test that trailing commas are allowed in all comma-separated lists.
2894 -
@test fn testTrailingCommas() throws (testing::TestError) {
2888 +
@test unsafe fn testTrailingCommas() throws (testing::TestError) {
2895 2889
    // Function call arguments.
2896 2890
    let call = try! parseExprStr("f(1, 2, 3,)");
2897 2891
    let case ast::NodeValue::Call(c) = call.value else throw testing::TestError::Failed;
2898 2892
    try testing::expect(c.args.len == 3);
2899 2893
2928 2922
    try testing::expect(items.len == 3);
2929 2923
2930 2924
    // Function type parameters.
2931 2925
    let fnType = try! parseTypeStr("fn (i32, bool,)");
2932 2926
    let case ast::NodeValue::TypeSig(sigValue) = fnType.value else throw testing::TestError::Failed;
2933 -
    let case ast::TypeSig::Fn(sig) = sigValue else throw testing::TestError::Failed;
2927 +
    let case ast::TypeSig::Fn { sig, .. } = sigValue else throw testing::TestError::Failed;
2934 2928
    try testing::expect(sig.params.len == 2);
2935 2929
    try testing::expect(sig.returnType == nil);
2936 2930
2937 2931
    // Throws lists.
2938 2932
    let throwsNode = try! parseStmtStr("fn handle() throws (Error, Other,) {}");
2939 2933
    let case ast::NodeValue::FnDecl(throwsDecl) = throwsNode.value else throw testing::TestError::Failed;
2940 2934
    try testing::expect(throwsDecl.sig.throwList.len == 2);
2941 2935
}
2936 +
2937 +
/// Unsafe function types preserve their call requirement and signature.
2938 +
@test unsafe fn testParseUnsafeFunctionType() throws (testing::TestError) {
2939 +
    let node = try! parseTypeStr("unsafe fn(&u32) -> u32 throws (Error)");
2940 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Fn { sig, isUnsafe }) = node.value
2941 +
        else throw testing::TestError::Failed;
2942 +
    assert isUnsafe;
2943 +
    assert sig.params.len == 1;
2944 +
    assert sig.returnType <> nil;
2945 +
    assert sig.throwList.len == 1;
2946 +
}
lib/std/lang/resolver.rad +488 -408
213 213
    /// Coerce a concrete pointer to a trait object.
214 214
    TraitObject {
215 215
        /// Trait type information.
216 216
        traitInfo: *TraitType,
217 217
        /// Instance entry for v-table lookup.
218 -
        inst: *InstanceEntry,
218 +
        inst: *unsafe InstanceEntry,
219 219
    },
220 220
}
221 221
222 222
/// Result of resolving a module path.
223 223
record ResolvedModule: Copy {
625 625
    InvalidRefPosition,
626 626
    /// A reference cannot be bound to a local.
627 627
    RefBinding,
628 628
    /// Call arguments contain overlapping incompatible loans.
629 629
    BorrowConflict(*[u8]),
630 -
    /// Unsafe pointer operation outside an `unsafe` declaration.
630 +
    /// Unsafe pointer operation outside an `unsafe` function.
631 631
    UnsafeOperation,
632 632
    /// Safe code cannot call an `unsafe` function.
633 633
    UnsafeCall,
634 634
    /// Internal error.
635 635
    Internal,
684 684
        traitInfo: *TraitType,
685 685
        /// Method index in the v-table.
686 686
        methodIndex: u32,
687 687
    },
688 688
    /// Standalone method call metadata.
689 -
    MethodCall { method: *MethodEntry },
689 +
    MethodCall { method: *unsafe MethodEntry },
690 690
    /// Slice `.append(val, allocator)` method call.
691 691
    SliceAppend { elemType: *Type },
692 692
    /// Slice `.delete(index)` method call.
693 693
    SliceDelete { elemType: *Type },
694 694
}
812 812
/// Function-local exact-use checker state.
813 813
/// Read loop arrays only at indices below `loopDepth`.
814 814
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
815 815
record LinearChecker: Copy {
816 816
    /// Resolver that owns the symbols and diagnostics.
817 -
    resolver: *mut Resolver,
817 +
    resolver: *unsafe mut Resolver,
818 818
    /// Binding count at entry to each active loop.
819 819
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
820 820
    /// Available bindings at entry to each active loop.
821 821
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
822 822
    /// Available bindings shared by the exits from each active loop.
847 847
    /// Stack of loop contexts for nested loops.
848 848
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
849 849
    /// Current loop depth, indexes into loop stack.
850 850
    loopDepth: u32,
851 851
    /// Signature of the function currently being analyzed.
852 -
    currentFn: ?*FnType,
852 +
    currentFn: ?*unsafe FnType,
853 853
    /// Current module being analyzed.
854 854
    currentMod: u16,
855 -
    /// Nesting depth of unsafe modules and function bodies.
856 -
    unsafeDepth: u32,
855 +
    /// Whether the current function permits unsafe operations.
856 +
    inUnsafeFn: bool,
857 857
    /// Configuration for semantic analysis.
858 858
    config: Config,
859 859
    /// Unified arena for symbols, scopes, and nominal type.
860 860
    arena: alloc::Arena,
861 861
    /// Combined semantic metadata table indexed by node ID.
863 863
    /// Linked list of interned types.
864 864
    types: ?*TypeNode,
865 865
    /// Diagnostics recorded so far.
866 866
    errors: *mut [Error],
867 867
    /// Module graph for the current package.
868 -
    moduleGraph: *module::ModuleGraph,
868 +
    moduleGraph: *unsafe module::ModuleGraph,
869 869
    /// Cache of module scopes indexed by module ID.
870 870
    moduleScopes: [?*mut Scope; module::MAX_MODULES],
871 871
    /// Trait instance registry.
872 872
    instances: [InstanceEntry; MAX_INSTANCES],
873 873
    /// Number of registered instances.
888 888
    ty: Type,
889 889
    next: ?*TypeNode,
890 890
}
891 891
892 892
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
893 -
export fn allocType(self: *mut Resolver, ty: Type) -> *Type {
893 +
export fn allocType(self: &mut Resolver, ty: Type) -> *Type {
894 894
    // Search existing types for a match.
895 895
    let mut cursor = self.types;
896 896
    while let node = cursor {
897 897
        if node.ty == ty {
898 898
            return &node.ty;
909 909
910 910
    return &node.ty;
911 911
}
912 912
913 913
/// Allocate a nominal type descriptor and return a pointer to it.
914 -
fn allocNominalType(self: *mut Resolver, info: NominalType) -> *mut NominalType {
914 +
fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType {
915 915
    // Nb. We don't attempt to de-duplicate nominal type entries,
916 916
    // since they don't carry node information and we create
917 917
    // placeholder entries when binding symbols.
918 918
    let entry = try! alloc::alloc(
919 919
        &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType)
923 923
924 924
    return entry;
925 925
}
926 926
927 927
/// Allocate a function type descriptor and return a pointer to it.
928 -
fn allocFnType(self: *mut Resolver, info: FnType) -> *FnType {
928 +
fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType {
929 929
    let entry = try! alloc::alloc(
930 930
        &mut self.arena, @sizeOf(FnType), @alignOf(FnType)
931 931
    ) as *mut FnType;
932 932
933 933
    set *entry = info;
934 934
935 935
    return entry;
936 936
}
937 937
938 938
/// Returns an error, if any, associated with the given node.
939 -
fn errorForNode(self: *Resolver, node: *ast::Node) -> ?*Error {
939 +
fn errorForNode(self: &Resolver, node: *ast::Node) -> ?*Error {
940 940
    for i in 0..self.errors.len {
941 941
        let err = &self.errors[i];
942 942
        if err.node == node {
943 943
            return err;
944 944
        }
1009 1009
        pkgScope: storage.pkgScope,
1010 1010
        loopStack: undefined,
1011 1011
        loopDepth: 0,
1012 1012
        currentFn: nil,
1013 1013
        currentMod: 0,
1014 -
        unsafeDepth: 0,
1014 +
        inUnsafeFn: false,
1015 1015
        config,
1016 1016
        arena,
1017 1017
        nodeData: NodeDataTable { entries: storage.nodeData },
1018 1018
        types: nil,
1019 1019
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1026 1026
        methodsLen: 0,
1027 1027
    };
1028 1028
}
1029 1029
1030 1030
/// Return `true` if there are no errors in the diagnostics.
1031 -
export fn success(diag: *Diagnostics) -> bool {
1031 +
export fn success(diag: &Diagnostics) -> bool {
1032 1032
    return diag.errors.len == 0;
1033 1033
}
1034 1034
1035 1035
/// Retrieve an error diagnostic by index, if present.
1036 1036
export fn errorAt(errs: *[Error], index: u32) -> ?*Error {
1039 1039
    }
1040 1040
    return &errs[index];
1041 1041
}
1042 1042
1043 1043
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1044 -
fn emitError(self: *mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1044 +
fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1045 1045
    // If our error list is full, just return an error without recording it.
1046 1046
    if self.errors.len >= self.errors.cap {
1047 1047
        return ResolveError::Failure;
1048 1048
    }
1049 1049
    // Don't record more than one error per node.
1056 1056
1057 1057
    return ResolveError::Failure;
1058 1058
}
1059 1059
1060 1060
/// Like [`emitError`], but for type mismatches specifically.
1061 -
fn emitTypeMismatch(self: *mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1061 +
fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1062 1062
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1063 1063
}
1064 1064
1065 1065
/// Allocate a scope object with the given symbol capacity.
1066 -
fn allocScope(self: *mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1066 +
fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1067 1067
    // Check for an existing scope for this node, and don't allocate a new
1068 1068
    // one in that case.
1069 1069
    if let scope = scopeFor(self, owner) {
1070 1070
        return scope;
1071 1071
    }
1085 1085
}
1086 1086
1087 1087
/// Enter a new local scope that is the child of the current scope.
1088 1088
/// This creates a parent/child relationship that means that lookups in the
1089 1089
/// child scope can recurse upwards.
1090 -
export fn enterScope(self: *mut Resolver, owner: *ast::Node) -> *Scope {
1090 +
export fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope {
1091 1091
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1092 1092
    set scope.parent = self.scope;
1093 1093
    set self.scope = scope;
1094 1094
    return scope;
1095 1095
}
1096 1096
1097 1097
/// Enter a module scope. Returns an object that can be used to exit the scope.
1098 -
export fn enterModuleScope(self: *mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1098 +
export fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1099 1099
    let prevScope = self.scope;
1100 1100
    let prevMod = self.currentMod;
1101 1101
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1102 1102
1103 1103
    set self.scope = scope;
1108 1108
1109 1109
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1110 1110
}
1111 1111
1112 1112
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1113 -
fn enterSubModule(self: *mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1114 -
    let modEntry = module::findChild(self.moduleGraph, name, self.currentMod)
1113 +
unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1114 +
    let modEntry = module::findChild(&*self.moduleGraph, name, self.currentMod)
1115 1115
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1116 1116
    let modRoot = modEntry.ast
1117 1117
        else panic "enterSubModule: analyzing module that wasn't parsed";
1118 1118
1119 1119
    return enterModuleScope(self, modRoot, modEntry);
1120 1120
}
1121 1121
1122 1122
/// Exit a module scope, given the object returned by `enterModuleScope`.
1123 -
export fn exitModuleScope(self: *mut Resolver, entry: ModuleScope) {
1123 +
export fn exitModuleScope(self: &mut Resolver, entry: ModuleScope) {
1124 1124
    set self.scope = entry.prevScope;
1125 1125
    set self.currentMod = entry.prevMod;
1126 1126
}
1127 1127
1128 1128
/// Exit the most recent scope.
1129 -
export fn exitScope(self: *mut Resolver) {
1129 +
export fn exitScope(self: &mut Resolver) {
1130 1130
    let parent = self.scope.parent else {
1131 1131
        // TODO: This should be a panic, but one of the tests hits this
1132 1132
        // clause, which might be a bug in the generator.
1133 1133
        return;
1134 1134
    };
1135 1135
    set self.scope = parent;
1136 1136
}
1137 1137
1138 1138
/// Visit the body of a loop while tracking nesting depth.
1139 -
fn visitLoop(self: *mut Resolver, body: *ast::Node) -> Type
1139 +
unsafe fn visitLoop(self: &mut Resolver, body: *ast::Node) -> Type
1140 1140
    throws (ResolveError)
1141 1141
{
1142 1142
    assert self.loopDepth < MAX_LOOP_DEPTH, "visitLoop: loop nesting depth exceeded";
1143 1143
    set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false };
1144 1144
    set self.loopDepth += 1;
1156 1156
    }
1157 1157
    return Type::Never;
1158 1158
}
1159 1159
1160 1160
/// Require that loop control statements appear inside a loop.
1161 -
fn ensureInsideLoop(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
1161 +
fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
1162 1162
    if self.loopDepth == 0 {
1163 1163
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1164 1164
    }
1165 1165
}
1166 1166
1167 1167
/// Bind a loop pattern to the provided type.
1168 -
fn bindForLoopPattern(self: *mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool)
1168 +
unsafe fn bindForLoopPattern(self: &mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool)
1169 1169
    throws (ResolveError)
1170 1170
{
1171 1171
    match pattern.value {
1172 1172
        case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => {
1173 1173
            let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0);
1178 1178
        }
1179 1179
    }
1180 1180
}
1181 1181
1182 1182
/// Set the expected return type for a new function body.
1183 -
fn enterFn(self: *mut Resolver, node: *ast::Node, ty: *FnType) {
1183 +
unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) {
1184 1184
    assert self.currentFn == nil, "enterFn: already in a function";
1185 -
    set self.currentFn = ty;
1185 +
    set self.currentFn = ty as *unsafe FnType;
1186 1186
    enterScope(self, node);
1187 1187
}
1188 1188
1189 1189
/// Clear the expected return type when leaving a function body.
1190 -
fn exitFn(self: *mut Resolver) {
1190 +
fn exitFn(self: &mut Resolver) {
1191 1191
    if self.currentFn == nil {
1192 1192
        // TODO: This should be a panic, but one of the tests hits this
1193 1193
        // clause, which might be a bug in the generator.
1194 1194
        return;
1195 1195
    }
1196 1196
    set self.currentFn = nil;
1197 1197
    exitScope(self);
1198 1198
}
1199 1199
1200 1200
/// Extract the identifier text from a node.
1201 -
fn nodeName(self: *mut Resolver, node: *ast::Node) -> *[u8]
1201 +
fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8]
1202 1202
    throws (ResolveError)
1203 1203
{
1204 1204
    let case ast::NodeValue::Ident(name) = node.value
1205 1205
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1206 1206
    return name;
1207 1207
}
1208 1208
1209 1209
/// Associate a resolved symbol with an AST node.
1210 -
fn setNodeSymbol(self: *mut Resolver, node: *ast::Node, symbol: *mut Symbol) {
1210 +
fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *mut Symbol) {
1211 1211
    if let existingSym = self.nodeData.entries[node.id].sym {
1212 1212
        panic "setNodeSymbol: a symbol is already associated with this node";
1213 1213
    }
1214 1214
    set self.nodeData.entries[node.id].sym = symbol;
1215 1215
}
1216 1216
1217 1217
/// Associate a resolved type with an AST node and return it.
1218 -
fn setNodeType(self: *mut Resolver, node: *ast::Node, ty: Type) -> Type {
1218 +
fn setNodeType(self: &mut Resolver, node: *ast::Node, ty: Type) -> Type {
1219 1219
    if ty == Type::Unknown {
1220 1220
        // In this case, we simply don't associate a type.
1221 1221
        return ty;
1222 1222
    }
1223 1223
    set self.nodeData.entries[node.id].ty = ty;
1236 1236
    }
1237 1237
    return Type::Void;
1238 1238
}
1239 1239
1240 1240
/// Associate a coercion plan with an AST node.
1241 -
fn setNodeCoercion(self: *mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion {
1241 +
fn setNodeCoercion(self: &mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion {
1242 1242
    if coercion == Coercion::Identity {
1243 1243
        return coercion;
1244 1244
    }
1245 1245
    set self.nodeData.entries[node.id].coercion = coercion;
1246 1246
1247 1247
    return coercion;
1248 1248
}
1249 1249
1250 1250
/// Associate a constant value with an AST node.
1251 -
fn setNodeConstValue(self: *mut Resolver, node: *ast::Node, value: ConstValue) {
1251 +
fn setNodeConstValue(self: &mut Resolver, node: *ast::Node, value: ConstValue) {
1252 1252
    set self.nodeData.entries[node.id].constValue = value;
1253 1253
}
1254 1254
1255 1255
/// Associate a record field index with a record literal field node.
1256 -
fn setRecordFieldIndex(self: *mut Resolver, node: *ast::Node, index: u32) {
1256 +
fn setRecordFieldIndex(self: &mut Resolver, node: *ast::Node, index: u32) {
1257 1257
    set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index };
1258 1258
}
1259 1259
1260 1260
/// Associate slice range metadata with a subscript expression.
1261 -
fn setSliceRangeInfo(self: *mut Resolver, node: *ast::Node, info: SliceRangeInfo) {
1261 +
fn setSliceRangeInfo(self: &mut Resolver, node: *ast::Node, info: SliceRangeInfo) {
1262 1262
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
1263 1263
}
1264 1264
1265 1265
/// Associate union variant metadata with a pattern or constructor node.
1266 -
fn setVariantInfo(self: *mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
1266 +
fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
1267 1267
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
1268 1268
}
1269 1269
1270 1270
/// Associate trait method call metadata with a call node.
1271 -
fn setTraitMethodCall(self: *mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) {
1271 +
fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) {
1272 1272
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
1273 1273
}
1274 1274
1275 1275
/// Associate for-loop metadata with a for-loop node.
1276 -
fn setForLoopInfo(self: *mut Resolver, node: *ast::Node, info: ForLoopInfo) {
1276 +
fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) {
1277 1277
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
1278 1278
}
1279 1279
1280 1280
/// Retrieve the constant value associated with a node, if any.
1281 -
export fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue {
1281 +
export fn constValueEntry(self: &Resolver, node: *ast::Node) -> ?ConstValue {
1282 1282
    return self.nodeData.entries[node.id].constValue;
1283 1283
}
1284 1284
1285 1285
/// Get the resolved record field index for a record literal field node.
1286 -
export fn recordFieldIndexFor(self: *Resolver, node: *ast::Node) -> ?u32 {
1286 +
export fn recordFieldIndexFor(self: &Resolver, node: *ast::Node) -> ?u32 {
1287 1287
    if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra {
1288 1288
        return index;
1289 1289
    }
1290 1290
    return nil;
1291 1291
}
1292 1292
1293 1293
/// Get the slice range metadata for a subscript expression with a range index.
1294 -
export fn sliceRangeInfoFor(self: *Resolver, node: *ast::Node) -> ?SliceRangeInfo {
1294 +
export fn sliceRangeInfoFor(self: &Resolver, node: *ast::Node) -> ?SliceRangeInfo {
1295 1295
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
1296 1296
        return info;
1297 1297
    }
1298 1298
    return nil;
1299 1299
}
1300 1300
1301 1301
/// Get the for-loop metadata for a for-loop node.
1302 -
export fn forLoopInfoFor(self: *Resolver, node: *ast::Node) -> ?ForLoopInfo {
1302 +
export fn forLoopInfoFor(self: &Resolver, node: *ast::Node) -> ?ForLoopInfo {
1303 1303
    if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra {
1304 1304
        return info;
1305 1305
    }
1306 1306
    return nil;
1307 1307
}
1308 1308
1309 1309
/// Associate match prong metadata with a match prong node.
1310 -
fn setProngCatchAll(self: *mut Resolver, node: *ast::Node, catchAll: bool) {
1310 +
fn setProngCatchAll(self: &mut Resolver, node: *ast::Node, catchAll: bool) {
1311 1311
    set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll };
1312 1312
}
1313 1313
1314 1314
/// Check if a prong is catch-all.
1315 -
export fn isProngCatchAll(self: *Resolver, node: *ast::Node) -> bool {
1315 +
export fn isProngCatchAll(self: &Resolver, node: *ast::Node) -> bool {
1316 1316
    if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra {
1317 1317
        return catchAll;
1318 1318
    }
1319 1319
    return false;
1320 1320
}
1321 1321
1322 1322
/// Set match metadata.
1323 -
fn setMatchConst(self: *mut Resolver, node: *ast::Node, isConst: bool) {
1323 +
fn setMatchConst(self: &mut Resolver, node: *ast::Node, isConst: bool) {
1324 1324
    set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst };
1325 1325
}
1326 1326
1327 1327
/// Check if a match has all constant patterns.
1328 -
export fn isMatchConst(self: *Resolver, node: *ast::Node) -> bool {
1328 +
export fn isMatchConst(self: &Resolver, node: *ast::Node) -> bool {
1329 1329
    if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra {
1330 1330
        return isConst;
1331 1331
    }
1332 1332
    return false;
1333 1333
}
1334 1334
1335 1335
/// Get the resolver metadata for a node.
1336 -
export fn nodeData(self: *Resolver, node: *ast::Node) -> *NodeData {
1336 +
export fn nodeData(self: &Resolver, node: *ast::Node) -> *NodeData {
1337 1337
    return &self.nodeData.entries[node.id];
1338 1338
}
1339 1339
1340 1340
/// Get the type for a node, or `nil` if unknown.
1341 -
export fn typeFor(self: *Resolver, node: *ast::Node) -> ?Type {
1341 +
export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type {
1342 1342
    let ty = self.nodeData.entries[node.id].ty;
1343 1343
    if ty == Type::Unknown {
1344 1344
        return nil;
1345 1345
    }
1346 1346
    return ty;
1347 1347
}
1348 1348
1349 1349
/// Get the scope associated with a node.
1350 -
export fn scopeFor(self: *Resolver, node: *ast::Node) -> ?*mut Scope {
1350 +
export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*mut Scope {
1351 1351
    return self.nodeData.entries[node.id].scope;
1352 1352
}
1353 1353
1354 1354
/// Get the symbol bound to a node.
1355 -
export fn symbolFor(self: *Resolver, node: *ast::Node) -> ?*mut Symbol {
1355 +
export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*mut Symbol {
1356 1356
    return self.nodeData.entries[node.id].sym;
1357 1357
}
1358 1358
1359 1359
/// Get the coercion plan associated with a node, if any.
1360 -
export fn coercionFor(self: *Resolver, node: *ast::Node) -> ?Coercion {
1360 +
export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion {
1361 1361
    let c = self.nodeData.entries[node.id].coercion;
1362 1362
    if c == Coercion::Identity {
1363 1363
        return nil;
1364 1364
    }
1365 1365
    return c;
1366 1366
}
1367 1367
1368 1368
/// Get the module ID for a symbol by walking up its scope chain.
1369 -
export fn moduleIdForSymbol(self: *Resolver, sym: *Symbol) -> ?u16 {
1369 +
export fn moduleIdForSymbol(self: &Resolver, sym: *Symbol) -> ?u16 {
1370 1370
    // For module-level symbols, return the cached module ID.
1371 1371
    if let id = sym.moduleId {
1372 1372
        return id;
1373 1373
    }
1374 1374
    // For module symbols, return the module ID directly.
1382 1382
    return nil;
1383 1383
}
1384 1384
1385 1385
/// Get the binding node for a variant pattern.
1386 1386
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
1387 -
export fn variantPatternBinding(self: *Resolver, pattern: *ast::Node) -> ?*ast::Node {
1387 +
export fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node {
1388 1388
    let case ast::NodeValue::Call(call) = pattern.value
1389 1389
        else return nil;
1390 1390
    let sym = symbolFor(self, call.callee)
1391 1391
        else return nil;
1392 1392
    let case SymbolData::Variant { .. } = sym.data
1402 1402
    }
1403 1403
    return arg;
1404 1404
}
1405 1405
1406 1406
/// Allocate a new symbol, and return a reference to it.
1407 -
fn allocSymbol(self: *mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1407 +
fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1408 1408
    let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol;
1409 1409
    set *sym = Symbol { name, data, attrs, node, moduleId: nil };
1410 1410
1411 1411
    return sym;
1412 1412
}
1413 1413
1414 1414
/// Check that a type is boolean, otherwise throw an error.
1415 -
fn checkBoolean(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1415 +
unsafe fn checkBoolean(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1416 1416
    return try checkEqual(self, node, Type::Bool);
1417 1417
}
1418 1418
1419 1419
/// Check that a type is numeric, otherwise throw an error.
1420 -
fn checkNumeric(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1420 +
unsafe fn checkNumeric(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1421 1421
    let ty = try infer(self, node);
1422 1422
    if not isNumericType(ty) {
1423 1423
        throw emitError(self, node, ErrorKind::ExpectedNumeric);
1424 1424
    }
1425 1425
    return ty;
1472 1472
        }
1473 1473
    }
1474 1474
}
1475 1475
1476 1476
/// Get the layout of a type or value.
1477 -
export fn getLayout(self: *Resolver, node: *ast::Node, ty: Type) -> Layout {
1477 +
export fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout {
1478 1478
    let mut layout = getTypeLayout(ty);
1479 1479
    // Check for symbol-specific alignment override.
1480 1480
    if let sym = symbolFor(self, node) {
1481 1481
        if let case SymbolData::Value { alignment, .. } = sym.data {
1482 1482
            if alignment > 0 {
1610 1610
    return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid };
1611 1611
}
1612 1612
1613 1613
/// Compute the discriminant tag for a variant, advancing the iota counter.
1614 1614
/// If the variant has an explicit `= N` value, uses that; otherwise uses iota.
1615 -
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: *mut u32) -> u32 {
1615 +
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: &mut u32) -> u32 {
1616 1616
    let mut tag: u32 = *iota;
1617 1617
    if let valueNode = variantDecl.value {
1618 1618
        let case ast::NodeValue::Number(lit) = valueNode.value
1619 1619
            else panic "variantTag: expected number literal";
1620 1620
        set tag = lit.magnitude as u32;
1704 1704
        }
1705 1705
    }
1706 1706
}
1707 1707
1708 1708
/// Ensure all nested nominal types in a type are resolved.
1709 -
fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
1709 +
unsafe fn ensureTypeResolved(self: &mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
1710 1710
    match ty {
1711 1711
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1712 1712
        case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site),
1713 1713
        case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse.
1714 1714
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1716 1716
        else => {},
1717 1717
    }
1718 1718
}
1719 1719
1720 1720
/// Ensure a nominal type has its body resolved.
1721 -
fn ensureNominalResolved(self: *mut Resolver, tyInfo: *NominalType, site: *ast::Node)
1721 +
unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *NominalType, site: *ast::Node)
1722 1722
    throws (ResolveError)
1723 1723
{
1724 1724
    if let case NominalType::Placeholder(declNode) = *tyInfo {
1725 1725
        // When resolving on-demand (e.g. from a child module), switch to the
1726 1726
        // declaring module's scope so field type lookups find the right symbols.
1751 1751
        set self.currentMod = prevMod;
1752 1752
    }
1753 1753
}
1754 1754
1755 1755
/// Check if all elements in a node list are assignable to the target type.
1756 -
fn isListAssignable(self: *mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool {
1756 +
unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool {
1757 1757
    for itemNode in items {
1758 1758
        let elemTy = typeFor(self, itemNode)
1759 1759
            else return false;
1760 1760
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
1761 1761
            // Do nothing.
1770 1770
fn pointerClassesAssignable(
1771 1771
    to: types::PointerClass,
1772 1772
    from: types::PointerClass,
1773 1773
) -> bool {
1774 1774
    return to == from or (
1775 -
        to == types::PointerClass::Owned
1776 -
        and from == types::PointerClass::Ref
1775 +
        to == types::PointerClass::Ref
1776 +
        and from == types::PointerClass::Owned
1777 1777
    );
1778 1778
}
1779 1779
1780 1780
/// Check if the `from` type is assignable to the `to` type, and return a
1781 1781
/// coercion plan if so.
1782 -
fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
1782 +
unsafe fn isAssignable(self: &mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
1783 +
    return isAssignableValue(self, to, from, rval, true);
1784 +
}
1785 +
1786 +
/// Check assignment while preserving function safety in referenced storage.
1787 +
unsafe fn isAssignableValue(
1788 +
    self: &mut Resolver, to: Type, from: Type, rval: *ast::Node,
1789 +
    allowFnSafetyCoercion: bool
1790 +
) -> ?Coercion {
1783 1791
    if to == Type::Unknown or from == Type::Unknown {
1784 1792
        return nil;
1785 1793
    }
1786 1794
    if from == Type::Undefined {
1787 1795
        // TODO: Don't let `undefined` be used in place of functions and other
1810 1818
            return Coercion::Identity;
1811 1819
        }
1812 1820
        if lhsMutable and not rhsMutable {
1813 1821
            return nil;
1814 1822
        }
1815 -
        return isAssignable(self, *lhsTarget, *rhsTarget, rval);
1823 +
        return isAssignableValue(self, *lhsTarget, *rhsTarget, rval, false);
1816 1824
    }
1817 1825
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
1818 1826
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
1819 1827
            if not pointerClassesAssignable(lhsClass, rhsClass)
1820 1828
                or (lhsMutable and not rhsMutable)
1848 1856
        }
1849 1857
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
1850 1858
        if *lhsItem == Type::Opaque {
1851 1859
            return Coercion::Identity;
1852 1860
        }
1853 -
        return isAssignable(self, *lhsItem, *rhsItem, rval);
1861 +
        return isAssignableValue(self, *lhsItem, *rhsItem, rval, false);
1854 1862
    }
1855 1863
    match to {
1856 1864
        case Type::Array(lhs) => {
1857 1865
            let case Type::Array(rhs) = from
1858 1866
                else return nil;
1874 1882
                        return Coercion::Identity;
1875 1883
                    }
1876 1884
                    return nil;
1877 1885
                }
1878 1886
                case ast::NodeValue::ArrayRepeatLit(repeat) => {
1879 -
                    return isAssignable(self, *lhs.item, *rhs.item, repeat.item);
1887 +
                    return isAssignableValue(self, *lhs.item, *rhs.item, repeat.item, allowFnSafetyCoercion);
1880 1888
                }
1881 1889
                else => {
1882 1890
                    if typesEqual(*lhs.item, *rhs.item) {
1883 1891
                        return Coercion::Identity;
1884 1892
                    }
1889 1897
1890 1898
        case Type::Optional(inner) => {
1891 1899
            if from == Type::Nil {
1892 1900
                return Coercion::OptionalLift(to);
1893 1901
            }
1894 -
            if let _ = isAssignable(self, *inner, from, rval) {
1902 +
            if let _ = isAssignableValue(self, *inner, from, rval, allowFnSafetyCoercion) {
1895 1903
                return Coercion::OptionalLift(to);
1896 1904
            }
1897 1905
            if let case Type::Optional(fromInner) = from {
1898 -
                return isAssignable(self, *inner, *fromInner, rval);
1906 +
                return isAssignableValue(self, *inner, *fromInner, rval, allowFnSafetyCoercion);
1899 1907
            }
1900 1908
            return nil;
1901 1909
        }
1902 1910
1903 1911
        case Type::Fn(toInfo) => {
1904 1912
            // Allow function type structural matching.
1905 1913
            if let case Type::Fn(fromInfo) = from {
1906 -
                if fnTypeEqual(toInfo, fromInfo) {
1914 +
                if fnTypeEqual(toInfo, fromInfo) or (
1915 +
                    allowFnSafetyCoercion and toInfo.isUnsafe and not fromInfo.isUnsafe
1916 +
                    and fnSignatureEqual(toInfo, fromInfo)
1917 +
                ) {
1907 1918
                    return Coercion::Identity;
1908 1919
                }
1909 1920
            }
1910 1921
            return nil;
1911 1922
        }
1941 1952
    }
1942 1953
    return nil;
1943 1954
}
1944 1955
1945 1956
/// Check if two function type descriptors are structurally equivalent.
1946 -
fn fnTypeEqual(a: *FnType, b: *FnType) -> bool {
1957 +
fn fnTypeEqual(a: &FnType, b: *FnType) -> bool {
1947 1958
    if a.isUnsafe <> b.isUnsafe {
1948 1959
        return false;
1949 1960
    }
1961 +
    return fnSignatureEqual(a, b);
1962 +
}
1963 +
1964 +
/// Compare parameter, return, and error types of functions.
1965 +
fn fnSignatureEqual(a: &FnType, b: *FnType) -> bool {
1950 1966
    if a.paramTypes.len <> b.paramTypes.len {
1951 1967
        return false;
1952 1968
    }
1953 1969
    if a.throwList.len <> b.throwList.len {
1954 1970
        return false;
2174 2190
    return false;
2175 2191
}
2176 2192
2177 2193
/// Check if the `from` type is assignable to the `to` type, and return a
2178 2194
/// coercion plan if so, or throw an error if not.
2179 -
fn expectAssignable(self: *mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
2195 +
unsafe fn expectAssignable(self: &mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
2180 2196
    // Ensure any nested nominal types are resolved before checking assignability.
2181 2197
    try ensureTypeResolved(self, to, site);
2182 2198
    if let coercion = isAssignable(self, to, from, site) {
2183 2199
        return setNodeCoercion(self, site, coercion);
2184 2200
    }
2187 2203
        actual: from,
2188 2204
    });
2189 2205
}
2190 2206
2191 2207
/// Check that a type is optional, otherwise throw an error.
2192 -
fn checkOptional(self: *mut Resolver, node: *ast::Node) -> *Type
2208 +
unsafe fn checkOptional(self: &mut Resolver, node: *ast::Node) -> *Type
2193 2209
    throws (ResolveError)
2194 2210
{
2195 2211
    if let case Type::Optional(inner) = try infer(self, node) {
2196 2212
        return inner;
2197 2213
    }
2198 2214
    throw emitError(self, node, ErrorKind::ExpectedOptional);
2199 2215
}
2200 2216
2201 2217
/// Check that a node's type is equal to the expected type.
2202 -
fn checkEqual(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type
2218 +
unsafe fn checkEqual(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type
2203 2219
    throws (ResolveError)
2204 2220
{
2205 2221
    let actualTy = try visit(self, node, expected);
2206 2222
    if actualTy <> expected {
2207 2223
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy });
2209 2225
    return actualTy;
2210 2226
}
2211 2227
2212 2228
/// Bind an identifier in the given scope.
2213 2229
fn bindIdent(
2214 -
    self: *mut Resolver,
2230 +
    self: &mut Resolver,
2215 2231
    name: *[u8],
2216 2232
    owner: *ast::Node,
2217 2233
    data: SymbolData,
2218 2234
    attrs: u32,
2219 2235
    scope: *mut Scope
2224 2240
2225 2241
    return sym;
2226 2242
}
2227 2243
2228 2244
/// Add a symbol to the given scope.
2229 -
fn addSymbolToScope(self: *mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2245 +
fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2230 2246
    for i in 0..scope.symbolsLen {
2231 2247
        if scope.symbols[i].name == sym.name {
2232 2248
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
2233 2249
        }
2234 2250
    }
2246 2262
    set scope.symbolsLen += 1;
2247 2263
}
2248 2264
2249 2265
/// Bind a value identifier in the current scope.
2250 2266
/// Returns `nil` if the identifier is a placeholder (`_`).
2251 -
fn bindValueIdent(
2252 -
    self: *mut Resolver,
2267 +
unsafe fn bindValueIdent(
2268 +
    self: &mut Resolver,
2253 2269
    ident: *ast::Node,
2254 2270
    owner: *ast::Node,
2255 2271
    type: Type,
2256 2272
    mutable: bool,
2257 2273
    alignment: u32,
2261 2277
        setNodeType(self, owner, type);
2262 2278
        return nil;
2263 2279
    }
2264 2280
    let name = try nodeName(self, ident);
2265 2281
    let data = SymbolData::Value { mutable, alignment, type, addressTaken: false };
2266 -
    let sym = try bindIdent(self, name, owner, data, attrs, self.scope);
2282 +
    let scope = self.scope;
2283 +
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
2267 2284
    setNodeType(self, owner, type);
2268 2285
    setNodeType(self, ident, type);
2269 2286
2270 2287
    // Track number of local bindings for lowering stage.
2271 2288
    if let mut fnType = self.currentFn {
2274 2291
    return sym;
2275 2292
}
2276 2293
2277 2294
/// Bind a constant identifier in the current scope.
2278 2295
fn bindConstIdent(
2279 -
    self: *mut Resolver,
2296 +
    self: &mut Resolver,
2280 2297
    ident: *ast::Node,
2281 2298
    owner: *ast::Node,
2282 2299
    type: Type,
2283 2300
    val: ?ConstValue,
2284 2301
    attrs: u32
2285 2302
) -> *mut Symbol throws (ResolveError) {
2286 2303
    let name = try nodeName(self, ident);
2287 2304
    let data = SymbolData::Constant { type, value: val };
2288 -
    let sym = try bindIdent(self, name, owner, data, attrs, self.scope);
2305 +
    let scope = self.scope;
2306 +
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
2289 2307
    setNodeType(self, owner, type);
2290 2308
    setNodeType(self, ident, type);
2291 2309
2292 2310
    return sym;
2293 2311
}
2294 2312
2295 2313
/// Bind a module identifier in the given scope.
2296 2314
/// This is used when declaring modules with `mod` or
2297 2315
/// importing modules with `use`.
2298 2316
fn bindModuleIdent(
2299 -
    self: *mut Resolver,
2317 +
    self: &mut Resolver,
2300 2318
    entry: *module::ModuleEntry,
2301 2319
    scope: *mut Scope,
2302 2320
    owner: *ast::Node,
2303 2321
    attrs: u32,
2304 2322
    bindingScope: *mut Scope
2309 2327
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
2310 2328
}
2311 2329
2312 2330
/// Bind a type identifier in the current scope.
2313 2331
fn bindTypeIdent(
2314 -
    self: *mut Resolver,
2332 +
    self: &mut Resolver,
2315 2333
    ident: *ast::Node,
2316 2334
    owner: *ast::Node,
2317 2335
    type: *mut NominalType,
2318 2336
    attrs: u32
2319 2337
) -> *mut Symbol throws (ResolveError) {
2320 2338
    let name = try nodeName(self, ident);
2321 2339
    let data = SymbolData::Type(type);
2322 -
    return try bindIdent(self, name, owner, data, attrs, self.scope);
2340 +
    let scope = self.scope;
2341 +
    return try bindIdent(self, name, owner, data, attrs, scope);
2323 2342
}
2324 2343
2325 2344
/// Predicate that matches any symbol.
2326 2345
fn isAnySymbol(_sym: *mut Symbol) -> bool {
2327 2346
    return true;
2393 2412
    return findInScopeRecursive(scope, name, isAnySymbol);
2394 2413
}
2395 2414
2396 2415
/// Flatten an identifier or scope access chain into an array of name segments.
2397 2416
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
2398 -
/// Returns a slice of the segments that were written.
2417 +
/// Return the number of segments written to the buffer.
2399 2418
fn flattenPath(
2400 -
    self: *mut Resolver,
2419 +
    self: &mut Resolver,
2401 2420
    node: *ast::Node,
2402 -
    buf: *mut [*[u8]]
2403 -
) -> *[*[u8]] throws (ResolveError) {
2404 -
    let mut out: *[*[u8]] = &[];
2421 +
    buf: &mut [*[u8]]
2422 +
) -> u32 throws (ResolveError) {
2423 +
    let mut out: u32 = 0;
2405 2424
2406 2425
    match node.value {
2407 2426
        case ast::NodeValue::Ident(name) if name.len > 0 => {
2408 2427
            assert buf.len >= 1, "flattenPath: invalid output buffer size";
2409 2428
            set buf[0] = name;
2410 -
            set out = &buf[..1];
2429 +
            set out = 1;
2411 2430
        }
2412 2431
        case ast::NodeValue::ScopeAccess(access) => {
2413 2432
            // Recursively flatten parent path.
2414 2433
            let parent = try flattenPath(self, access.parent, buf);
2415 -
            assert parent.len < buf.len, "flattenPath: invalid output buffer size";
2434 +
            assert parent < buf.len, "flattenPath: invalid output buffer size";
2416 2435
            let child = try nodeName(self, access.child);
2417 -
            set buf[parent.len] = child;
2418 -
            set out = &buf[..parent.len + 1];
2436 +
            set buf[parent] = child;
2437 +
            set out = parent + 1;
2419 2438
        }
2420 2439
        case ast::NodeValue::Super => {
2421 2440
            // `super` is handled by scope adjustment in `checkSuperAccess`.
2422 2441
            // Return empty prefix so the path continues from the next segment.
2423 -
            set out = &buf[..0];
2442 +
            set out = 0;
2424 2443
            return out;
2425 2444
        }
2426 2445
        else => {
2427 2446
            // Fallthrough to error.
2428 2447
        }
2429 2448
    }
2430 -
    if out.len < 1 {
2449 +
    if out < 1 {
2431 2450
        throw emitError(self, node, ErrorKind::InvalidIdentifier(node));
2432 2451
    }
2433 2452
    return out;
2434 2453
}
2435 2454
2449 2468
    }
2450 2469
}
2451 2470
2452 2471
/// Get the parent module scope for the current module.
2453 2472
/// Returns the scope of the parent module, or `nil` if this is a root module.
2454 -
fn getParentModuleScope(self: *mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) {
2455 -
    let currentMod = module::get(self.moduleGraph, self.currentMod)
2473 +
unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) {
2474 +
    let currentMod = module::get(&*self.moduleGraph, self.currentMod)
2456 2475
        else throw emitError(self, node, ErrorKind::Internal);
2457 2476
    let parentId = currentMod.parent
2458 2477
        else return nil; // No parent module.
2459 2478
2460 2479
    return self.moduleScopes[parentId as u32];
2461 2480
}
2462 2481
2463 2482
/// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`).
2464 2483
/// Returns the parent scope and the original node so `flattenPath` can strip `super`.
2465 -
fn checkSuperAccess(
2466 -
    self: *mut Resolver,
2484 +
unsafe fn checkSuperAccess(
2485 +
    self: &mut Resolver,
2467 2486
    node: *ast::Node
2468 2487
) -> ?SuperAccessResult throws (ResolveError) {
2469 2488
    // TODO: Maybe we should deal with `super` after the path is flattened.
2470 2489
    if let case ast::NodeValue::ScopeAccess(access) = node.value {
2471 2490
        // Direct super access: `super::x`.
2505 2524
    return symModuleId == currentModuleId;
2506 2525
}
2507 2526
2508 2527
/// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol,
2509 2528
/// starting from the given scope.
2510 -
fn resolveAccess(
2511 -
    self: *mut Resolver,
2529 +
unsafe fn resolveAccess(
2530 +
    self: &mut Resolver,
2512 2531
    node: *ast::Node,
2513 2532
    access: ast::Access,
2514 2533
    scope: *Scope
2515 2534
) -> *mut Symbol throws (ResolveError) {
2516 2535
    // Handle `super` access by adjusting scope and node.
2521 2540
        set pathNode = superAccess.child;
2522 2541
    }
2523 2542
    // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access,
2524 2543
    // while this function requires a scope access.
2525 2544
    let mut buffer: [*[u8]; 32] = undefined;
2526 -
    let path = try flattenPath(self, pathNode, &mut buffer[..]);
2545 +
    let pathLen = try flattenPath(self, pathNode, &mut buffer[..]);
2527 2546
2528 -
    return try resolvePath(self, node, access, path, startScope);
2547 +
    return try resolvePath(self, node, access, &buffer[..pathLen], startScope);
2529 2548
}
2530 2549
2531 2550
/// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol,
2532 2551
/// starting from the given scope.
2533 -
fn resolvePath(
2534 -
    self: *mut Resolver,
2552 +
unsafe fn resolvePath(
2553 +
    self: &mut Resolver,
2535 2554
    node: *ast::Node,
2536 2555
    access: ast::Access,
2537 -
    path: *[*[u8]],
2556 +
    path: &[*[u8]],
2538 2557
    scope: *Scope
2539 2558
) -> *mut Symbol throws (ResolveError) {
2540 2559
    assert path.len <> 0, "resolvePath: empty path";
2541 2560
    // Start by finding the root of the path.
2542 2561
    let root = path[0];
2543 2562
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
2544 2563
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2545 -
    let suffix = &path[1..];
2546 2564
2547 2565
    // Check visibility for symbol.
2548 2566
    if not isSymbolVisible(sym, scope, self.scope) {
2549 2567
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2550 2568
    }
2551 2569
    // End condition.
2552 -
    if suffix.len == 0 {
2570 +
    if path.len == 1 {
2553 2571
        return sym;
2554 2572
    }
2555 2573
    // Otherwise, we need to enter the next scope with the path suffix.
2556 2574
    match sym.data {
2557 2575
        case SymbolData::Module { scope, .. } => {
2558 -
            return try resolvePath(self, node, access, suffix, scope);
2576 +
            return try resolvePath(self, node, access, &path[1..], scope);
2559 2577
        }
2560 2578
        case SymbolData::Type(ty) => {
2561 2579
            // Lazily resolve union body if not yet done.
2562 2580
            try ensureNominalResolved(self, ty, node);
2563 2581
2564 2582
            if let case NominalType::Union(unionType) = *ty {
2565 2583
                // TODO: Recurse with variant so we consolidate everything.
2566 -
                if suffix.len > 1 {
2584 +
                if path.len > 2 {
2567 2585
                    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
2568 2586
                }
2569 -
                let variantName = suffix[0];
2587 +
                let variantName = path[1];
2570 2588
                let variantSym = try resolveUnionVariantAccess(
2571 2589
                    self, node, access, unionType, variantName
2572 2590
                );
2573 2591
                // TODO: This shouldn't be here.
2574 2592
                setNodeType(self, node, Type::Nominal(ty));
2580 2598
    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
2581 2599
}
2582 2600
2583 2601
/// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope.
2584 2602
/// This traverses the module hierarchy, checking visibility at each step.
2585 -
fn resolveModulePath(
2586 -
    self: *mut Resolver,
2603 +
unsafe fn resolveModulePath(
2604 +
    self: &mut Resolver,
2587 2605
    module: *ast::Node
2588 2606
) -> ResolvedModule throws (ResolveError) {
2589 2607
    let mut startScope = self.scope;
2590 2608
    let mut pathNode = module;
2591 2609
2593 2611
    if let superAccess = try checkSuperAccess(self, module) {
2594 2612
        set startScope = superAccess.scope;
2595 2613
        set pathNode = superAccess.child;
2596 2614
    }
2597 2615
    let mut pathBuf: [*[u8]; 16] = undefined;
2598 -
    let path = try flattenPath(self, pathNode, &mut pathBuf[..]);
2599 -
    if path.len == 0 {
2616 +
    let pathLen = try flattenPath(self, pathNode, &mut pathBuf[..]);
2617 +
    if pathLen == 0 {
2600 2618
        throw emitError(self, module, ErrorKind::UnresolvedSymbol(""));
2601 2619
    }
2602 -
    let parentName = path[0];
2620 +
    let parentName = pathBuf[0];
2603 2621
2604 2622
    // First, check if this is a sub-module of the start scope.
2605 2623
    if let sym = findSymbolInScope(startScope, parentName) {
2606 -
        return try resolveModulePathRecursive(self, module, &path[1..], sym);
2624 +
        return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
2607 2625
    }
2608 2626
    // Not a sub-module, so look in the global scope for a package root.
2609 2627
    let sym = findSymbolInScope(self.pkgScope, parentName)
2610 2628
        else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName));
2611 2629
2612 -
    return try resolveModulePathRecursive(self, module, &path[1..], sym);
2630 +
    return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
2613 2631
}
2614 2632
2615 2633
/// Recursively resolve the remaining path segments by traversing child modules.
2616 2634
fn resolveModulePathRecursive(
2617 -
    self: *mut Resolver,
2635 +
    self: &mut Resolver,
2618 2636
    node: *ast::Node,
2619 -
    path: *[*[u8]],
2637 +
    path: &[*[u8]],
2620 2638
    sym: *Symbol
2621 2639
) -> ResolvedModule throws (ResolveError) {
2622 2640
    let case SymbolData::Module { entry, scope } = sym.data
2623 2641
        else throw emitError(self, node, ErrorKind::Internal);
2624 2642
2639 2657
        childSym
2640 2658
    );
2641 2659
}
2642 2660
2643 2661
/// Resolve a type name, which could be an identifier or scoped path.
2644 -
fn resolveTypeName(self: *mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) {
2662 +
unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) {
2645 2663
    match node.value {
2646 2664
        case ast::NodeValue::Ident(name) => {
2647 2665
            let sym = findTypeSymbol(self.scope, name)
2648 2666
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2649 2667
            let case SymbolData::Type(ty) = sym.data
2652 2670
            setNodeSymbol(self, node, sym);
2653 2671
2654 2672
            return ty;
2655 2673
        }
2656 2674
        case ast::NodeValue::ScopeAccess(access) => {
2657 -
            let sym = try resolveAccess(self, node, access, self.scope);
2675 +
            let scope = self.scope;
2676 +
            let sym = try resolveAccess(self, node, access, scope);
2658 2677
            let case SymbolData::Type(ty) = sym.data
2659 2678
                else throw emitError(self, node, ErrorKind::Internal);
2660 2679
2661 2680
            setNodeSymbol(self, node, sym);
2662 2681
2669 2688
/// Visit a top-level declaration in the declaration phase.
2670 2689
/// This binds all names and analyzes signatures, types, and initializers.
2671 2690
/// Function bodies are deferred to the definition phase.
2672 2691
///
2673 2692
/// Nb. User-defined types are already handled by this point.
2674 -
fn visitDecl(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2693 +
unsafe fn visitDecl(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
2675 2694
    match node.value {
2676 2695
        case ast::NodeValue::FnDecl(_),
2677 2696
             ast::NodeValue::ConstDecl(_),
2678 2697
             ast::NodeValue::Mod(_),
2679 2698
             ast::NodeValue::Use(_) => {
2692 2711
            // Ignore non-declaration nodes.
2693 2712
        }
2694 2713
    }
2695 2714
}
2696 2715
2697 -
/// Require the current declaration to be unsafe.
2698 -
fn requireUnsafe(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2699 -
    if self.unsafeDepth == 0 {
2716 +
/// Require the current function to be unsafe.
2717 +
fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
2718 +
    if not self.inUnsafeFn {
2700 2719
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2701 2720
    }
2702 2721
}
2703 2722
2704 2723
/// Reject calls from safe code through unsafe function types.
2705 -
fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType)
2724 +
fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType)
2706 2725
    throws (ResolveError)
2707 2726
{
2708 -
    if info.isUnsafe and self.unsafeDepth == 0 {
2727 +
    if info.isUnsafe and not self.inUnsafeFn {
2709 2728
        throw emitError(self, node, ErrorKind::UnsafeCall);
2710 2729
    }
2711 2730
}
2712 2731
2713 2732
/// Visit a top-level definition, recursing into sub-modules.
2714 -
fn visitDef(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2733 +
unsafe fn visitDef(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
2715 2734
    match node.value {
2716 2735
        case ast::NodeValue::FnDecl(decl) => {
2717 2736
            try resolveFnDeclBody(self, node, decl) catch {
2718 2737
                return;
2719 2738
            };
2724 2743
            }
2725 2744
            let modName = try nodeName(self, decl.name);
2726 2745
            let submod = try enterSubModule(self, modName, node);
2727 2746
            let case ast::NodeValue::Block(block) = submod.root.value
2728 2747
                else panic "visitDef: expected block for module root";
2729 -
            let mut isUnsafe = false;
2730 -
            if let attrs = decl.attrs {
2731 -
                set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe);
2732 -
            }
2733 -
            if isUnsafe {
2734 -
                set self.unsafeDepth += 1;
2735 -
            }
2736 2748
            try resolveModuleDefs(self, &block) catch e {
2737 -
                if isUnsafe { set self.unsafeDepth -= 1; }
2738 2749
                exitModuleScope(self, submod);
2739 2750
                throw e;
2740 2751
            };
2741 -
            if isUnsafe {
2742 -
                set self.unsafeDepth -= 1;
2743 -
            }
2744 2752
            exitModuleScope(self, submod);
2745 2753
        }
2746 2754
        case ast::NodeValue::RecordDecl(_),
2747 2755
             ast::NodeValue::UnionDecl(_),
2748 2756
             ast::NodeValue::Use(_),
2766 2774
        }
2767 2775
    }
2768 2776
}
2769 2777
2770 2778
/// Try to infer a node's type.
2771 -
fn infer(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2779 +
unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2772 2780
    return try visit(self, node, Type::Unknown);
2773 2781
}
2774 2782
2775 2783
/// Reject nested references while allowing a direct parameter reference.
2776 -
fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type)
2784 +
fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type)
2777 2785
    throws (ResolveError)
2778 2786
{
2779 2787
    if isRefType(ty) {
2780 2788
        if let case Type::Pointer { target, .. } = ty {
2781 2789
            if containsRef(*target) {
2790 2798
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2791 2799
    }
2792 2800
}
2793 2801
2794 2802
/// Require a type that may be stored or escape a call.
2795 -
fn ensureStorableType(self: *mut Resolver, node: *ast::Node, ty: Type)
2803 +
fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type)
2796 2804
    throws (ResolveError)
2797 2805
{
2798 2806
    if containsRef(ty) {
2799 2807
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2800 2808
    }
2801 2809
}
2802 2810
2803 2811
/// Resolve a type signature node.
2804 -
fn resolveValueType(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2812 +
unsafe fn resolveValueType(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2805 2813
    let ty = try visit(self, node, Type::Unknown);
2806 2814
    // Opaque value types are not allowed.
2807 2815
    if ty == Type::Opaque {
2808 2816
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
2809 2817
    }
2810 2818
    try validateValueTypeReferences(self, node, ty);
2811 2819
    return ty;
2812 2820
}
2813 2821
2814 2822
/// Analyze a node's type and check that it can be assigned to the expected type.
2815 -
fn checkAssignable(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2823 +
unsafe fn checkAssignable(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2816 2824
    let actual = try visit(self, node, expected);
2817 2825
    let _ = try expectAssignable(self, expected, actual, node);
2818 2826
    return actual;
2819 2827
}
2820 2828
2821 2829
/// Analyze a node and propagate the resolved type.
2822 2830
/// The `hint` parameter provides type context for inference and validation.
2823 2831
/// When `nil`, the type must be inferred from the expression itself.
2824 -
fn visit(self: *mut Resolver, node: *ast::Node, hint: Type) -> Type
2832 +
unsafe fn visit(self: &mut Resolver, node: *ast::Node, hint: Type) -> Type
2825 2833
    throws (ResolveError)
2826 2834
{
2827 2835
    if let ty = typeFor(self, node) {
2828 2836
        return ty;
2829 2837
    }
2974 2982
        }
2975 2983
    }
2976 2984
}
2977 2985
2978 2986
/// Visit an optional node when present.
2979 -
fn visitOptional(self: *mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type
2987 +
unsafe fn visitOptional(self: &mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type
2980 2988
    throws (ResolveError)
2981 2989
{
2982 2990
    if let n = node {
2983 2991
        return try visit(self, n, hint);
2984 2992
    }
2985 2993
    return nil;
2986 2994
}
2987 2995
2988 2996
/// Visit every node contained in a list, returning the last resolved type.
2989 -
fn visitList(self: *mut Resolver, list: *mut [*ast::Node]) -> Type
2997 +
unsafe fn visitList(self: &mut Resolver, list: *mut [*ast::Node]) -> Type
2990 2998
    throws (ResolveError)
2991 2999
{
2992 3000
    let mut diverges = false;
2993 3001
    for item in list {
2994 3002
        if try infer(self, item) == Type::Never {
3000 3008
    }
3001 3009
    return Type::Void;
3002 3010
}
3003 3011
3004 3012
/// Collect attribute flags applied to a declaration.
3005 -
fn resolveAttributes(self: *mut Resolver, attrs: ?ast::Attributes) -> u32 {
3013 +
fn resolveAttributes(self: &mut Resolver, attrs: ?ast::Attributes) -> u32 {
3006 3014
    let list = attrs else return 0;
3007 3015
    let attrNodes = list.list;
3008 3016
    let mut mask: u32 = 0;
3009 3017
3010 3018
    for node in attrNodes {
3014 3022
    }
3015 3023
    return mask;
3016 3024
}
3017 3025
3018 3026
/// Ensure the `default` attribute is only applied to functions.
3019 -
fn ensureDefaultAttrNotAllowed(self: *mut Resolver, node: *ast::Node, attrs: u32)
3027 +
fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32)
3020 3028
    throws (ResolveError)
3021 3029
{
3022 3030
    let defaultBit = ast::Attribute::Default as u32;
3023 3031
    if (attrs & defaultBit) <> 0 {
3024 3032
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
3025 3033
    }
3026 3034
}
3027 3035
3028 3036
/// Analyze a block node, allocating a nested lexical scope.
3029 -
fn resolveBlock(self: *mut Resolver, node: *ast::Node, block: ast::Block) -> Type
3037 +
unsafe fn resolveBlock(self: &mut Resolver, node: *ast::Node, block: ast::Block) -> Type
3030 3038
    throws (ResolveError)
3031 3039
{
3032 3040
    enterScope(self, node);
3033 3041
    let blockTy = try visitList(self, block.statements) catch {
3034 3042
        // One of the statements in the block failed analysis. We simply proceed
3041 3049
3042 3050
    return setNodeType(self, node, blockTy);
3043 3051
}
3044 3052
3045 3053
/// Analyze a `let` declaration and bind its identifier.
3046 -
fn resolveLet(self: *mut Resolver, node: *ast::Node, decl: ast::Let) -> Type
3054 +
unsafe fn resolveLet(self: &mut Resolver, node: *ast::Node, decl: ast::Let) -> Type
3047 3055
    throws (ResolveError)
3048 3056
{
3049 3057
    let mut alignment: u32 = 0; // Zero is default.
3050 3058
    let mut bindingTy = Type::Unknown;
3051 3059
3102 3110
        else => return false,
3103 3111
    }
3104 3112
}
3105 3113
3106 3114
/// Determine whether a node represents a compile-time constant expression.
3107 -
export fn isConstExpr(self: *Resolver, node: *ast::Node) -> bool {
3115 +
export fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool {
3108 3116
    match node.value {
3109 3117
        case ast::NodeValue::Bool(_),
3110 3118
             ast::NodeValue::Char(_),
3111 3119
             ast::NodeValue::Number(_),
3112 3120
             ast::NodeValue::String(_),
3222 3230
            return ConstValue::Int(constIntFromBits(raw, bits, true)),
3223 3231
    }
3224 3232
}
3225 3233
3226 3234
/// Return the constant `u32` value for a slice bound when known.
3227 -
fn constSliceIndex(self: *mut Resolver, node: *ast::Node) -> ?u32 {
3235 +
fn constSliceIndex(self: &mut Resolver, node: *ast::Node) -> ?u32 {
3228 3236
    let value = constValueEntry(self, node)
3229 3237
        else return nil;
3230 3238
    let case ConstValue::Int(int) = value
3231 3239
        else return nil;
3232 3240
    if int.negative {
3240 3248
/// This function ensures that a node represents a valid, non-negative integer constant
3241 3249
/// that fits within a machine word. It is used for contexts requiring compile-time
3242 3250
/// non-negative integers, such as array sizes and alignment specifications.
3243 3251
///
3244 3252
/// Returns the unsigned magnitude of the constant as `u32`.
3245 -
fn checkSizeInt(self: *mut Resolver, node: *ast::Node) -> u32
3253 +
unsafe fn checkSizeInt(self: &mut Resolver, node: *ast::Node) -> u32
3246 3254
    throws (ResolveError)
3247 3255
{
3248 3256
    // First traverse the node expect a numeric type.
3249 3257
    let _ = try checkNumeric(self, node);
3250 3258
3267 3275
3268 3276
/// Check that constructor arguments match record fields.
3269 3277
///
3270 3278
/// Verifies argument count matches field count, and that each argument is
3271 3279
/// assignable to its corresponding field type.
3272 -
fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType)
3280 +
unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType)
3273 3281
    throws (ResolveError)
3274 3282
{
3275 3283
    try checkRecordArity(self, args, recInfo, node);
3276 3284
    for arg, i in args {
3277 3285
        let fieldType = recInfo.fields[i].fieldType;
3278 3286
        try checkAssignable(self, arg, fieldType);
3279 3287
    }
3280 3288
}
3281 3289
3282 3290
/// Check that the argument count of a constructor pattern or call matches the record field count.
3283 -
fn checkRecordArity(self: *mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3291 +
fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3284 3292
    if args.len <> recInfo.fields.len {
3285 3293
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch {
3286 3294
            expected: recInfo.fields.len as u32,
3287 3295
            actual: args.len,
3288 3296
        }));
3289 3297
    }
3290 3298
}
3291 3299
3292 3300
/// Helper for analyzing `constant` and `static` declarations.
3293 -
fn resolveConstOrStatic(
3294 -
    self: *mut Resolver,
3301 +
unsafe fn resolveConstOrStatic(
3302 +
    self: &mut Resolver,
3295 3303
    node: *ast::Node,
3296 3304
    ident: *ast::Node,
3297 3305
    typeNode: *ast::Node,
3298 3306
    valueNode: *ast::Node,
3299 3307
    attrList: ?ast::Attributes,
3325 3333
3326 3334
    return Type::Void;
3327 3335
}
3328 3336
3329 3337
/// Analyze a function declaration signature and bind the function name.
3330 -
fn resolveFnDecl(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type
3338 +
unsafe fn resolveFnDecl(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type
3331 3339
    throws (ResolveError)
3332 3340
{
3333 3341
    let attrMask = resolveAttributes(self, decl.attrs);
3334 3342
    let mut retTy = Type::Void;
3335 3343
    if let retNode = decl.sig.returnType {
3390 3398
3391 3399
    return ty;
3392 3400
}
3393 3401
3394 3402
/// Analyze a function body.
3395 -
fn resolveFnDeclBody(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
3403 +
unsafe fn resolveFnDeclBody(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
3396 3404
    let sym = symbolFor(self, node) else {
3397 3405
        // The function declaration failed to type check, therefore
3398 3406
        // no symbol was associated with it.
3399 3407
        return;
3400 3408
    };
3416 3424
        throw emitError(self, node, ErrorKind::FnMissingBody);
3417 3425
    }
3418 3426
}
3419 3427
3420 3428
/// Resolve a function or method body and restore the enclosing context.
3421 -
fn resolveExecutableBody(
3422 -
    self: *mut Resolver,
3429 +
unsafe fn resolveExecutableBody(
3430 +
    self: &mut Resolver,
3423 3431
    node: *ast::Node,
3424 3432
    fnType: *FnType,
3425 3433
    receiverName: ?*ast::Node,
3426 3434
    params: *mut [*ast::Node],
3427 3435
    body: *ast::Node,
3428 3436
) throws (ResolveError) {
3429 -
    let isUnsafe = fnType.isUnsafe;
3430 -
    if isUnsafe {
3431 -
        set self.unsafeDepth += 1;
3432 -
    }
3437 +
    let wasUnsafe = self.inUnsafeFn;
3438 +
    set self.inUnsafeFn = fnType.isUnsafe;
3433 3439
    // Enter function scope.
3434 3440
    enterFn(self, node, fnType); // Enter function scope for body analysis.
3435 3441
3436 3442
    let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e {
3437 3443
        exitFn(self);
3438 -
        if isUnsafe { set self.unsafeDepth -= 1; }
3444 +
        set self.inUnsafeFn = wasUnsafe;
3439 3445
        throw e;
3440 3446
    };
3441 3447
    exitFn(self);
3442 -
    if isUnsafe {
3443 -
        set self.unsafeDepth -= 1;
3444 -
    }
3448 +
    set self.inUnsafeFn = wasUnsafe;
3445 3449
    if missingReturn {
3446 3450
        throw emitError(self, body, ErrorKind::FnMissingReturn);
3447 3451
    }
3448 3452
}
3449 3453
3450 3454
/// Check parameters, body types, and ownership.
3451 3455
/// Return whether a required return is missing.
3452 -
fn checkExecutableBody(
3453 -
    self: *mut Resolver,
3456 +
unsafe fn checkExecutableBody(
3457 +
    self: &mut Resolver,
3454 3458
    fnType: *FnType,
3455 3459
    receiverName: ?*ast::Node,
3456 3460
    params: *mut [*ast::Node],
3457 3461
    body: *ast::Node,
3458 3462
) -> bool throws (ResolveError) {
3474 3478
    try checkLinearFn(self, receiverName, params, body);
3475 3479
    return false;
3476 3480
}
3477 3481
3478 3482
/// Analyze a function parameter and bind its identifier.
3479 -
fn resolveFnParam(self: *mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
3483 +
unsafe fn resolveFnParam(self: &mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
3480 3484
    throws (ResolveError)
3481 3485
{
3482 3486
    let ty = try resolveValueType(self, param.type);
3483 3487
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3484 3488
3492 3496
    /// The declaration permits implicit copies.
3493 3497
    copy: bool,
3494 3498
}
3495 3499
3496 3500
/// Resolve compiler-known ownership markers from a derive list.
3497 -
fn resolveOwnershipMarkers(self: *mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers
3501 +
unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers
3498 3502
    throws (ResolveError)
3499 3503
{
3500 3504
    let mut result = OwnershipMarkers { linear: false, copy: false };
3501 3505
    for derive in derives {
3502 3506
        let name = try nodeName(self, derive);
3523 3527
    }
3524 3528
    return result;
3525 3529
}
3526 3530
3527 3531
/// Resolve record fields from a node list.
3528 -
fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3532 +
unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3529 3533
    throws (ResolveError)
3530 3534
{
3531 3535
    let a = alloc::arenaAllocator(&mut self.arena);
3532 3536
    let mut result: *mut [RecordField] = &mut [];
3533 3537
    let mut currentOffset: u32 = 0;
3587 3591
        declaredCopy: false,
3588 3592
    };
3589 3593
}
3590 3594
3591 3595
/// Resolve record field types for a named record declaration.
3592 -
fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3596 +
unsafe fn resolveRecordBody(self: &mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3593 3597
    throws (ResolveError)
3594 3598
{
3595 3599
    // Get the type symbol that was bound to this declaration node.
3596 3600
    // If there's no symbol, it's because an earlier phase failed.
3597 3601
    let sym = symbolFor(self, node)
3617 3621
3618 3622
    set *nominalTy = NominalType::Record(recordType);
3619 3623
}
3620 3624
3621 3625
/// Bind a type name.
3622 -
fn bindTypeName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3626 +
fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3623 3627
    throws (ResolveError)
3624 3628
{
3625 3629
    let attrMask = resolveAttributes(self, attrs);
3626 3630
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3627 3631
3631 3635
3632 3636
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3633 3637
}
3634 3638
3635 3639
/// Allocate a trait type descriptor and return a pointer to it.
3636 -
fn allocTraitType(self: *mut Resolver, name: *[u8]) -> *mut TraitType {
3640 +
fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType {
3637 3641
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3638 3642
    let entry = p as *mut TraitType;
3639 3643
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
3640 3644
3641 3645
    return entry;
3642 3646
}
3643 3647
3644 3648
/// Bind a trait name in the current scope.
3645 -
fn bindTraitName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3649 +
fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3646 3650
    throws (ResolveError)
3647 3651
{
3648 3652
    let attrMask = resolveAttributes(self, attrs);
3649 3653
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3650 3654
3651 3655
    let traitName = try nodeName(self, name);
3652 3656
    let traitType = allocTraitType(self, traitName);
3653 3657
    let data = SymbolData::Trait(traitType);
3654 -
    let sym = try bindIdent(self, traitName, node, data, attrMask, self.scope);
3658 +
    let scope = self.scope;
3659 +
    let sym = try bindIdent(self, traitName, node, data, attrMask, scope);
3655 3660
3656 3661
    setNodeType(self, node, Type::Void);
3657 3662
    setNodeType(self, name, Type::Void);
3658 3663
3659 3664
    return sym;
3668 3673
    }
3669 3674
    return nil;
3670 3675
}
3671 3676
3672 3677
/// Resolve a trait declaration body: supertrait methods, then own methods.
3673 -
fn resolveTraitBody(self: *mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node])
3678 +
unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node])
3674 3679
    throws (ResolveError)
3675 3680
{
3676 3681
    let sym = symbolFor(self, node)
3677 3682
        else return;
3678 3683
    let case SymbolData::Trait(traitType) = sym.data
3803 3808
    }
3804 3809
}
3805 3810
3806 3811
/// Resolve a name path node to a symbol.
3807 3812
/// Used for trait and type references in instance declarations and trait objects.
3808 -
fn resolveNamePath(self: *mut Resolver, node: *ast::Node) -> *mut Symbol
3813 +
unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *mut Symbol
3809 3814
    throws (ResolveError)
3810 3815
{
3811 3816
    match node.value {
3812 3817
        case ast::NodeValue::Ident(name) => {
3813 3818
            let sym = findAnySymbol(self.scope, name)
3814 3819
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
3815 3820
            return sym;
3816 3821
        }
3817 3822
        case ast::NodeValue::ScopeAccess(access) => {
3818 -
            return try resolveAccess(self, node, access, self.scope);
3823 +
            let scope = self.scope;
3824 +
            return try resolveAccess(self, node, access, scope);
3819 3825
        }
3820 3826
        else => {
3821 3827
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
3822 3828
        }
3823 3829
    }
3824 3830
}
3825 3831
3826 3832
/// Resolve an instance declaration.
3827 3833
/// Validates that the trait exists, the target type exists, and all methods
3828 3834
/// match the trait's signatures.
3829 -
fn resolveInstanceDecl(
3830 -
    self: *mut Resolver,
3835 +
unsafe fn resolveInstanceDecl(
3836 +
    self: &mut Resolver,
3831 3837
    node: *ast::Node,
3832 3838
    traitName: *ast::Node,
3833 3839
    targetType: *ast::Node,
3834 3840
    methods: *mut [*ast::Node]
3835 3841
) throws (ResolveError) {
4031 4037
4032 4038
    setNodeType(self, node, Type::Void);
4033 4039
}
4034 4040
4035 4041
/// Resolve instance method bodies.
4036 -
fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *mut [*ast::Node])
4042 +
unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *mut [*ast::Node])
4037 4043
    throws (ResolveError)
4038 4044
{
4039 4045
    for methodNode in methods {
4040 4046
        let case ast::NodeValue::MethodDecl {
4041 4047
            name, receiverName, receiverType, sig, body, ..
4050 4056
    }
4051 4057
}
4052 4058
4053 4059
/// Resolve a method body shared by instance methods and standalone methods.
4054 4060
/// Binds the receiver and parameters, then type-checks the body.
4055 -
fn resolveMethodBody(
4056 -
    self: *mut Resolver,
4061 +
unsafe fn resolveMethodBody(
4062 +
    self: &mut Resolver,
4057 4063
    node: *ast::Node,
4058 4064
    receiverName: *ast::Node,
4059 4065
    sig: ast::FnSig,
4060 4066
    body: *ast::Node,
4061 4067
) throws (ResolveError) {
4069 4075
/// Resolve a standalone method declaration (signature only).
4070 4076
/// Validates the receiver type and registers the method in the method table.
4071 4077
4072 4078
/// Extract the type name from a resolved receiver type node.
4073 4079
fn receiverTypeName(
4074 -
    self: *mut Resolver,
4080 +
    self: &mut Resolver,
4075 4081
    receiverType: *ast::Node,
4076 4082
) -> *[u8] throws (ResolveError) {
4077 4083
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4078 4084
        receiverType.value
4079 4085
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4084 4090
4085 4091
    return sym.name;
4086 4092
}
4087 4093
4088 4094
/// Resolve and register a standalone method declaration.
4089 -
fn resolveMethodDecl(
4090 -
    self: *mut Resolver,
4095 +
unsafe fn resolveMethodDecl(
4096 +
    self: &mut Resolver,
4091 4097
    node: *ast::Node,
4092 4098
    name: *ast::Node,
4093 4099
    receiverName: *ast::Node,
4094 4100
    receiverType: *ast::Node,
4095 4101
    sig: ast::FnSig,
4196 4202
    };
4197 4203
    set self.methodsLen += 1;
4198 4204
}
4199 4205
4200 4206
/// Look up an instance entry by trait and concrete type.
4201 -
fn findInstance(self: *Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*InstanceEntry {
4207 +
unsafe fn findInstance(self: &Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
4202 4208
    for i in 0..self.instancesLen {
4203 -
        let entry = &self.instances[i];
4209 +
        let entry: *unsafe InstanceEntry = &self.instances[i];
4204 4210
        if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) {
4205 4211
            return entry;
4206 4212
        }
4207 4213
    }
4208 4214
    return nil;
4209 4215
}
4210 4216
4211 4217
/// Look up a standalone method by concrete type and name.
4212 -
export fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry {
4218 +
export unsafe fn findMethod(self: &Resolver, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
4213 4219
    for i in 0..self.methodsLen {
4214 -
        let entry = &self.methods[i];
4220 +
        let entry: *unsafe MethodEntry = &self.methods[i];
4215 4221
        if typesEqual(entry.concreteType, concreteType) and entry.name == name {
4216 4222
            return entry;
4217 4223
        }
4218 4224
    }
4219 4225
    return nil;
4220 4226
}
4221 4227
4222 4228
/// Look up a standalone method entry by its symbol.
4223 -
export fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry {
4229 +
export unsafe fn findMethodBySymbol(self: &Resolver, sym: *mut Symbol) -> ?*unsafe MethodEntry {
4224 4230
    for i in 0..self.methodsLen {
4225 -
        let entry = &self.methods[i];
4231 +
        let entry: *unsafe MethodEntry = &self.methods[i];
4226 4232
        if entry.symbol == sym {
4227 4233
            return entry;
4228 4234
        }
4229 4235
    }
4230 4236
    return nil;
4231 4237
}
4232 4238
4233 4239
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
4234 -
fn resolveUnionBody(self: *mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
4240 +
unsafe fn resolveUnionBody(self: &mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
4235 4241
    throws (ResolveError)
4236 4242
{
4237 4243
    // Get the type symbol that was bound to this declaration node.
4238 4244
    // If there's no symbol, it's because an earlier phase failed.
4239 4245
    let sym = symbolFor(self, node)
4306 4312
        declaredCopy: markers.copy,
4307 4313
    });
4308 4314
}
4309 4315
4310 4316
/// Check if a module should be analyzed based on its attributes and build configuration.
4311 -
fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool {
4317 +
fn shouldAnalyzeModule(self: &Resolver, attrs: ?ast::Attributes) -> bool {
4312 4318
    if let attributes = attrs {
4313 4319
        // Skip test modules unless we're building in test mode.
4314 4320
        if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest {
4315 4321
            return false;
4316 4322
        }
4317 4323
    }
4318 4324
    return true;
4319 4325
}
4320 4326
4321 4327
/// Analyze a module during the graph analysis phase.
4322 -
fn resolveModGraph(self: *mut Resolver, node: *ast::Node, decl: ast::Mod)
4328 +
unsafe fn resolveModGraph(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
4323 4329
    throws (ResolveError)
4324 4330
{
4325 4331
    if not shouldAnalyzeModule(self, decl.attrs) {
4326 4332
        return;
4327 4333
    }
4338 4344
4339 4345
    exitModuleScope(self, submod);
4340 4346
}
4341 4347
4342 4348
/// Analyze a module in the declaration phase.
4343 -
fn resolveModDecl(self: *mut Resolver, node: *ast::Node, decl: ast::Mod)
4349 +
unsafe fn resolveModDecl(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
4344 4350
    throws (ResolveError)
4345 4351
{
4346 4352
    if not shouldAnalyzeModule(self, decl.attrs) {
4347 4353
        return;
4348 4354
    }
4355 4361
4356 4362
    exitModuleScope(self, submod);
4357 4363
}
4358 4364
4359 4365
/// Analyze a `use` statement and create a symbol for the imported module.
4360 -
fn resolveUse(self: *mut Resolver, node: *ast::Node, decl: ast::Use) -> Type
4366 +
unsafe fn resolveUse(self: &mut Resolver, node: *ast::Node, decl: ast::Use) -> Type
4361 4367
    throws (ResolveError)
4362 4368
{
4363 4369
    let resolved = try resolveModulePath(self, decl.path);
4364 4370
    let attrMask = resolveAttributes(self, decl.attrs);
4365 4371
4371 4377
                if let existing = findSymbolInScope(self.scope, sym.name) {
4372 4378
                    if existing == sym {
4373 4379
                        continue;
4374 4380
                    }
4375 4381
                }
4376 -
                try addSymbolToScope(self, sym, self.scope, node);
4382 +
                let scope = self.scope;
4383 +
                try addSymbolToScope(self, sym, scope, node);
4377 4384
            }
4378 4385
        }
4379 4386
    } else {
4380 4387
        // Regular module import.
4381 -
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, self.scope);
4388 +
        let scope = self.scope;
4389 +
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope);
4382 4390
    }
4383 4391
    return Type::Void;
4384 4392
}
4385 4393
4386 4394
/// Analyze a standard `if` statement.
4387 -
fn resolveIf(self: *mut Resolver, node: *ast::Node, cond: ast::If) -> Type
4395 +
unsafe fn resolveIf(self: &mut Resolver, node: *ast::Node, cond: ast::If) -> Type
4388 4396
    throws (ResolveError)
4389 4397
{
4390 4398
    try checkBoolean(self, cond.condition);
4391 4399
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4392 4400
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4393 4401
4394 4402
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4395 4403
}
4396 4404
4397 4405
/// Analyze a conditional expression.
4398 -
fn resolveCondExpr(self: *mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type
4406 +
unsafe fn resolveCondExpr(self: &mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type
4399 4407
    throws (ResolveError)
4400 4408
{
4401 4409
    try checkBoolean(self, cond.condition);
4402 4410
    let thenTy = try infer(self, cond.thenExpr);
4403 4411
    let elseTy = try infer(self, cond.elseExpr);
4416 4424
4417 4425
    return setNodeType(self, node, thenTy);
4418 4426
}
4419 4427
4420 4428
/// Analyze a pattern match structure (used by if-let, while-let).
4421 -
fn resolvePatternMatch(self: *mut Resolver, node: *ast::Node, pat: *ast::PatternMatch)
4429 +
unsafe fn resolvePatternMatch(self: &mut Resolver, node: *ast::Node, pat: &ast::PatternMatch)
4422 4430
    throws (ResolveError)
4423 4431
{
4424 4432
    match pat.kind {
4425 4433
        case ast::PatternKind::Case => {
4426 4434
            // Analyze pattern against scrutinee type.
4441 4449
        try checkBoolean(self, guard);
4442 4450
    }
4443 4451
}
4444 4452
4445 4453
/// Analyze an `if let` or `if let case` pattern binding.
4446 -
fn resolveIfLet(self: *mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type
4454 +
unsafe fn resolveIfLet(self: &mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type
4447 4455
    throws (ResolveError)
4448 4456
{
4449 4457
    enterScope(self, node);
4450 4458
    try resolvePatternMatch(self, node, &cond.pattern);
4451 4459
4479 4487
4480 4488
/// Analyze a case pattern for match, if-case, let-case, or while-case.
4481 4489
///
4482 4490
/// At the top level, bare identifiers are compared against existing values.
4483 4491
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
4484 -
fn resolveCasePattern(
4485 -
    self: *mut Resolver,
4492 +
unsafe fn resolveCasePattern(
4493 +
    self: &mut Resolver,
4486 4494
    pattern: *ast::Node,
4487 4495
    scrutineeTy: Type,
4488 4496
    mode: IdentMode,
4489 4497
    matchBy: MatchBy
4490 4498
) throws (ResolveError) {
4545 4553
        }
4546 4554
    }
4547 4555
}
4548 4556
4549 4557
/// Analyze a traditional `while` loop.
4550 -
fn resolveWhile(self: *mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type
4558 +
unsafe fn resolveWhile(self: &mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type
4551 4559
    throws (ResolveError)
4552 4560
{
4553 4561
    try checkBoolean(self, loopNode.condition);
4554 4562
    try visitLoop(self, loopNode.body);
4555 4563
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4556 4564
4557 4565
    return setNodeType(self, node, Type::Void);
4558 4566
}
4559 4567
4560 4568
/// Analyze a `while let` loop with pattern binding.
4561 -
fn resolveWhileLet(self: *mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type
4569 +
unsafe fn resolveWhileLet(self: &mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type
4562 4570
    throws (ResolveError)
4563 4571
{
4564 4572
    enterScope(self, node);
4565 4573
    try resolvePatternMatch(self, node, &loopNode.pattern);
4566 4574
4571 4579
4572 4580
    return setNodeType(self, node, Type::Void);
4573 4581
}
4574 4582
4575 4583
/// Analyze a `for` loop, binding iteration variables.
4576 -
fn resolveFor(self: *mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type
4584 +
unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type
4577 4585
    throws (ResolveError)
4578 4586
{
4579 4587
    let iterableTy = try infer(self, forStmt.iterable);
4580 4588
4581 4589
    // Extract binding names for the lowerer.
4688 4696
4689 4697
/// Check whether a pattern contains nested sub-patterns that further
4690 4698
/// refine the match beyond the outer variant (e.g. nested union variant
4691 4699
/// tests or literal comparisons). Used to allow the same outer variant
4692 4700
/// to appear in multiple match arms.
4693 -
fn hasNestedRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool {
4701 +
fn hasNestedRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
4694 4702
    for i in 0..patternSubCount(pattern) {
4695 4703
        if let sub = patternSubElement(pattern, i) {
4696 4704
            if isRefiningPattern(self, sub) {
4697 4705
                return true;
4698 4706
            }
4703 4711
4704 4712
/// Check whether a single pattern node is a refining pattern that tests
4705 4713
/// a value rather than just binding it. Union variants, literals, and
4706 4714
/// scope accesses are refining; identifiers, placeholders, and plain
4707 4715
/// record destructurings are not.
4708 -
fn isRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool {
4716 +
fn isRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
4709 4717
    match pattern.value {
4710 4718
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
4711 4719
            return false,
4712 4720
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
4713 4721
            if let keyNode = patternVariantKeyNode(pattern) {
4770 4778
    return true;
4771 4779
}
4772 4780
4773 4781
/// Analyze a match prong, checking for duplicate catch-alls. Returns the
4774 4782
/// unified match type.
4775 -
fn resolveMatchProng(
4776 -
    self: *mut Resolver,
4783 +
unsafe fn resolveMatchProng(
4784 +
    self: &mut Resolver,
4777 4785
    prongNode: *ast::Node,
4778 4786
    prong: ast::MatchProng,
4779 4787
    subjectTy: Type,
4780 -
    state: *mut MatchState,
4788 +
    state: &mut MatchState,
4781 4789
    matchType: Type,
4782 4790
    matchBy: MatchBy
4783 4791
) -> Type throws (ResolveError) {
4784 4792
    // Whether this prong is catch-all.
4785 4793
    let mut isCatchAll = false;
4804 4812
    return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
4805 4813
}
4806 4814
4807 4815
/// Analyze a `match` expression. Dispatches to specialized functions based on
4808 4816
/// the subject type.
4809 -
fn resolveMatch(self: *mut Resolver, node: *ast::Node, sw: ast::Match) -> Type
4817 +
unsafe fn resolveMatch(self: &mut Resolver, node: *ast::Node, sw: ast::Match) -> Type
4810 4818
    throws (ResolveError)
4811 4819
{
4812 4820
    let subjectTy = try infer(self, sw.subject);
4813 4821
    let subject = unwrapMatchSubject(subjectTy);
4814 4822
4832 4840
    };
4833 4841
    return ty;
4834 4842
}
4835 4843
4836 4844
/// Analyze a `match` expression on an optional subject.
4837 -
fn resolveMatchOptional(
4838 -
    self: *mut Resolver,
4845 +
unsafe fn resolveMatchOptional(
4846 +
    self: &mut Resolver,
4839 4847
    node: *ast::Node,
4840 4848
    sw: ast::Match,
4841 4849
    innerTy: *Type,
4842 4850
    matchBy: MatchBy
4843 4851
) -> Type throws (ResolveError)
4905 4913
    }
4906 4914
    return setNodeType(self, node, matchType);
4907 4915
}
4908 4916
4909 4917
/// Analyze a `match` expression on a union subject.
4910 -
fn resolveMatchUnion(
4911 -
    self: *mut Resolver,
4918 +
unsafe fn resolveMatchUnion(
4919 +
    self: &mut Resolver,
4912 4920
    node: *ast::Node,
4913 4921
    sw: ast::Match,
4914 4922
    subjectTy: Type,
4915 4923
    info: UnionType,
4916 4924
    matchBy: MatchBy
4961 4969
    return setNodeType(self, node, matchType);
4962 4970
}
4963 4971
4964 4972
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
4965 4973
/// booleans must cover both `true` and `false`, other types require a catch-all.
4966 -
fn resolveMatchGeneric(self: *mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type
4974 +
unsafe fn resolveMatchGeneric(self: &mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type
4967 4975
    throws (ResolveError)
4968 4976
{
4969 4977
    let prongs = sw.prongs;
4970 4978
    let mut state = MatchState { catchAll: false, isConst: true };
4971 4979
    let mut matchType = Type::Never;
5030 5038
5031 5039
    return setNodeType(self, node, matchType);
5032 5040
}
5033 5041
5034 5042
/// Analyze a single `match` prong branch. Returns the unified match type.
5035 -
fn visitMatchProng(
5036 -
    self: *mut Resolver,
5043 +
unsafe fn visitMatchProng(
5044 +
    self: &mut Resolver,
5037 5045
    node: *ast::Node,
5038 5046
    prongNode: ast::MatchProng,
5039 5047
    subjectTy: Type,
5040 5048
    matchType: Type,
5041 5049
    matchBy: MatchBy
5050 5058
5051 5059
    return unifyBranches(matchType, prongTy);
5052 5060
}
5053 5061
5054 5062
/// Analyze the contents of a `match` prong while inside the prong scope.
5055 -
fn resolveMatchProngBody(
5056 -
    self: *mut Resolver,
5063 +
unsafe fn resolveMatchProngBody(
5064 +
    self: &mut Resolver,
5057 5065
    prong: ast::MatchProng,
5058 5066
    subjectTy: Type,
5059 5067
    matchBy: MatchBy
5060 5068
) -> Type throws (ResolveError) {
5061 5069
    match prong.arm {
5079 5087
    }
5080 5088
    return try visit(self, prong.body, Type::Void);
5081 5089
}
5082 5090
5083 5091
/// Ensure a scope access pattern references a compatible union variant.
5084 -
fn resolveUnionScopePattern(
5085 -
    self: *mut Resolver,
5092 +
unsafe fn resolveUnionScopePattern(
5093 +
    self: &mut Resolver,
5086 5094
    pattern: *ast::Node,
5087 5095
    access: ast::Access,
5088 5096
    subjectTy: Type,
5089 5097
    unionType: UnionType
5090 5098
) throws (ResolveError) {
5105 5113
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
5106 5114
    }
5107 5115
}
5108 5116
5109 5117
/// Validate and bind a union constructor call used as a `match` pattern.
5110 -
fn resolveUnionCallPattern(
5111 -
    self: *mut Resolver,
5118 +
unsafe fn resolveUnionCallPattern(
5119 +
    self: &mut Resolver,
5112 5120
    pattern: *ast::Node,
5113 5121
    call: ast::Call,
5114 5122
    subjectTy: Type,
5115 5123
    unionType: UnionType,
5116 5124
    matchBy: MatchBy
5129 5137
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5130 5138
    }
5131 5139
}
5132 5140
5133 5141
/// Bind the payload introduced by a union constructor pattern.
5134 -
fn bindUnionPatternPayload(
5135 -
    self: *mut Resolver,
5142 +
unsafe fn bindUnionPatternPayload(
5143 +
    self: &mut Resolver,
5136 5144
    pattern: *ast::Node,
5137 5145
    call: ast::Call,
5138 5146
    variantName: *[u8],
5139 5147
    payloadTy: Type,
5140 5148
    matchBy: MatchBy
5150 5158
5151 5159
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5152 5160
}
5153 5161
5154 5162
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
5155 -
fn bindPatternVar(self: *mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy)
5163 +
unsafe fn bindPatternVar(self: &mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy)
5156 5164
    throws (ResolveError)
5157 5165
{
5158 5166
    let mut bindTy = ty;
5159 5167
    match matchBy {
5160 5168
        case MatchBy::Value => {}
5183 5191
        }
5184 5192
    }
5185 5193
}
5186 5194
5187 5195
/// Bind record pattern fields to variables in the current scope.
5188 -
fn bindRecordPatternFields(
5189 -
    self: *mut Resolver,
5196 +
unsafe fn bindRecordPatternFields(
5197 +
    self: &mut Resolver,
5190 5198
    pattern: *ast::Node,
5191 5199
    recInfo: RecordType,
5192 5200
    matchBy: MatchBy
5193 5201
) throws (ResolveError) {
5194 5202
    match pattern.value {
5224 5232
        else => throw emitError(self, pattern, ErrorKind::Internal)
5225 5233
    }
5226 5234
}
5227 5235
5228 5236
/// Validate and bind a record literal pattern for matching labeled union variants.
5229 -
fn resolveUnionRecordPattern(
5230 -
    self: *mut Resolver,
5237 +
unsafe fn resolveUnionRecordPattern(
5238 +
    self: &mut Resolver,
5231 5239
    pattern: *ast::Node,
5232 5240
    lit: ast::RecordLit,
5233 5241
    subjectTy: Type,
5234 5242
    unionType: UnionType,
5235 5243
    matchBy: MatchBy
5261 5269
5262 5270
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5263 5271
}
5264 5272
5265 5273
/// Analyze a pattern appearing in a union case.
5266 -
fn resolveUnionPattern(
5267 -
    self: *mut Resolver,
5274 +
unsafe fn resolveUnionPattern(
5275 +
    self: &mut Resolver,
5268 5276
    pattern: *ast::Node,
5269 5277
    subjectTy: Type,
5270 5278
    unionType: UnionType,
5271 5279
    matchBy: MatchBy
5272 5280
) throws (ResolveError) {
5318 5326
    }
5319 5327
    return false;
5320 5328
}
5321 5329
5322 5330
/// Analyze a `let-else` guard.
5323 -
fn resolveLetElse(self: *mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
5331 +
unsafe fn resolveLetElse(self: &mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
5324 5332
    throws (ResolveError)
5325 5333
{
5326 -
    let pat = &letElse.pattern;
5334 +
    let pat = letElse.pattern;
5327 5335
    let exprTy = try infer(self, pat.scrutinee);
5328 5336
5329 5337
    match pat.kind {
5330 5338
        case ast::PatternKind::Binding => {
5331 5339
            // Simple binding requires an optional expression.
5365 5373
    }
5366 5374
    return setNodeType(self, node, Type::Void);
5367 5375
}
5368 5376
5369 5377
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
5370 -
fn resolveBuiltinCall(
5371 -
    self: *mut Resolver,
5378 +
unsafe fn resolveBuiltinCall(
5379 +
    self: &mut Resolver,
5372 5380
    node: *ast::Node,
5373 5381
    kind: ast::Builtin,
5374 5382
    args: *mut [*ast::Node]
5375 5383
) -> Type throws (ResolveError) {
5376 5384
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
5430 5438
    return setNodeType(self, node, Type::U32);
5431 5439
}
5432 5440
5433 5441
/// Validate call arguments against a function type: check argument count,
5434 5442
/// type-check each argument, and verify that throwing functions use `try`.
5435 -
fn checkCallArgs(self: *mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
5443 +
unsafe fn checkCallArgs(self: &mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
5436 5444
    throws (ResolveError)
5437 5445
{
5438 5446
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
5439 5447
        throw emitError(self, node, ErrorKind::MissingTry);
5440 5448
    }
5450 5458
        try checkAssignable(self, argNode, expectedTy);
5451 5459
    }
5452 5460
}
5453 5461
5454 5462
/// Analyze a function call expression.
5455 -
fn resolveCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type
5463 +
unsafe fn resolveCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type
5456 5464
    throws (ResolveError)
5457 5465
{
5458 5466
    // Intercept method calls on slices before inferring the callee.
5459 5467
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5460 5468
        let parentTy = try infer(self, access.parent);
5565 5573
    // Associate return type to call.
5566 5574
    return setNodeType(self, node, *info.returnType);
5567 5575
}
5568 5576
5569 5577
/// Resolve `slice.append(val, allocator)`.
5570 -
fn resolveSliceAppend(
5571 -
    self: *mut Resolver,
5578 +
unsafe fn resolveSliceAppend(
5579 +
    self: &mut Resolver,
5572 5580
    node: *ast::Node,
5573 5581
    parent: *ast::Node,
5574 5582
    parentType: Type,
5575 5583
    args: *mut [*ast::Node],
5576 5584
    elemType: *Type,
5595 5603
    // Return the parent's type so the caller can rebind:
5596 5604
    return setNodeType(self, node, parentType);
5597 5605
}
5598 5606
5599 5607
/// Resolve `slice.delete(index)`.
5600 -
fn resolveSliceDelete(
5601 -
    self: *mut Resolver,
5608 +
unsafe fn resolveSliceDelete(
5609 +
    self: &mut Resolver,
5602 5610
    node: *ast::Node,
5603 5611
    parent: *ast::Node,
5604 5612
    args: *mut [*ast::Node],
5605 5613
    elemType: *Type,
5606 5614
    mutable: bool
5619 5627
5620 5628
    return setNodeType(self, node, Type::Void);
5621 5629
}
5622 5630
5623 5631
/// Analyze an assignment expression.
5624 -
fn resolveAssign(self: *mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
5632 +
unsafe fn resolveAssign(self: &mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
5625 5633
    throws (ResolveError)
5626 5634
{
5627 5635
    // Slice assignment: `slice[range] = value`.
5628 5636
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
5629 5637
        if let case ast::NodeValue::Range(range) = index.value {
5682 5690
5683 5691
    return setNodeType(self, node, leftTy);
5684 5692
}
5685 5693
5686 5694
/// Ensure slice range bounds are valid `u32` values.
5687 -
fn checkSliceRangeIndices(self: *mut Resolver, range: ast::Range) throws (ResolveError) {
5695 +
unsafe fn checkSliceRangeIndices(self: &mut Resolver, range: ast::Range) throws (ResolveError) {
5688 5696
    if let start = range.start {
5689 5697
        try checkIndex(self, start);
5690 5698
    }
5691 5699
    if let end = range.end {
5692 5700
        try checkIndex(self, end);
5693 5701
    }
5694 5702
}
5695 5703
5696 5704
/// Emit an error when a slice range with compile-tyime values exceeds the array length.
5697 -
fn validateArraySliceBounds(self: *mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5705 +
fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5698 5706
    let mut startVal: ?u32 = nil;
5699 5707
    let mut endVal: ?u32 = length;
5700 5708
5701 5709
    if let startNode = range.start {
5702 5710
        if let val = constSliceIndex(self, startNode) {
5722 5730
}
5723 5731
5724 5732
/// Check that an index expression has an unsigned integer type.
5725 5733
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
5726 5734
/// Smaller types are widened to `u32` via a numeric cast coercion.
5727 -
fn checkIndex(self: *mut Resolver, indexNode: *ast::Node) throws (ResolveError) {
5735 +
unsafe fn checkIndex(self: &mut Resolver, indexNode: *ast::Node) throws (ResolveError) {
5728 5736
    let indexTy = try visit(self, indexNode, Type::U32);
5729 5737
    if indexTy == Type::Int or indexTy == Type::U32 {
5730 5738
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
5731 5739
        return;
5732 5740
    }
5744 5752
        }
5745 5753
    }
5746 5754
}
5747 5755
5748 5756
/// Analyze an array or slice subscript expression.
5749 -
fn resolveSubscript(self: *mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
5757 +
unsafe fn resolveSubscript(self: &mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
5750 5758
    throws (ResolveError)
5751 5759
{
5752 5760
    // Range subscripts always require `&` to form a slice.
5753 5761
    if let case ast::NodeValue::Range(range) = indexNode.value {
5754 5762
        let _ = try infer(self, indexNode);
5775 5783
        }
5776 5784
    }
5777 5785
}
5778 5786
5779 5787
/// Find a record field by name.
5780 -
fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 {
5788 +
fn findRecordField(s: &RecordType, fieldName: *[u8]) -> ?u32 {
5781 5789
    for field, i in s.fields {
5782 5790
        if let name = field.name {
5783 5791
            if name == fieldName {
5784 5792
                return i;
5785 5793
            }
5787 5795
    }
5788 5796
    return nil;
5789 5797
}
5790 5798
5791 5799
/// Analyze a union constructor call with payload.
5792 -
fn resolveUnionConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type
5800 +
unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type
5793 5801
    throws (ResolveError)
5794 5802
{
5795 5803
    // Get the union nominal type.
5796 5804
    let case NominalType::Union(unionType) = *unionNominal
5797 5805
        else panic "resolveUnionConstructorCall: not a union type";
5822 5830
/// Analyze an unlabeled record constructor call.
5823 5831
///
5824 5832
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
5825 5833
/// number of arguments matches the record's field count and that each argument
5826 5834
/// is assignable to its corresponding field type.
5827 -
fn resolveRecordConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type
5835 +
unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type
5828 5836
    throws (ResolveError)
5829 5837
{
5830 5838
    let case NominalType::Record(recInfo) = *recordType
5831 5839
        else panic "resolveRecordConstructorCall: not a record type";
5832 5840
5834 5842
    return setNodeType(self, node, Type::Nominal(recordType));
5835 5843
}
5836 5844
5837 5845
/// Resolve the type name of a record literal, handling both record types and
5838 5846
/// union variant payloads like `Union::Variant { ... }`.
5839 -
fn resolveRecordLitType(
5840 -
    self: *mut Resolver, node: *ast::Node, typeIdent: *ast::Node
5847 +
unsafe fn resolveRecordLitType(
5848 +
    self: &mut Resolver, node: *ast::Node, typeIdent: *ast::Node
5841 5849
) -> ResolvedRecordLitType
5842 5850
    throws (ResolveError)
5843 5851
{
5844 5852
    // Check if this is a scope access that might be a union variant.
5845 5853
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
5846 -
        let sym = try resolveAccess(self, typeIdent, access, self.scope);
5854 +
        let scope = self.scope;
5855 +
        let sym = try resolveAccess(self, typeIdent, access, scope);
5847 5856
5848 5857
        // Check if resolved symbol is a union variant.
5849 5858
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
5850 5859
            // Get the union type from the variant's declaration.
5851 5860
            let declSym = symbolFor(self, decl)
5880 5889
        resultType: Type::Nominal(tyInfo),
5881 5890
    };
5882 5891
}
5883 5892
5884 5893
/// Analyze a record literal expression.
5885 -
fn resolveRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
5894 +
unsafe fn resolveRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
5886 5895
    throws (ResolveError)
5887 5896
{
5888 5897
    // If no type name, infer an anonymous tuple type.
5889 5898
    let typeIdent = lit.typeName else {
5890 5899
        return try resolveAnonRecordLit(self, node, lit, hint);
5937 5946
    }
5938 5947
    return setNodeType(self, node, resultType);
5939 5948
}
5940 5949
5941 5950
/// Analyze an anonymous record literal, checking fields against the hint type.
5942 -
fn resolveAnonRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
5951 +
unsafe fn resolveAnonRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
5943 5952
    throws (ResolveError)
5944 5953
{
5945 5954
    // Unwrap optional hint to get the inner record type.
5946 5955
    let mut innerHint = hint;
5947 5956
    if let case Type::Optional(inner) = hint {
5995 6004
    }
5996 6005
    return setNodeType(self, node, innerHint);
5997 6006
}
5998 6007
5999 6008
/// Analyze an array literal expression.
6000 -
fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type
6009 +
unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type
6001 6010
    throws (ResolveError)
6002 6011
{
6003 6012
    let length = items.len;
6004 6013
    let mut expectedTy: Type = Type::Unknown;
6005 6014
6027 6036
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
6028 6037
    return setNodeType(self, node, arrayTy);
6029 6038
}
6030 6039
6031 6040
/// Analyze an array repeat literal expression.
6032 -
fn resolveArrayRepeat(self: *mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
6041 +
unsafe fn resolveArrayRepeat(self: &mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
6033 6042
    throws (ResolveError)
6034 6043
{
6035 6044
    let mut itemHint = hint;
6036 6045
    if let case Type::Array(ary) = hint {
6037 6046
        set itemHint = *ary.item;
6049 6058
    return setNodeType(self, node, arrayTy);
6050 6059
}
6051 6060
6052 6061
/// Resolve union variant access.
6053 6062
fn resolveUnionVariantAccess(
6054 -
    self: *mut Resolver,
6063 +
    self: &mut Resolver,
6055 6064
    node: *ast::Node,
6056 6065
    access: ast::Access,
6057 6066
    unionType: UnionType,
6058 6067
    variantName: *[u8]
6059 6068
) -> *mut Symbol throws (ResolveError) {
6076 6085
    }
6077 6086
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
6078 6087
}
6079 6088
6080 6089
/// Analyze a scope access expression.
6081 -
fn resolveScopeAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6090 +
unsafe fn resolveScopeAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6082 6091
    throws (ResolveError)
6083 6092
{
6084 -
    let sym = try resolveAccess(self, node, access, self.scope);
6093 +
    let scope = self.scope;
6094 +
    let sym = try resolveAccess(self, node, access, scope);
6085 6095
    let mut ty: Type = undefined;
6086 6096
6087 6097
    match sym.data {
6088 6098
        case SymbolData::Value { type, .. } => {
6089 6099
            setNodeSymbol(self, node, sym);
6124 6134
    }
6125 6135
    return setNodeType(self, node, ty);
6126 6136
}
6127 6137
6128 6138
/// Analyze a field access expression.
6129 -
fn resolveFieldAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6139 +
unsafe fn resolveFieldAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6130 6140
    throws (ResolveError)
6131 6141
{
6132 6142
    let parentTy = try infer(self, access.parent);
6133 6143
    if isUnsafePointerType(parentTy) {
6134 6144
        try requireUnsafe(self, access.parent);
6203 6213
        }
6204 6214
    }
6205 6215
}
6206 6216
6207 6217
/// Determine whether an expression can yield a mutable location for borrowing.
6208 -
fn canBorrowMutFrom(self: *mut Resolver, node: *ast::Node) -> bool
6218 +
unsafe fn canBorrowMutFrom(self: &mut Resolver, node: *ast::Node) -> bool
6209 6219
    throws (ResolveError)
6210 6220
{
6211 6221
    match node.value {
6212 6222
        case ast::NodeValue::Ident(name) => {
6213 6223
            let sym = findValueSymbol(self.scope, name)
6295 6305
            return false;
6296 6306
        }
6297 6307
    }
6298 6308
}
6299 6309
6310 +
/// Return the storage class of an addressed location.
6311 +
fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass {
6312 +
    match node.value {
6313 +
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
6314 +
            if let sym = symbolFor(self, node) {
6315 +
                match sym.node.value {
6316 +
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
6317 +
                        return types::PointerClass::Owned,
6318 +
                    else => {}
6319 +
                }
6320 +
            }
6321 +
        }
6322 +
        case ast::NodeValue::FieldAccess(access) => {
6323 +
            if let ty = typeFor(self, access.parent) {
6324 +
                if let case Type::Pointer { class, .. } = ty {
6325 +
                    return class;
6326 +
                }
6327 +
            }
6328 +
            return addressStorageClass(self, access.parent);
6329 +
        }
6330 +
        case ast::NodeValue::Subscript { container, .. } => {
6331 +
            if let ty = typeFor(self, container) {
6332 +
                if let case Type::Slice { class, .. } = autoDeref(ty) {
6333 +
                    return class;
6334 +
                }
6335 +
                if let case Type::Pointer { class, .. } = ty {
6336 +
                    return class;
6337 +
                }
6338 +
            }
6339 +
            return addressStorageClass(self, container);
6340 +
        }
6341 +
        case ast::NodeValue::Deref(target) => {
6342 +
            if let ty = typeFor(self, target) {
6343 +
                if let case Type::Pointer { class, .. } = ty {
6344 +
                    return class;
6345 +
                }
6346 +
            }
6347 +
            return addressStorageClass(self, target);
6348 +
        }
6349 +
        else => {}
6350 +
    }
6351 +
    return types::PointerClass::Ref;
6352 +
}
6353 +
6354 +
/// Select an address type without extending the target storage lifetime.
6355 +
fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass
6356 +
    throws (ResolveError)
6357 +
{
6358 +
    if isUnsafePointerType(hint) {
6359 +
        try requireUnsafe(self, target);
6360 +
        return types::PointerClass::Unsafe;
6361 +
    }
6362 +
    if isRefType(hint) {
6363 +
        return types::PointerClass::Ref;
6364 +
    }
6365 +
    match target.value {
6366 +
        case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => {
6367 +
            if isConstExpr(self, target) {
6368 +
                return types::PointerClass::Owned;
6369 +
            }
6370 +
        }
6371 +
        else => {}
6372 +
    }
6373 +
    return addressStorageClass(self, target);
6374 +
}
6375 +
6300 6376
/// Analyze an address-of expression.
6301 -
fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
6377 +
unsafe fn resolveAddressOf(self: &mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
6302 6378
    throws (ResolveError)
6303 6379
{
6304 -
    // Checked and unsafe pointer contexts require a reference source.
6305 -
    let class = types::PointerClass::Ref
6306 -
        if isRefType(hint) or isUnsafePointerType(hint)
6307 -
        else types::PointerClass::Owned;
6308 6380
    if addr.mutable {
6309 6381
        if not try canBorrowMutFrom(self, addr.target) {
6310 6382
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6311 6383
        }
6312 6384
    }
6335 6407
                    else => {
6336 6408
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
6337 6409
                    }
6338 6410
                }
6339 6411
            }
6412 +
            let class = try addressClass(self, addr.target, hint);
6340 6413
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
6341 6414
            let alloc = allocType(self, sliceTy);
6342 6415
            setSliceRangeInfo(self, node, SliceRangeInfo {
6343 6416
                itemType: item,
6344 6417
                mutable: addr.mutable,
6352 6425
    let mut targetHint: Type = Type::Unknown;
6353 6426
    if let case Type::Slice { item, .. } = hint {
6354 6427
        set targetHint = Type::Array(ArrayType { item, length: 0 });
6355 6428
    }
6356 6429
    let targetTy = try visit(self, addr.target, targetHint);
6430 +
    let class = try addressClass(self, addr.target, hint);
6357 6431
6358 6432
    // Mark local variable symbols as address-taken so the lowerer
6359 6433
    // allocates a stack slot eagerly.
6360 6434
    if let case ast::NodeValue::Ident(name) = addr.target.value {
6361 6435
        if let sym = findValueSymbol(self.scope, name) {
6384 6458
    };
6385 6459
    return setNodeType(self, node, pointerTy);
6386 6460
}
6387 6461
6388 6462
/// Analyze a dereference expression.
6389 -
fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
6463 +
unsafe fn resolveDeref(self: &mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
6390 6464
    throws (ResolveError)
6391 6465
{
6392 6466
    let operandTy = try visit(self, targetNode, hint);
6393 6467
    if let case Type::Pointer { class, target, .. } = operandTy {
6394 6468
        if class == types::PointerClass::Unsafe {
6492 6566
    }
6493 6567
    return false;
6494 6568
}
6495 6569
6496 6570
/// Analyze an `as` cast expression.
6497 -
fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6571 +
unsafe fn resolveAs(self: &mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6498 6572
    throws (ResolveError)
6499 6573
{
6500 6574
    let targetTy = try infer(self, expr.type);
6501 6575
    let sourceTy = try visit(self, expr.value, targetTy);
6502 6576
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6552 6626
        to: targetTy,
6553 6627
    }));
6554 6628
}
6555 6629
6556 6630
/// Analyze a range expression.
6557 -
fn resolveRange(self: *mut Resolver, node: *ast::Node, range: ast::Range) -> Type
6631 +
unsafe fn resolveRange(self: &mut Resolver, node: *ast::Node, range: ast::Range) -> Type
6558 6632
    throws (ResolveError)
6559 6633
{
6560 6634
    let mut start: ?*Type = nil;
6561 6635
    let mut end: ?*Type = nil;
6562 6636
6589 6663
}
6590 6664
6591 6665
/// Analyze a `try` expression and its handlers.
6592 6666
/// The `expected` type is used to determine if the value is discarded (`Void`)
6593 6667
/// or if the catch expression needs type checking.
6594 -
fn resolveTry(self: *mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
6668 +
unsafe fn resolveTry(self: &mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
6595 6669
    throws (ResolveError)
6596 6670
{
6597 6671
    let call = tryExpr.expr;
6598 6672
    let case ast::NodeValue::Call(callExpr) = call.value
6599 6673
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
6647 6721
    return setNodeType(self, node, tryResultTy);
6648 6722
}
6649 6723
6650 6724
/// Check that a `catch` body is assignable to the expected result type, but only
6651 6725
/// in expression context (`hint` is neither `Unknown` nor `Void`).
6652 -
fn checkCatchBody(self: *mut Resolver, body: *ast::Node, resultTy: Type, hint: Type)
6726 +
unsafe fn checkCatchBody(self: &mut Resolver, body: *ast::Node, resultTy: Type, hint: Type)
6653 6727
    throws (ResolveError)
6654 6728
{
6655 6729
    if hint <> Type::Unknown and hint <> Type::Void {
6656 6730
        try checkAssignable(self, body, resultTy);
6657 6731
    }
6660 6734
/// Resolve catch clauses for a `try ... catch` expression.
6661 6735
///
6662 6736
/// For a single untyped catch (with or without binding), resolves the catch
6663 6737
/// body and returns the result type. Multi-error callees with inferred bindings
6664 6738
/// are rejected; you must use typed catches.
6665 -
fn resolveTryCatches(
6666 -
    self: *mut Resolver,
6739 +
unsafe fn resolveTryCatches(
6740 +
    self: &mut Resolver,
6667 6741
    node: *ast::Node,
6668 6742
    catches: *mut [*ast::Node],
6669 6743
    calleeInfo: *FnType,
6670 6744
    resultTy: Type,
6671 6745
    hint: Type
6700 6774
6701 6775
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
6702 6776
///
6703 6777
/// Validates that each type annotation is in the callee's throw list, that
6704 6778
/// there are no duplicate catch types, and that the clauses are exhaustive.
6705 -
fn resolveTypedCatches(
6706 -
    self: *mut Resolver,
6779 +
unsafe fn resolveTypedCatches(
6780 +
    self: &mut Resolver,
6707 6781
    node: *ast::Node,
6708 6782
    catches: *mut [*ast::Node],
6709 6783
    calleeInfo: *FnType,
6710 6784
    resultTy: Type,
6711 6785
    hint: Type
6765 6839
    }
6766 6840
    return resultTy;
6767 6841
}
6768 6842
6769 6843
/// Analyze a `throw` statement.
6770 -
fn resolveThrow(self: *mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type
6844 +
unsafe fn resolveThrow(self: &mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type
6771 6845
    throws (ResolveError)
6772 6846
{
6773 6847
    let fnInfo = self.currentFn
6774 6848
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
6775 6849
    if fnInfo.throwList.len == 0 {
6784 6858
    }
6785 6859
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
6786 6860
}
6787 6861
6788 6862
/// Analyze a `return` statement.
6789 -
fn resolveReturn(self: *mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type
6863 +
unsafe fn resolveReturn(self: &mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type
6790 6864
    throws (ResolveError)
6791 6865
{
6792 6866
    let f = self.currentFn
6793 6867
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
6794 6868
    let expected = *f.returnType;
6930 7004
    }
6931 7005
}
6932 7006
6933 7007
/// Try to constant-fold a binary operation on two resolved operands.
6934 7008
/// Only folds when the result type is concrete.
6935 -
fn tryFoldBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
7009 +
fn tryFoldBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
6936 7010
    let leftVal = constValueEntry(self, binop.left)
6937 7011
        else return;
6938 7012
    let rightVal = constValueEntry(self, binop.right)
6939 7013
        else return;
6940 7014
6962 7036
        }
6963 7037
    }
6964 7038
}
6965 7039
6966 7040
/// Analyze a binary expression.
6967 -
fn resolveBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type
7041 +
unsafe fn resolveBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type
6968 7042
    throws (ResolveError)
6969 7043
{
6970 7044
    let mut resultTy = Type::Unknown;
6971 7045
6972 7046
    match binop.op {
7077 7151
7078 7152
    return setNodeType(self, node, resultTy);
7079 7153
}
7080 7154
7081 7155
/// Analyze a unary expression.
7082 -
fn resolveUnOp(self: *mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type
7156 +
unsafe fn resolveUnOp(self: &mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type
7083 7157
    throws (ResolveError)
7084 7158
{
7085 7159
    let mut resultTy = Type::Unknown;
7086 7160
7087 7161
    match unop.op {
7122 7196
    };
7123 7197
    return setNodeType(self, node, resultTy);
7124 7198
}
7125 7199
7126 7200
/// Resolve a type signature node and set its type.
7127 -
fn inferTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7201 +
unsafe fn inferTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7128 7202
    throws (ResolveError)
7129 7203
{
7130 7204
    let resolved = try resolveTypeSig(self, node, sig);
7131 7205
7132 7206
    return setNodeType(self, node, resolved);
7133 7207
}
7134 7208
7135 7209
/// Convert a type signature node into a type value.
7136 -
fn resolveTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7210 +
unsafe fn resolveTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7137 7211
    throws (ResolveError)
7138 7212
{
7139 7213
    match sig {
7140 7214
        case ast::TypeSig::Void => {
7141 7215
            return Type::Void;
7197 7271
                }
7198 7272
            }
7199 7273
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
7200 7274
            return Type::Nominal(nominalTy);
7201 7275
        }
7202 -
        case ast::TypeSig::Fn(t) => {
7276 +
        case ast::TypeSig::Fn { sig: t, isUnsafe } => {
7203 7277
            let a = alloc::arenaAllocator(&mut self.arena);
7204 7278
            let mut paramTypes: *mut [*Type] = &mut [];
7205 7279
            let mut throwList: *mut [*Type] = &mut [];
7206 7280
7207 7281
            if t.params.len > MAX_FN_PARAMS {
7234 7308
            }
7235 7309
            let fnType = FnType {
7236 7310
                paramTypes: &paramTypes[..],
7237 7311
                returnType: retType,
7238 7312
                throwList: &throwList[..],
7239 -
                isUnsafe: false,
7313 +
                isUnsafe,
7240 7314
                localCount: 0,
7241 7315
            };
7242 7316
            return Type::Fn(allocFnType(self, fnType));
7243 7317
        }
7244 7318
        // Resolve an opaque trait object signature.
7265 7339
        else => return true,
7266 7340
    }
7267 7341
}
7268 7342
7269 7343
/// Analyze a standalone expression by wrapping it in a synthetic function.
7270 -
export fn resolveExpr(
7271 -
    self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena
7344 +
export unsafe fn resolveExpr(
7345 +
    self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena
7272 7346
) -> Diagnostics throws (ResolveError) {
7273 7347
    let a = alloc::arenaAllocator(&mut arena.arena);
7274 7348
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
7275 7349
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
7276 7350
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
7288 7362
7289 7363
    return Diagnostics { errors: self.errors };
7290 7364
}
7291 7365
7292 7366
/// Analyze a parsed module root, ie. a block of top-level statements.
7293 -
export fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7367 +
export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7294 7368
    let case ast::NodeValue::Block(block) = root.value
7295 7369
        else panic "resolveModuleRoot: expected block for module root";
7296 7370
7297 7371
    enterScope(self, root);
7298 7372
    try resolveModuleDecls(self, &block) catch {
7308 7382
}
7309 7383
7310 7384
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
7311 7385
/// and scopes for them, and also binds type names in each module so that cross-module
7312 7386
/// type references work regardless of declaration order.
7313 -
fn resolveModuleGraph(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7387 +
unsafe fn resolveModuleGraph(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7314 7388
    try bindTypeNames(self, block);
7315 7389
7316 7390
    for node in block.statements {
7317 7391
        if let case ast::NodeValue::Mod(decl) = node.value {
7318 7392
            try resolveModGraph(self, node, decl);
7320 7394
    }
7321 7395
}
7322 7396
7323 7397
/// Bind all type names in a module.
7324 7398
/// Skips declarations that have already been bound.
7325 -
fn bindTypeNames(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7399 +
fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7326 7400
    for node in block.statements {
7327 7401
        match node.value {
7328 7402
            case ast::NodeValue::RecordDecl(decl) => {
7329 7403
                if symbolFor(self, node) == nil {
7330 7404
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7344 7418
        }
7345 7419
    }
7346 7420
}
7347 7421
7348 7422
/// Resolve all type bodies in a module.
7349 -
fn resolveTypeBodies(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7423 +
unsafe fn resolveTypeBodies(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7350 7424
    for node in block.statements {
7351 7425
        match node.value {
7352 7426
            case ast::NodeValue::RecordDecl(decl) => {
7353 7427
                try resolveRecordBody(self, node, decl) catch {
7354 7428
                    // Continue resolving other types even if one fails.
7377 7451
/// previous pass.
7378 7452
///
7379 7453
/// This function uses a two-phase approach:
7380 7454
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
7381 7455
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
7382 -
fn resolveModuleDecls(res: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7456 +
unsafe fn resolveModuleDecls(res: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7383 7457
    // Phase 1: Bind all type names as placeholders.
7384 7458
    try bindTypeNames(res, block);
7385 7459
    // Phase 2: Process imports so names available from the module graph can
7386 7460
    // be used in function signatures.
7387 7461
    for node in block.statements {
7428 7502
        try visitDecl(res, stmt);
7429 7503
    }
7430 7504
}
7431 7505
7432 7506
/// Find a tracked binding by symbol identity.
7433 -
fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 {
7507 +
fn findLinearBinding(env: &LinearEnv, sym: *mut Symbol) -> ?u32 {
7434 7508
    for i in 0..env.len {
7435 7509
        if env.symbols[i] == sym {
7436 7510
            return i;
7437 7511
        }
7438 7512
    }
7439 7513
    return nil;
7440 7514
}
7441 7515
7442 7516
/// Return whether a tracked binding is still available.
7443 -
fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool {
7517 +
fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool {
7444 7518
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7445 7519
}
7446 7520
7447 7521
/// Add a local binding when its resolved type moves by value.
7448 -
fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node)
7522 +
unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node)
7449 7523
    throws (ResolveError)
7450 7524
{
7451 -
    let sym = symbolFor(checker.resolver, node) else return;
7525 +
    let sym = symbolFor(&mut *checker.resolver, node) else return;
7452 7526
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7453 7527
    if not isMoveOnly(ty) {
7454 7528
        return;
7455 7529
    }
7456 7530
    if env.len >= MAX_LINEAR_BINDINGS {
7457 -
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7531 +
        throw emitError(&mut *checker.resolver, node, ErrorKind::Internal);
7458 7532
    }
7459 7533
    set env.symbols[env.len] = sym;
7460 7534
    set env.available |= (1 as u64) << (env.len as u64);
7461 7535
    set env.len += 1;
7462 7536
}
7463 7537
7464 7538
/// Mark a tracked binding as uninitialized.
7465 -
fn markLinearBindingUnavailable(self: *mut Resolver, env: *mut LinearEnv, node: *ast::Node) {
7539 +
fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) {
7466 7540
    let sym = symbolFor(self, node) else return;
7467 7541
    let index = findLinearBinding(env, sym) else return;
7468 7542
    set env.available &= ~((1 as u64) << (index as u64));
7469 7543
}
7470 7544
7471 7545
/// Require exact-use bindings introduced after `start` to be consumed.
7472 -
fn finishLinearScope(
7473 -
    checker: *mut LinearChecker,
7474 -
    env: *mut LinearEnv,
7546 +
unsafe fn finishLinearScope(
7547 +
    checker: &mut LinearChecker,
7548 +
    env: &mut LinearEnv,
7475 7549
    start: u32,
7476 7550
) throws (ResolveError) {
7477 7551
    if not env.terminated {
7478 7552
        for i in start..env.len {
7479 7553
            if linearBindingAvailable(env, i) {
7480 7554
                let sym = env.symbols[i];
7481 7555
                let case SymbolData::Value { type: ty, .. } = sym.data
7482 7556
                    else panic "finishLinearScope: expected value symbol";
7483 7557
                if isLinear(ty) {
7484 7558
                    throw emitError(
7485 -
                        checker.resolver,
7559 +
                        &mut *checker.resolver,
7486 7560
                        sym.node,
7487 7561
                        ErrorKind::LinearNotConsumed(sym.name),
7488 7562
                    );
7489 7563
                }
7490 7564
            }
7492 7566
    }
7493 7567
    set env.len = start;
7494 7568
}
7495 7569
7496 7570
/// Move or consume a tracked identifier once.
7497 -
fn consumeLinearIdent(
7498 -
    checker: *mut LinearChecker,
7499 -
    env: *mut LinearEnv,
7571 +
unsafe fn consumeLinearIdent(
7572 +
    checker: &mut LinearChecker,
7573 +
    env: &mut LinearEnv,
7500 7574
    node: *ast::Node,
7501 7575
) throws (ResolveError) {
7502 -
    let sym = symbolFor(checker.resolver, node) else return;
7576 +
    let sym = symbolFor(&mut *checker.resolver, node) else return;
7503 7577
    let index = findLinearBinding(env, sym) else return;
7504 7578
    if not linearBindingAvailable(env, index) {
7505 7579
        let case SymbolData::Value { type: ty, .. } = sym.data
7506 7580
            else panic "consumeLinearIdent: expected value symbol";
7507 7581
        let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty)
7508 7582
            else ErrorKind::AffineUseAfterMove(sym.name);
7509 -
        throw emitError(checker.resolver, node, kind);
7583 +
        throw emitError(&mut *checker.resolver, node, kind);
7510 7584
    }
7511 7585
    set env.available &= ~((1 as u64) << (index as u64));
7512 7586
}
7513 7587
7514 7588
/// Merge ownership availability across two live branches.
7515 7589
/// Validate both inputs before writing to an output that can alias either input.
7516 -
fn joinLinearBranches(
7517 -
    checker: *mut LinearChecker,
7518 -
    env: *mut LinearEnv,
7519 -
    left: *LinearEnv,
7520 -
    right: *LinearEnv,
7590 +
unsafe fn joinLinearBranches(
7591 +
    checker: &mut LinearChecker,
7592 +
    env: &mut LinearEnv,
7593 +
    left: &LinearEnv,
7594 +
    right: &LinearEnv,
7521 7595
    node: *ast::Node,
7522 7596
) throws (ResolveError) {
7523 7597
    if left.terminated and right.terminated {
7524 7598
        set *env = *left;
7525 7599
        set env.terminated = true;
7540 7614
            let sym = left.symbols[i];
7541 7615
            let case SymbolData::Value { type: ty, .. } = sym.data
7542 7616
                else panic "joinLinearBranches: expected value symbol";
7543 7617
            if isLinear(ty) {
7544 7618
                throw emitError(
7545 -
                    checker.resolver,
7619 +
                    &mut *checker.resolver,
7546 7620
                    node,
7547 7621
                    ErrorKind::LinearBranchMismatch(sym.name),
7548 7622
                );
7549 7623
            }
7550 7624
            set available &= ~((1 as u64) << (i as u64));
7553 7627
    set *env = *left;
7554 7628
    set env.available = available;
7555 7629
}
7556 7630
7557 7631
/// Require all available exact-use bindings to be consumed at a function exit.
7558 -
fn finishLinearExit(
7559 -
    checker: *mut LinearChecker,
7560 -
    env: *mut LinearEnv,
7632 +
unsafe fn finishLinearExit(
7633 +
    checker: &mut LinearChecker,
7634 +
    env: &mut LinearEnv,
7561 7635
) throws (ResolveError) {
7562 7636
    for i in 0..env.len {
7563 7637
        if linearBindingAvailable(env, i) {
7564 7638
            let sym = env.symbols[i];
7565 7639
            let case SymbolData::Value { type: ty, .. } = sym.data
7566 7640
                else panic "finishLinearExit: expected value symbol";
7567 7641
            if isLinear(ty) {
7568 7642
                throw emitError(
7569 -
                    checker.resolver,
7643 +
                    &mut *checker.resolver,
7570 7644
                    sym.node,
7571 7645
                    ErrorKind::LinearNotConsumed(sym.name),
7572 7646
                );
7573 7647
            }
7574 7648
        }
7575 7649
    }
7576 7650
    set env.terminated = true;
7577 7651
}
7578 7652
7579 7653
/// Find the local root borrowed or consumed by an argument expression.
7580 -
fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7654 +
fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7581 7655
    match node.value {
7582 7656
        case ast::NodeValue::Ident(_) => return symbolFor(self, node),
7583 7657
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7584 7658
        case ast::NodeValue::FieldAccess(access) =>
7585 7659
            return linearRootSymbol(self, access.parent),
7589 7663
        else => return nil,
7590 7664
    }
7591 7665
}
7592 7666
7593 7667
/// Add the value identifiers introduced by a pattern.
7594 -
fn addLinearPatternBindings(
7595 -
    checker: *mut LinearChecker,
7596 -
    env: *mut LinearEnv,
7668 +
unsafe fn addLinearPatternBindings(
7669 +
    checker: &mut LinearChecker,
7670 +
    env: &mut LinearEnv,
7597 7671
    pattern: *ast::Node,
7598 7672
) throws (ResolveError) {
7599 7673
    match pattern.value {
7600 7674
        case ast::NodeValue::Ident(_) => try addLinearBinding(checker, env, pattern),
7601 7675
        case ast::NodeValue::Call(call) => {
7618 7692
        else => {}
7619 7693
    }
7620 7694
}
7621 7695
7622 7696
/// Check a lexical block and exact-use of locals introduced in it.
7623 -
fn checkLinearBlock(
7624 -
    checker: *mut LinearChecker,
7625 -
    env: *mut LinearEnv,
7697 +
unsafe fn checkLinearBlock(
7698 +
    checker: &mut LinearChecker,
7699 +
    env: &mut LinearEnv,
7626 7700
    node: *ast::Node,
7627 7701
) throws (ResolveError) {
7628 7702
    let start = env.len;
7629 7703
    let case ast::NodeValue::Block(block) = node.value
7630 7704
        else panic "checkLinearBlock: expected block";
7637 7711
    try finishLinearScope(checker, env, start);
7638 7712
}
7639 7713
7640 7714
/// Push a repeated-control-flow boundary.
7641 7715
/// Initialize all loop state at this depth before increasing `loopDepth`.
7642 -
fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) {
7716 +
fn enterLinearLoop(checker: &mut LinearChecker, env: &LinearEnv) {
7643 7717
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
7644 7718
    let depth = checker.loopDepth;
7645 7719
    set checker.loopMarks[depth] = env.len;
7646 7720
    set checker.loopAvailable[depth] = env.available;
7647 7721
    set checker.loopExitAvailable[depth] = env.available;
7649 7723
    set checker.loopBreakSeen[depth] = false;
7650 7724
    set checker.loopDepth += 1;
7651 7725
}
7652 7726
7653 7727
/// Require a repeated body's outer bindings to match its entry state.
7654 -
fn checkLinearLoopBackEdge(
7655 -
    checker: *mut LinearChecker,
7656 -
    env: *LinearEnv,
7728 +
unsafe fn checkLinearLoopBackEdge(
7729 +
    checker: &mut LinearChecker,
7730 +
    env: &LinearEnv,
7657 7731
    node: *ast::Node,
7658 7732
) throws (ResolveError) {
7659 7733
    if env.terminated {
7660 7734
        return;
7661 7735
    }
7666 7740
    for i in 0..mark {
7667 7741
        let bit = (1 as u64) << (i as u64);
7668 7742
        if (env.available & bit) <> (entryAvailable & bit) {
7669 7743
            let sym = env.symbols[i];
7670 7744
            throw emitError(
7671 -
                checker.resolver,
7745 +
                &mut *checker.resolver,
7672 7746
                node,
7673 7747
                ErrorKind::LinearBranchMismatch(sym.name),
7674 7748
            );
7675 7749
        }
7676 7750
    }
7677 7751
}
7678 7752
7679 7753
/// Record the ownership state of a loop's condition-false exit.
7680 -
fn setLinearLoopNaturalExit(checker: *mut LinearChecker, env: *LinearEnv) {
7754 +
fn setLinearLoopNaturalExit(checker: &mut LinearChecker, env: &LinearEnv) {
7681 7755
    assert checker.loopDepth > 0, "linear loop exit outside loop";
7682 7756
    let depth = checker.loopDepth - 1;
7683 7757
    set checker.loopExitAvailable[depth] = env.available;
7684 7758
    set checker.loopHasNaturalExit[depth] = true;
7685 7759
}
7686 7760
7687 7761
/// Require a break exit to agree with every other exit from this loop.
7688 -
fn checkLinearLoopBreak(
7689 -
    checker: *mut LinearChecker,
7690 -
    env: *LinearEnv,
7762 +
unsafe fn checkLinearLoopBreak(
7763 +
    checker: &mut LinearChecker,
7764 +
    env: &LinearEnv,
7691 7765
    node: *ast::Node,
7692 7766
) throws (ResolveError) {
7693 7767
    assert checker.loopDepth > 0, "linear loop break outside loop";
7694 7768
    let depth = checker.loopDepth - 1;
7695 7769
    let mark = checker.loopMarks[depth];
7698 7772
        for i in 0..mark {
7699 7773
            let bit = (1 as u64) << (i as u64);
7700 7774
            if (env.available & bit) <> (expected & bit) {
7701 7775
                let sym = env.symbols[i];
7702 7776
                throw emitError(
7703 -
                    checker.resolver,
7777 +
                    &mut *checker.resolver,
7704 7778
                    node,
7705 7779
                    ErrorKind::LinearBranchMismatch(sym.name),
7706 7780
                );
7707 7781
            }
7708 7782
        }
7711 7785
    }
7712 7786
    set checker.loopBreakSeen[depth] = true;
7713 7787
}
7714 7788
7715 7789
/// Pop a repeated-control-flow boundary.
7716 -
fn exitLinearLoop(checker: *mut LinearChecker) {
7790 +
fn exitLinearLoop(checker: &mut LinearChecker) {
7717 7791
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
7718 7792
    set checker.loopDepth -= 1;
7719 7793
}
7720 7794
7721 7795
/// Check a conditional and merge its ownership states.
7722 -
fn checkLinearIf(
7723 -
    checker: *mut LinearChecker,
7724 -
    env: *mut LinearEnv,
7796 +
unsafe fn checkLinearIf(
7797 +
    checker: &mut LinearChecker,
7798 +
    env: &mut LinearEnv,
7725 7799
    node: *ast::Node,
7726 7800
    conditional: ast::If,
7727 7801
) throws (ResolveError) {
7728 7802
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7729 7803
    let base = *env;
7735 7809
    }
7736 7810
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7737 7811
}
7738 7812
7739 7813
/// Check an expression conditional and merge its ownership states.
7740 -
fn checkLinearCondExpr(
7741 -
    checker: *mut LinearChecker,
7742 -
    env: *mut LinearEnv,
7814 +
unsafe fn checkLinearCondExpr(
7815 +
    checker: &mut LinearChecker,
7816 +
    env: &mut LinearEnv,
7743 7817
    node: *ast::Node,
7744 7818
    conditional: ast::CondExpr,
7745 7819
    usage: LinearUse,
7746 7820
) throws (ResolveError) {
7747 7821
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7752 7826
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7753 7827
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7754 7828
}
7755 7829
7756 7830
/// Check a match expression, including ownership transferred into patterns.
7757 -
fn checkLinearMatch(
7758 -
    checker: *mut LinearChecker,
7759 -
    env: *mut LinearEnv,
7831 +
unsafe fn checkLinearMatch(
7832 +
    checker: &mut LinearChecker,
7833 +
    env: &mut LinearEnv,
7760 7834
    node: *ast::Node,
7761 7835
    matchExpr: ast::Match,
7762 7836
) throws (ResolveError) {
7763 7837
    try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume);
7764 7838
    let base = *env;
7784 7858
            for i in bindingsStart..branch.len {
7785 7859
                let sym = branch.symbols[i];
7786 7860
                let case SymbolData::Value { type: ty, .. } = sym.data
7787 7861
                    else panic "checkLinearMatch: expected value symbol";
7788 7862
                if isLinear(ty) {
7789 -
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7863 +
                    throw emitError(&mut *checker.resolver, prongNode, ErrorKind::LinearDiscard);
7790 7864
                }
7791 7865
            }
7792 7866
        }
7793 7867
        if let guard = prong.guard {
7794 7868
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
7795 7869
        }
7796 7870
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
7797 7871
        try finishLinearScope(checker, &mut branch, bindingsStart);
7798 7872
        if haveResult {
7799 -
            try joinLinearBranches(checker, &mut result, &result, &branch, node);
7873 +
            let previous = result;
7874 +
        try joinLinearBranches(checker, &mut result, &previous, &branch, node);
7800 7875
        } else {
7801 7876
            set result = branch;
7802 7877
            set haveResult = true;
7803 7878
        }
7804 7879
    }
7806 7881
        set *env = result;
7807 7882
    }
7808 7883
}
7809 7884
7810 7885
/// Check call-scoped loans and argument ownership transfers.
7811 -
fn checkLinearCall(
7812 -
    checker: *mut LinearChecker,
7813 -
    env: *mut LinearEnv,
7886 +
unsafe fn checkLinearCall(
7887 +
    checker: &mut LinearChecker,
7888 +
    env: &mut LinearEnv,
7814 7889
    node: *ast::Node,
7815 7890
    call: ast::Call,
7816 7891
) throws (ResolveError) {
7817 7892
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
7818 7893
    let mut fnInfo: ?*FnType = nil;
7819 7894
    match checker.resolver.nodeData.entries[node.id].extra {
7820 7895
        case NodeExtra::TraitMethodCall { traitInfo, methodIndex } =>
7821 7896
            set fnInfo = traitInfo.methods[methodIndex].fnType,
7822 7897
        case NodeExtra::MethodCall { method } => set fnInfo = method.fnType,
7823 7898
        else => {
7824 -
            if let calleeTy = typeFor(checker.resolver, call.callee) {
7899 +
            if let calleeTy = typeFor(&mut *checker.resolver, call.callee) {
7825 7900
                if let case Type::Fn(info) = calleeTy {
7826 7901
                    set fnInfo = info;
7827 7902
                }
7828 7903
            }
7829 7904
        }
7859 7934
            }
7860 7935
            else => {}
7861 7936
        }
7862 7937
        if haveReceiver {
7863 7938
            if receiverClass <> types::PointerClass::Unsafe {
7864 -
                let root = linearRootSymbol(checker.resolver, access.parent);
7939 +
                let root = linearRootSymbol(&mut *checker.resolver, access.parent);
7865 7940
                if let rootSym = root {
7866 7941
                    set roots[rootsLen] = rootSym;
7867 7942
                    set exclusive[rootsLen] =
7868 7943
                        receiverClass == types::PointerClass::Owned or receiverMutable;
7869 7944
                    set rootsLen += 1;
7877 7952
        }
7878 7953
    }
7879 7954
7880 7955
    for arg, i in call.args {
7881 7956
        let expected = *info.paramTypes[i];
7882 -
        let root = linearRootSymbol(checker.resolver, arg);
7957 +
        let root = linearRootSymbol(&mut *checker.resolver, arg);
7883 7958
        let mut argExclusive = isMoveOnly(expected);
7884 7959
        if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected {
7885 7960
            set argExclusive = mutable;
7886 7961
        } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected {
7887 7962
            set argExclusive = mutable;
7894 7969
            if let rootSym = root {
7895 7970
                for j in 0..rootsLen {
7896 7971
                    if let previous = roots[j] {
7897 7972
                        if previous == rootSym and (exclusive[j] or argExclusive) {
7898 7973
                            throw emitError(
7899 -
                                checker.resolver,
7974 +
                                &mut *checker.resolver,
7900 7975
                                arg,
7901 7976
                                ErrorKind::BorrowConflict(rootSym.name),
7902 7977
                            );
7903 7978
                        }
7904 7979
                    }
7915 7990
        }
7916 7991
    }
7917 7992
}
7918 7993
7919 7994
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
7920 -
fn checkLinearIfLet(
7921 -
    checker: *mut LinearChecker,
7922 -
    env: *mut LinearEnv,
7995 +
unsafe fn checkLinearIfLet(
7996 +
    checker: &mut LinearChecker,
7997 +
    env: &mut LinearEnv,
7923 7998
    node: *ast::Node,
7924 7999
    conditional: ast::IfLet,
7925 8000
) throws (ResolveError) {
7926 -
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
8001 +
    if let subjectTy = typeFor(&mut *checker.resolver, conditional.pattern.scrutinee);
7927 8002
        isLinear(subjectTy)
7928 8003
    {
7929 8004
        throw emitError(
7930 -
            checker.resolver,
8005 +
            &mut *checker.resolver,
7931 8006
            conditional.pattern.scrutinee,
7932 8007
            ErrorKind::LinearPartialMove,
7933 8008
        );
7934 8009
    }
7935 8010
    try checkLinearNode(
7953 8028
    }
7954 8029
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7955 8030
}
7956 8031
7957 8032
/// Check one expression or statement under an ownership-use context.
7958 -
fn checkLinearNode(
7959 -
    checker: *mut LinearChecker,
7960 -
    env: *mut LinearEnv,
8033 +
unsafe fn checkLinearNode(
8034 +
    checker: &mut LinearChecker,
8035 +
    env: &mut LinearEnv,
7961 8036
    node: *ast::Node,
7962 8037
    usage: LinearUse,
7963 8038
) throws (ResolveError) {
7964 8039
    if env.terminated {
7965 8040
        return;
7969 8044
            if usage == LinearUse::Consume {
7970 8045
                try consumeLinearIdent(checker, env, node);
7971 8046
            }
7972 8047
        }
7973 8048
        case ast::NodeValue::ExprStmt(expr) => {
7974 -
            if let exprTy = typeFor(checker.resolver, expr) {
8049 +
            if let exprTy = typeFor(&mut *checker.resolver, expr) {
7975 8050
                if isLinear(exprTy) {
7976 -
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
8051 +
                    throw emitError(&mut *checker.resolver, expr, ErrorKind::LinearDiscard);
7977 8052
                }
7978 8053
            }
7979 8054
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
7980 8055
        }
7981 8056
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
7983 8058
            let mut isUndefined = false;
7984 8059
            if let case ast::NodeValue::Undef = binding.value.value {
7985 8060
                set isUndefined = true;
7986 8061
            }
7987 8062
            if isUndefined {
7988 -
                if let bindingTy = typeFor(checker.resolver, binding.ident);
8063 +
                if let bindingTy = typeFor(&mut *checker.resolver, binding.ident);
7989 8064
                    isLinear(bindingTy)
7990 8065
                {
7991 8066
                    throw emitError(
7992 -
                        checker.resolver,
8067 +
                        &mut *checker.resolver,
7993 8068
                        binding.value,
7994 8069
                        ErrorKind::LinearUndefined,
7995 8070
                    );
7996 8071
                }
7997 8072
            }
7998 8073
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
7999 8074
            try addLinearBinding(checker, env, node);
8000 8075
            if isUndefined {
8001 -
                markLinearBindingUnavailable(checker.resolver, env, node);
8076 +
                markLinearBindingUnavailable(&mut *checker.resolver, env, node);
8002 8077
            }
8003 8078
        }
8004 8079
        case ast::NodeValue::Assign(assign) => {
8005 8080
            let mut target: ?u32 = nil;
8006 8081
            let mut targetLinear = false;
8007 -
            if let leftTy = typeFor(checker.resolver, assign.left) {
8082 +
            if let leftTy = typeFor(&mut *checker.resolver, assign.left) {
8008 8083
                if isMoveOnly(leftTy) {
8009 8084
                    set targetLinear = isLinear(leftTy);
8010 8085
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
8011 -
                        if let sym = symbolFor(checker.resolver, assign.left) {
8086 +
                        if let sym = symbolFor(&mut *checker.resolver, assign.left) {
8012 8087
                            set target = findLinearBinding(env, sym);
8013 8088
                        }
8014 8089
                    }
8015 8090
                    if target == nil {
8016 8091
                        throw emitError(
8017 -
                            checker.resolver,
8092 +
                            &mut *checker.resolver,
8018 8093
                            assign.left,
8019 8094
                            ErrorKind::LinearOverwrite,
8020 8095
                        );
8021 8096
                    }
8022 8097
                }
8024 8099
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
8025 8100
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
8026 8101
            if let index = target {
8027 8102
                if targetLinear and linearBindingAvailable(env, index) {
8028 8103
                    throw emitError(
8029 -
                        checker.resolver,
8104 +
                        &mut *checker.resolver,
8030 8105
                        assign.left,
8031 8106
                        ErrorKind::LinearOverwrite,
8032 8107
                    );
8033 8108
                }
8034 8109
                set env.available |= (1 as u64) << (index as u64);
8037 8112
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
8038 8113
        case ast::NodeValue::AddressOf(addr) => {
8039 8114
            try checkLinearNode(checker, env, addr.target, LinearUse::Borrow);
8040 8115
        }
8041 8116
        case ast::NodeValue::Deref(target) => {
8042 -
            if let resultTy = typeFor(checker.resolver, node) {
8117 +
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8043 8118
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8044 -
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8119 +
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8045 8120
                }
8046 8121
            }
8047 8122
            try checkLinearNode(checker, env, target, LinearUse::Observe);
8048 8123
        }
8049 8124
        case ast::NodeValue::FieldAccess(access) => {
8050 -
            if let resultTy = typeFor(checker.resolver, node) {
8125 +
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8051 8126
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8052 -
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8127 +
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8053 8128
                }
8054 8129
            }
8055 8130
            try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
8056 8131
        }
8057 8132
        case ast::NodeValue::ScopeAccess(_) => {}
8058 8133
        case ast::NodeValue::Subscript { container, index } => {
8059 -
            if let resultTy = typeFor(checker.resolver, node) {
8134 +
            if let resultTy = typeFor(&mut *checker.resolver, node) {
8060 8135
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8061 -
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8136 +
                    throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove);
8062 8137
                }
8063 8138
            }
8064 8139
            try checkLinearNode(checker, env, container, LinearUse::Observe);
8065 8140
            try checkLinearNode(checker, env, index, LinearUse::Consume);
8066 8141
        }
8075 8150
            for item in items {
8076 8151
                try checkLinearNode(checker, env, item, LinearUse::Consume);
8077 8152
            }
8078 8153
        }
8079 8154
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
8080 -
            if let itemTy = typeFor(checker.resolver, repeat.item) {
8155 +
            if let itemTy = typeFor(&mut *checker.resolver, repeat.item) {
8081 8156
                if not isCopy(itemTy) {
8082 8157
                    throw emitError(
8083 -
                        checker.resolver,
8158 +
                        &mut *checker.resolver,
8084 8159
                        repeat.item,
8085 8160
                        ErrorKind::LinearDiscard,
8086 8161
                    );
8087 8162
                }
8088 8163
            }
8120 8195
        }
8121 8196
        case ast::NodeValue::IfLet(conditional) => {
8122 8197
            try checkLinearIfLet(checker, env, node, conditional);
8123 8198
        }
8124 8199
        case ast::NodeValue::LetElse(binding) => {
8125 -
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
8200 +
            if let subjectTy = typeFor(&mut *checker.resolver, binding.pattern.scrutinee);
8126 8201
                isLinear(subjectTy)
8127 8202
            {
8128 8203
                throw emitError(
8129 -
                    checker.resolver,
8204 +
                    &mut *checker.resolver,
8130 8205
                    binding.pattern.scrutinee,
8131 8206
                    ErrorKind::LinearPartialMove,
8132 8207
                );
8133 8208
            }
8134 8209
            try checkLinearNode(
8161 8236
                    checker,
8162 8237
                    &mut guardFallbackEnv,
8163 8238
                    binding.elseBranch,
8164 8239
                    LinearUse::Consume,
8165 8240
                );
8241 +
                let previous = fallbackEnv;
8166 8242
                try joinLinearBranches(
8167 8243
                    checker,
8168 8244
                    &mut fallbackEnv,
8169 -
                    &fallbackEnv,
8245 +
                    &previous,
8170 8246
                    &guardFallbackEnv,
8171 8247
                    binding.elseBranch,
8172 8248
                );
8173 8249
            }
8174 8250
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8194 8270
                if let binding = catchClause.binding {
8195 8271
                    try addLinearBinding(checker, &mut branch, binding);
8196 8272
                }
8197 8273
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8198 8274
                try finishLinearScope(checker, &mut branch, start);
8199 -
                try joinLinearBranches(checker, env, env, &branch, node);
8275 +
                let previous = *env;
8276 +
                try joinLinearBranches(checker, env, &previous, &branch, node);
8200 8277
            }
8201 8278
        }
8202 8279
        case ast::NodeValue::While(whileStmt) => {
8203 8280
            enterLinearLoop(checker, env);
8204 8281
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8219 8296
                );
8220 8297
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8221 8298
            }
8222 8299
        }
8223 8300
        case ast::NodeValue::WhileLet(whileStmt) => {
8224 -
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8301 +
            if let subjectTy = typeFor(&mut *checker.resolver, whileStmt.pattern.scrutinee);
8225 8302
                isLinear(subjectTy)
8226 8303
            {
8227 8304
                throw emitError(
8228 -
                    checker.resolver,
8305 +
                    &mut *checker.resolver,
8229 8306
                    whileStmt.pattern.scrutinee,
8230 8307
                    ErrorKind::LinearPartialMove,
8231 8308
                );
8232 8309
            }
8233 8310
            let base = *env;
8244 8321
            try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern);
8245 8322
            if let guard = whileStmt.pattern.guard {
8246 8323
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8247 8324
                let mut guardExit = bodyEnv;
8248 8325
                try finishLinearScope(checker, &mut guardExit, start);
8326 +
                let previous = conditionExit;
8249 8327
                try joinLinearBranches(
8250 8328
                    checker,
8251 8329
                    &mut conditionExit,
8252 -
                    &conditionExit,
8330 +
                    &previous,
8253 8331
                    &guardExit,
8254 8332
                    guard,
8255 8333
                );
8256 8334
            }
8257 8335
            setLinearLoopNaturalExit(checker, &conditionExit);
8270 8348
                );
8271 8349
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8272 8350
            }
8273 8351
        }
8274 8352
        case ast::NodeValue::For(forStmt) => {
8275 -
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8353 +
            if let iterableTy = typeFor(&mut *checker.resolver, forStmt.iterable) {
8276 8354
                if isLinear(iterableTy) {
8277 8355
                    throw emitError(
8278 -
                        checker.resolver,
8356 +
                        &mut *checker.resolver,
8279 8357
                        forStmt.iterable,
8280 8358
                        ErrorKind::LinearPartialMove,
8281 8359
                    );
8282 8360
                }
8283 8361
            }
8363 8441
        else => {}
8364 8442
    }
8365 8443
}
8366 8444
8367 8445
/// Check exact-use ownership for one resolved function.
8368 -
fn checkLinearFn(
8369 -
    self: *mut Resolver,
8446 +
unsafe fn checkLinearFn(
8447 +
    self: &mut Resolver,
8370 8448
    receiver: ?*ast::Node,
8371 8449
    params: *mut [*ast::Node],
8372 8450
    body: *ast::Node,
8373 8451
) throws (ResolveError) {
8374 8452
    let mut checker = LinearChecker {
8375 -
        resolver: self,
8453 +
        resolver: self as *unsafe mut Resolver,
8376 8454
        loopMarks: undefined,
8377 8455
        loopAvailable: undefined,
8378 8456
        loopExitAvailable: undefined,
8379 8457
        loopHasNaturalExit: undefined,
8380 8458
        loopBreakSeen: undefined,
8397 8475
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
8398 8476
    try finishLinearScope(&mut checker, &mut env, 0);
8399 8477
}
8400 8478
8401 8479
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
8402 -
fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
8480 +
unsafe fn resolveModuleDefs(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
8403 8481
    for stmt in block.statements {
8404 8482
        try visitDef(self, stmt);
8405 8483
    }
8406 8484
}
8407 8485
8408 8486
/// Resolve all packages.
8409 -
export fn resolve(self: *mut Resolver, graph: *module::ModuleGraph, packages: *[Pkg]) -> Diagnostics throws (ResolveError) {
8410 -
    set self.moduleGraph = graph;
8487 +
/// The graph must outlive later uses of the resolver.
8488 +
export unsafe fn resolve(self: &mut Resolver, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
8489 +
    set self.moduleGraph = graph as *unsafe module::ModuleGraph;
8411 8490
8412 8491
    // 1. Bind all package roots to enable cross-package references.
8413 8492
    for i in 0..packages.len {
8414 -
        let pkg = &packages[i];
8493 +
        let pkg = packages[i];
8415 8494
        // Enter a new scope for the module.
8416 8495
        let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry);
8417 8496
        // Bind the package root module name in the global package scope.
8418 -
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, self.pkgScope);
8497 +
        let scope = self.pkgScope;
8498 +
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope);
8419 8499
8420 8500
        exitModuleScope(self, enter);
8421 8501
    }
8422 8502
    // 2. Resolve each package's contents.
8423 8503
    for i in 0..packages.len {
8424 -
        let pkg = &packages[i];
8504 +
        let pkg = packages[i];
8425 8505
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
8426 8506
        if not success(&diags) {
8427 8507
            return diags;
8428 8508
        }
8429 8509
    }
8430 8510
    return Diagnostics { errors: self.errors };
8431 8511
}
8432 8512
8433 8513
/// Resolve a package.
8434 -
fn resolvePackage(self: *mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
8514 +
unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
8435 8515
    let rootId = rootEntry.id;
8436 8516
    let scope = self.moduleScopes[rootId as u32]
8437 8517
        else panic "resolvePackage: module scope not found";
8438 8518
8439 8519
    // Set up the module scope for this package.
lib/std/lang/resolver/printer.rad +5 -5
242 242
        }
243 243
    }
244 244
}
245 245
246 246
/// Print a single diagnostic entry.
247 -
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
537 537
        }
538 538
        case super::ErrorKind::BorrowConflict(name) => {
539 539
            printQuoted("conflicting call-scoped loans of '", name);
540 540
        }
541 541
        case super::ErrorKind::UnsafeOperation => {
542 -
            io::print("unsafe pointer operation requires an unsafe declaration");
542 +
            io::print("unsafe pointer operation requires an unsafe function");
543 543
        }
544 544
        case super::ErrorKind::UnsafeCall => {
545 -
            io::print("calling an unsafe function requires an unsafe declaration");
545 +
            io::print("calling an unsafe function requires an unsafe function");
546 546
        }
547 547
        case super::ErrorKind::Internal => {
548 548
            io::print("internal compiler error");
549 549
        }
550 550
        case super::ErrorKind::RecordFieldOutOfOrder { .. } => {
565 565
    }
566 566
    io::print("\n");
567 567
}
568 568
569 569
/// Entry point for printing resolver diagnostics in vim quickfix format.
570 -
export fn printDiagnostics(diag: *super::Diagnostics, res: *super::Resolver) {
570 +
export unsafe fn printDiagnostics(diag: &super::Diagnostics, res: &super::Resolver) {
571 571
    for i in 0..diag.errors.len {
572 572
        printError(&diag.errors[i], res);
573 573
    }
574 574
}
lib/std/lang/resolver/tests.rad +530 -443
67 67
        errors: &mut ERROR_STORAGE[..],
68 68
    };
69 69
}
70 70
71 71
/// Construct a resolver backed by test storage and a synthetic module graph.
72 -
fn testResolver() -> super::Resolver {
72 +
unsafe fn testResolver() -> super::Resolver {
73 73
    // TODO: This should be initialized only once.
74 74
    for i in 0..LITERALS.len {
75 75
        strings::intern(&mut STRING_POOL, LITERALS[i]);
76 76
    }
77 77
    // TODO: Use local static for this.
83 83
84 84
    return res;
85 85
}
86 86
87 87
/// Resolve a block of statements by wrapping them in a synthetic function.
88 -
fn resolveStatements(
89 -
    self: *mut super::Resolver, block: ast::Block, arena: *mut ast::NodeArena
88 +
unsafe fn resolveStatements(
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 93
        return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, 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.
99 -
fn resolveExprStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
99 +
unsafe fn resolveExprStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
100 100
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
101 101
    let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL);
102 102
    parser::advance(&mut p);
103 103
104 104
    let expr = try parser::parseExpr(&mut p) catch {
110 110
    return TestResult { diagnostics, root: expr };
111 111
}
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 -
fn resolveProgramStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
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 117
    let stmt = 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 {
123 123
    return TestResult { diagnostics, root: stmt };
124 124
}
125 125
126 126
/// Parse and analyze a block of statements (eg. inside a function body) for testing.
127 127
/// Use this for code with `let` bindings and expressions, not module-level declarations.
128 -
fn resolveBlockStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
128 +
unsafe fn resolveBlockStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
129 129
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
130 130
    let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
131 131
        panic "resolveBlockStr: parsing failed";
132 132
    };
133 133
    let case ast::NodeValue::Block(block) = parsed.value
141 141
        root: analysis.root,
142 142
    };
143 143
}
144 144
145 145
/// Resolve a module with the full resolution process.
146 -
fn resolveModuleTree(
147 -
    res: *mut super::Resolver,
146 +
unsafe fn resolveModuleTree(
147 +
    res: &mut super::Resolver,
148 148
    rootId: u16
149 149
) -> TestResult throws (testing::TestError) {
150 150
    let root = module::get(&MODULE_GRAPH, rootId)
151 151
        else throw testing::TestError::Failed;
152 152
    let rootAst = root.ast
153 153
        else throw testing::TestError::Failed;
154 -
    let packages: *[super::Pkg] = &[super::Pkg {
154 +
    let packages = [super::Pkg {
155 155
        rootEntry: root,
156 156
        rootAst,
157 157
    }];
158 -
    let diagnostics = try super::resolve(res, &MODULE_GRAPH, packages) catch {
158 +
    let diagnostics = try super::resolve(res, &MODULE_GRAPH, &packages[..]) catch {
159 159
        throw testing::TestError::Failed;
160 160
    };
161 161
    return TestResult { diagnostics, root: rootAst };
162 162
}
163 163
164 164
/// Register a module in the graph and attach a parsed AST to it.
165 165
/// If parentId is nil, registers as a root module.
166 -
fn registerModule(
167 -
    graph: *mut module::ModuleGraph,
166 +
unsafe fn registerModule(
167 +
    graph: &mut module::ModuleGraph,
168 168
    parentId: ?u16,
169 169
    name: *[u8],
170 170
    code: *[u8],
171 -
    arena: *mut ast::NodeArena
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 176
        set modId = try module::registerChild(graph, parent, name, filePath) catch {
189 189
    };
190 190
    return modId;
191 191
}
192 192
193 193
/// Ensure an expression statement produces the expected type and return the expression node.
194 -
fn expectExprStmtType(self: *super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node
194 +
fn expectExprStmtType(self: &super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node
195 195
    throws (testing::TestError)
196 196
{
197 197
    let case ast::NodeValue::ExprStmt(expr) = node.value
198 198
        else throw testing::TestError::Failed;
199 199
    try expectType(self, expr, expected);
200 200
201 201
    return expr;
202 202
}
203 203
204 204
/// Assert that the test result contains no diagnostic errors.
205 -
fn expectNoErrors(r: *TestResult) throws (testing::TestError) {
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
216 216
/// Check if two error kinds match.
217 -
fn errorKindMatches(actual: *super::ErrorKind, expected: super::ErrorKind) -> bool {
217 +
fn errorKindMatches(actual: &super::ErrorKind, expected: super::ErrorKind) -> bool {
218 218
    if let case super::ErrorKind::DuplicateBinding(expectedName) = expected {
219 219
        if let case super::ErrorKind::DuplicateBinding(actualName) = *actual {
220 220
            return mem::eq(actualName, expectedName);
221 221
        }
222 222
        return false;
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;
324 324
}
325 325
326 326
/// Ensure an expression resolves to the expected type annotation.
327 -
fn expectType(self: *super::Resolver, expr: *ast::Node, expected: super::Type)
327 +
fn expectType(self: &super::Resolver, expr: *ast::Node, expected: super::Type)
328 328
    throws (testing::TestError)
329 329
{
330 330
    let actual = super::typeFor(self, expr)
331 331
        else throw testing::TestError::Failed;
332 332
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);
345 345
    try testing::expect(mismatch.actual == actual);
346 346
}
347 347
348 348
/// Resolve a program and require successful analysis.
349 -
fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) {
349 +
unsafe fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) {
350 350
    let mut a = testResolver();
351 351
    let result = try resolveProgramStr(&mut a, program);
352 352
    try expectNoErrors(&result);
353 353
}
354 354
355 355
/// Require an inferred integer type mismatch.
356 -
fn expectIntMismatch(program: *[u8], expected: super::Type)
356 +
unsafe fn expectIntMismatch(program: *[u8], expected: super::Type)
357 357
    throws (testing::TestError)
358 358
{
359 359
    let mut a = testResolver();
360 360
    let result = try resolveProgramStr(&mut a, program);
361 361
    let err = try expectError(&result);
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 +
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)
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 +
fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *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)
430 430
        else throw testing::TestError::Failed;
431 431
    return ty;
432 432
}
433 433
434 434
/// Return the resolved type of a syntax node.
435 -
fn typeOf(a: *super::Resolver, node: *ast::Node) -> super::Type
435 +
fn typeOf(a: &super::Resolver, node: *ast::Node) -> super::Type
436 436
    throws (testing::TestError)
437 437
{
438 438
    let ty = super::typeFor(a, node)
439 439
        else throw testing::TestError::Failed;
440 440
    return ty;
472 472
473 473
    return *target;
474 474
}
475 475
476 476
/// Verify that a node has a constant integer value with the expected magnitude.
477 -
fn expectConstInt(a: *super::Resolver, node: *ast::Node, expected: u32)
477 +
fn expectConstInt(a: &super::Resolver, node: *ast::Node, expected: u32)
478 478
    throws (testing::TestError)
479 479
{
480 480
    let constVal = super::constValueEntry(a, node)
481 481
        else throw testing::TestError::Failed;
482 482
485 485
486 486
    try testing::expect(int.magnitude == expected);
487 487
}
488 488
489 489
/// Resolve an expression that should evaluate to a constant, and verify it equals the expected value.
490 -
fn resolveAndExpectConstExpr(expr: *[u8], expected: u32)
490 +
unsafe fn resolveAndExpectConstExpr(expr: *[u8], expected: u32)
491 491
    throws (testing::TestError)
492 492
{
493 493
    let mut a = testResolver();
494 494
    let result = try resolveExprStr(&mut a, expr);
495 495
    try expectNoErrors(&result);
496 496
    try expectType(&a, result.root, super::Type::U32);
497 497
    try expectConstInt(&a, result.root, expected);
498 498
}
499 499
500 500
/// Resolve a statement that should evaluate to a constant, and verify it equals the expected value.
501 -
fn resolveAndExpectConstStmt(expr: *[u8], expected: u32)
501 +
unsafe fn resolveAndExpectConstStmt(expr: *[u8], expected: u32)
502 502
    throws (testing::TestError)
503 503
{
504 504
    let mut a = testResolver();
505 505
    let result = try resolveProgramStr(&mut a, expr);
506 506
    try expectNoErrors(&result);
509 509
    try expectConstInt(&a, expr, expected);
510 510
}
511 511
512 512
// Tests ///////////////////////////////////////////////////////////////////////
513 513
514 -
@test fn testResolveLit() throws (testing::TestError) {
514 +
@test unsafe fn testResolveLit() throws (testing::TestError) {
515 515
    let mut a = testResolver();
516 516
    let result = try resolveExprStr(&mut a, "true");
517 517
518 518
    try expectNoErrors(&result);
519 519
    try expectType(&a, result.root, super::Type::Bool);
520 520
}
521 521
522 -
@test fn testResolveStringLiteralType() throws (testing::TestError) {
522 +
@test unsafe fn testResolveStringLiteralType() throws (testing::TestError) {
523 523
    let mut a = testResolver();
524 524
    let result = try resolveExprStr(&mut a, "\"hello\"");
525 525
526 526
    try expectNoErrors(&result);
527 527
    let ty = try typeOf(&a, result.root);
528 528
    let elemTy = try expectSliceType(ty, false);
529 529
    try testing::expect(elemTy == super::Type::U8);
530 530
}
531 531
532 -
@test fn testResolveAsNumeric() throws (testing::TestError) {
532 +
@test unsafe fn testResolveAsNumeric() throws (testing::TestError) {
533 533
    {
534 534
        let mut a = testResolver();
535 535
        let result = try resolveExprStr(&mut a, "1 as u32");
536 536
        try expectNoErrors(&result);
537 537
        try expectType(&a, result.root, super::Type::U32);
543 543
        let x = try getBlockStmt(result.root, 1);
544 544
        try expectExprStmtType(&a, x, super::Type::U8);
545 545
    }
546 546
}
547 547
548 -
@test fn testResolveAsInvalid() throws (testing::TestError) {
548 +
@test unsafe fn testResolveAsInvalid() throws (testing::TestError) {
549 549
    let mut a = testResolver();
550 550
    let result = try resolveProgramStr(&mut a, "true as u32");
551 551
552 552
    try expectErrorKind(
553 553
        &result,
556 556
            to: super::Type::U32,
557 557
        })
558 558
    );
559 559
}
560 560
561 -
@test fn testResolveAsUnionToInt() throws (testing::TestError) {
561 +
@test unsafe fn testResolveAsUnionToInt() throws (testing::TestError) {
562 562
    let mut a = testResolver();
563 563
    let program = "union Color { Red } Color::Red as u32;";
564 564
    let result = try resolveProgramStr(&mut a, program);
565 565
    try expectNoErrors(&result);
566 566
567 567
    let red = try getBlockStmt(result.root, 1);
568 568
    try expectExprStmtType(&a, red, super::Type::U32);
569 569
}
570 570
571 -
@test fn testResolveBinding() throws (testing::TestError) {
571 +
@test unsafe fn testResolveBinding() throws (testing::TestError) {
572 572
    let mut a = testResolver();
573 573
    let result = try resolveBlockStr(&mut a, "let x: bool = true; x;");
574 574
    let stmt = try parser::tests::getBlockLastStmt(result.root);
575 575
576 576
    try expectNoErrors(&result);
585 585
    let case super::SymbolData::Value { type: valType, .. } = sym.data
586 586
        else throw testing::TestError::Failed;
587 587
    try testing::expect(valType == super::Type::Bool);
588 588
}
589 589
590 -
@test fn testResolveBindingInvalid() throws (testing::TestError) {
590 +
@test unsafe fn testResolveBindingInvalid() throws (testing::TestError) {
591 591
    let mut a = testResolver();
592 592
    let result = try resolveBlockStr(&mut a, "let x: i32 = true;");
593 593
    let err = try expectError(&result);
594 594
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
595 595
}
596 596
597 -
@test fn testResolveDuplicateBinding() throws (testing::TestError) {
597 +
@test unsafe fn testResolveDuplicateBinding() throws (testing::TestError) {
598 598
    let mut a = testResolver();
599 599
    let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;");
600 600
    let stmt = try parser::tests::getBlockLastStmt(result.root);
601 601
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x"));
602 602
}
603 603
604 -
@test fn testResolveConstLiteralValue() throws (testing::TestError) {
604 +
@test unsafe fn testResolveConstLiteralValue() throws (testing::TestError) {
605 605
    let mut a = testResolver();
606 606
    let program = "constant ANSWER: i32 = 42;";
607 607
    let result = try resolveProgramStr(&mut a, program);
608 608
    try expectNoErrors(&result);
609 609
613 613
    let case super::SymbolData::Constant { type: constType, .. } = sym.data
614 614
        else throw testing::TestError::Failed;
615 615
    try testing::expect(constType == super::Type::I32);
616 616
}
617 617
618 -
@test fn testResolveConstRequiresConstantExpr() throws (testing::TestError) {
618 +
@test unsafe fn testResolveConstRequiresConstantExpr() throws (testing::TestError) {
619 619
    let mut a = testResolver();
620 620
    let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }";
621 621
    let result = try resolveProgramStr(&mut a, program);
622 622
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
623 623
625 625
        else throw testing::TestError::Failed;
626 626
    let case ast::NodeValue::Call(_) = errNode.value
627 627
        else throw testing::TestError::Failed;
628 628
}
629 629
630 -
@test fn testResolveStaticLiteralValue() throws (testing::TestError) {
630 +
@test unsafe fn testResolveStaticLiteralValue() throws (testing::TestError) {
631 631
    let mut a = testResolver();
632 632
    let program = "static COUNTER: i32 = 0;";
633 633
    let result = try resolveProgramStr(&mut a, program);
634 634
    try expectNoErrors(&result);
635 635
639 639
    let case super::SymbolData::Value { type: valType, .. } = sym.data
640 640
        else throw testing::TestError::Failed;
641 641
    try testing::expect(valType == super::Type::I32);
642 642
}
643 643
644 -
@test fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) {
644 +
@test unsafe fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) {
645 645
    let mut a = testResolver();
646 646
    let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();";
647 647
    let result = try resolveProgramStr(&mut a, program);
648 648
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
649 649
651 651
        else throw testing::TestError::Failed;
652 652
    let case ast::NodeValue::Call(_) = errNode.value
653 653
        else throw testing::TestError::Failed;
654 654
}
655 655
656 -
@test fn testSymbolStoresFnAttributes() throws (testing::TestError) {
656 +
@test unsafe fn testSymbolStoresFnAttributes() throws (testing::TestError) {
657 657
    let mut a = testResolver();
658 658
    let program = "@default export fn f() { return; }";
659 659
    let result = try resolveProgramStr(&mut a, program);
660 660
    try expectNoErrors(&result);
661 661
667 667
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
668 668
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
669 669
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern));
670 670
}
671 671
672 -
@test fn testSymbolStoresRecordAttributes() throws (testing::TestError) {
672 +
@test unsafe fn testSymbolStoresRecordAttributes() throws (testing::TestError) {
673 673
    let mut a = testResolver();
674 674
    let program = "export record S { value: i32 }";
675 675
    let result = try resolveProgramStr(&mut a, program);
676 676
    try expectNoErrors(&result);
677 677
682 682
683 683
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
684 684
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
685 685
}
686 686
687 -
@test fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) {
687 +
@test unsafe fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) {
688 688
    let mut a = testResolver();
689 689
    let program = "@default record T { value: i32 }";
690 690
    let result = try resolveProgramStr(&mut a, program);
691 691
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
692 692
}
693 693
694 -
@test fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) {
694 +
@test unsafe fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) {
695 695
    let mut a = testResolver();
696 696
    let program = "@default union Result { Ok, Err }";
697 697
    let result = try resolveProgramStr(&mut a, program);
698 698
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
699 699
}
700 700
701 -
@test fn testResolveArrayLiteralTyped() throws (testing::TestError) {
701 +
@test unsafe fn testResolveArrayLiteralTyped() throws (testing::TestError) {
702 702
    let mut a = testResolver();
703 703
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];");
704 704
    try expectNoErrors(&result);
705 705
706 706
    let stmt = try getBlockStmt(result.root, 0);
709 709
    let arrayTy = try typeOf(&a, decl.value);
710 710
    let elemTy = try expectArrayType(arrayTy, 2);
711 711
    try testing::expect(elemTy == super::Type::I32);
712 712
}
713 713
714 -
@test fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) {
714 +
@test unsafe fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) {
715 715
    let mut a = testResolver();
716 716
    let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];");
717 717
    let err = try expectError(&result);
718 718
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
719 719
}
720 720
721 -
@test fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) {
721 +
@test unsafe fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) {
722 722
    let mut a = testResolver();
723 723
    let result = try resolveProgramStr(&mut a, "let xs = [1, 2];");
724 724
    try expectErrorKind(&result, super::ErrorKind::CannotInferType);
725 725
}
726 726
727 -
@test fn testResolveArrayLiteralOverflow() throws (testing::TestError) {
727 +
@test unsafe fn testResolveArrayLiteralOverflow() throws (testing::TestError) {
728 728
    let mut a = testResolver();
729 729
    let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];");
730 730
    let err = try expectError(&result);
731 731
    let case super::ErrorKind::TypeMismatch(_) = err.kind
732 732
        else throw testing::TestError::Failed;
733 733
}
734 734
735 -
@test fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) {
735 +
@test unsafe fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) {
736 736
    let mut a = testResolver();
737 737
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];");
738 738
    let err = try expectError(&result);
739 739
    let case super::ErrorKind::TypeMismatch(_) = err.kind
740 740
        else throw testing::TestError::Failed;
741 741
}
742 742
743 -
@test fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) {
743 +
@test unsafe fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) {
744 744
    let mut a = testResolver();
745 745
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];");
746 746
    let err = try expectError(&result);
747 747
    let case super::ErrorKind::TypeMismatch(_) = err.kind
748 748
        else throw testing::TestError::Failed;
749 749
}
750 750
751 -
@test fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) {
751 +
@test unsafe fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) {
752 752
    let mut a = testResolver();
753 753
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];");
754 754
    try expectNoErrors(&result);
755 755
}
756 756
757 -
@test fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) {
757 +
@test unsafe fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) {
758 758
    let mut a = testResolver();
759 759
    let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];");
760 760
    try expectNoErrors(&result);
761 761
762 762
    let stmt = try getBlockStmt(result.root, 0);
766 766
    let rowTy = try expectArrayType(gridTy, 2);
767 767
    let elemTy = try expectArrayType(rowTy, 2);
768 768
    try testing::expect(elemTy == super::Type::I32);
769 769
}
770 770
771 -
@test fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) {
771 +
@test unsafe fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) {
772 772
    let mut a = testResolver();
773 773
    let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];");
774 774
    try expectNoErrors(&result);
775 775
776 776
    let stmt = try getBlockStmt(result.root, 0);
781 781
    let case super::Type::Optional(inner) = elemTy
782 782
        else throw testing::TestError::Failed;
783 783
    try testing::expect(*inner == super::Type::I32);
784 784
}
785 785
786 -
@test fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) {
786 +
@test unsafe fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) {
787 787
    let mut a = testResolver();
788 788
    let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];");
789 789
    let err = try expectError(&result);
790 790
    let case super::ErrorKind::TypeMismatch(_) = err.kind
791 791
        else throw testing::TestError::Failed;
792 792
}
793 793
794 -
@test fn testResolveArrayRepeatBasic() throws (testing::TestError) {
794 +
@test unsafe fn testResolveArrayRepeatBasic() throws (testing::TestError) {
795 795
    let mut a = testResolver();
796 796
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];");
797 797
    try expectNoErrors(&result);
798 798
799 799
    let stmt = try getBlockStmt(result.root, 0);
802 802
    let arrayTy = try typeOf(&a, decl.value);
803 803
    let elemTy = try expectArrayType(arrayTy, 3);
804 804
    try testing::expect(elemTy == super::Type::I32);
805 805
}
806 806
807 -
@test fn testResolveArrayRepeatWithExpression() throws (testing::TestError) {
807 +
@test unsafe fn testResolveArrayRepeatWithExpression() throws (testing::TestError) {
808 808
    let mut a = testResolver();
809 809
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];");
810 810
    try expectNoErrors(&result);
811 811
812 812
    let stmt = try getBlockStmt(result.root, 0);
815 815
    let arrayTy = try typeOf(&a, decl.value);
816 816
    let elemTy = try expectArrayType(arrayTy, 5);
817 817
    try testing::expect(elemTy == super::Type::I32);
818 818
}
819 819
820 -
@test fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) {
820 +
@test unsafe fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) {
821 821
    let mut a = testResolver();
822 822
    // `3 * 1` folds to a compile-time constant, so the repeat count is valid.
823 823
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];");
824 824
    try expectNoErrors(&result);
825 825
}
826 826
827 -
@test fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) {
827 +
@test unsafe fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) {
828 828
    let mut a = testResolver();
829 829
    // A function call is not a constant expression.
830 830
    let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];");
831 831
    try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
832 832
}
833 833
834 -
@test fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) {
834 +
@test unsafe fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) {
835 835
    let mut a = testResolver();
836 836
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];");
837 837
    let err = try expectError(&result);
838 838
    let case super::ErrorKind::TypeMismatch(_) = err.kind
839 839
        else throw testing::TestError::Failed;
840 840
}
841 841
842 -
@test fn testResolveArrayIndex() throws (testing::TestError) {
842 +
@test unsafe fn testResolveArrayIndex() throws (testing::TestError) {
843 843
    let mut a = testResolver();
844 844
    let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];";
845 845
    let result = try resolveProgramStr(&mut a, program);
846 846
    try expectNoErrors(&result);
847 847
848 848
    let stmt = try getBlockStmt(result.root, 1);
849 849
    try expectExprStmtType(&a, stmt, super::Type::I32);
850 850
}
851 851
852 -
@test fn testResolveSliceIndex() throws (testing::TestError) {
852 +
@test unsafe fn testResolveSliceIndex() throws (testing::TestError) {
853 853
    let mut a = testResolver();
854 -
    let program = "let xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];";
854 +
    let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];";
855 855
    let result = try resolveProgramStr(&mut a, program);
856 856
    try expectNoErrors(&result);
857 857
858 858
    let sliceStmt = try getBlockStmt(result.root, 1);
859 859
    let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value
864 864
865 865
    let indexStmt = try getBlockStmt(result.root, 2);
866 866
    try expectExprStmtType(&a, indexStmt, super::Type::I32);
867 867
}
868 868
869 -
@test fn testResolveSliceFields() throws (testing::TestError) {
869 +
@test unsafe fn testResolveSliceFields() throws (testing::TestError) {
870 870
    let mut a = testResolver();
871 -
    let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; slice.ptr;";
871 +
    let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; slice.ptr;";
872 872
    let result = try resolveProgramStr(&mut a, program);
873 873
    try expectNoErrors(&result);
874 874
875 875
    let lenStmt = try getBlockStmt(result.root, 2);
876 876
    let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value
884 884
    let ptrTy = try typeOf(&a, ptrExpr);
885 885
    let targetTy = try expectPointerType(ptrTy, false);
886 886
    try testing::expect(targetTy == super::Type::I32);
887 887
}
888 888
889 -
@test fn testResolveSliceLiteralImmutable() throws (testing::TestError) {
889 +
@test unsafe fn testResolveSliceLiteralImmutable() throws (testing::TestError) {
890 890
    let mut a = testResolver();
891 891
    let program = "let slice: *[i32] = &[1, 2, 3];";
892 892
    let result = try resolveProgramStr(&mut a, program);
893 893
    try expectNoErrors(&result);
894 894
}
895 895
896 896
/// Empty array literal infers element type from slice annotation.
897 -
@test fn testResolveSliceLiteralEmpty() throws (testing::TestError) {
897 +
@test unsafe fn testResolveSliceLiteralEmpty() throws (testing::TestError) {
898 898
    let mut a = testResolver();
899 899
    let program = "let slice: *[i32] = &[];";
900 900
    let result = try resolveProgramStr(&mut a, program);
901 901
    try expectNoErrors(&result);
902 902
}
903 903
904 904
/// Nested array literal should infer inner element type from slice annotation.
905 -
@test fn testResolveSliceLiteralNestedArray() throws (testing::TestError) {
905 +
@test unsafe fn testResolveSliceLiteralNestedArray() throws (testing::TestError) {
906 906
    let mut a = testResolver();
907 907
    let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];";
908 908
    let result = try resolveProgramStr(&mut a, program);
909 909
    try expectNoErrors(&result);
910 910
}
911 911
912 -
@test fn testResolveSliceFromArray() throws (testing::TestError) {
912 +
@test unsafe fn testResolveSliceFromArray() throws (testing::TestError) {
913 913
    {
914 914
        let mut a = testResolver();
915 -
        let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];";
915 +
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];";
916 916
        let result = try resolveProgramStr(&mut a, program);
917 917
        try expectNoErrors(&result);
918 918
    } {
919 919
        let mut a = testResolver();
920 -
        let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];";
920 +
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];";
921 921
        let result = try resolveProgramStr(&mut a, program);
922 922
        try expectNoErrors(&result);
923 923
    } {
924 924
        let mut a = testResolver();
925 -
        let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];";
925 +
        let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];";
926 926
        let result = try resolveProgramStr(&mut a, program);
927 927
        try expectNoErrors(&result);
928 928
    } {
929 929
        let mut a = testResolver();
930 -
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];";
930 +
        let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];";
931 931
        let result = try resolveProgramStr(&mut a, program);
932 932
        try expectNoErrors(&result);
933 933
    }
934 934
}
935 935
936 -
@test fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) {
936 +
@test unsafe fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) {
937 937
    let mut a = testResolver();
938 938
    let program = "let slice: *mut [i32] = &[1, 2, 3];";
939 939
    let result = try resolveProgramStr(&mut a, program);
940 940
    let err = try expectError(&result);
941 941
    let case super::ErrorKind::TypeMismatch(_) = err.kind
942 942
        else throw testing::TestError::Failed;
943 943
}
944 944
945 -
@test fn testResolveSliceLiteralMutable() throws (testing::TestError) {
945 +
@test unsafe fn testResolveSliceLiteralMutable() throws (testing::TestError) {
946 946
    let mut a = testResolver();
947 947
    let program = "let slice: *mut [i32] = &mut [1, 2, 3];";
948 948
    let result = try resolveProgramStr(&mut a, program);
949 949
    try expectNoErrors(&result);
950 950
}
951 951
952 -
@test fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) {
952 +
@test unsafe fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) {
953 953
    let mut a = testResolver();
954 954
    let program = "let x: i32 = 0; let ptr: *mut i32 = &x;";
955 955
    let result = try resolveProgramStr(&mut a, program);
956 956
    let err = try expectError(&result);
957 957
    let case super::ErrorKind::TypeMismatch(_) = err.kind
958 958
        else throw testing::TestError::Failed;
959 959
}
960 960
961 -
@test fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) {
961 +
@test unsafe fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) {
962 962
    let mut a = testResolver();
963 -
    let program = "let mut x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;";
963 +
    let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;";
964 964
    let result = try resolveProgramStr(&mut a, program);
965 965
    try expectNoErrors(&result);
966 966
}
967 967
968 -
@test fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) {
968 +
@test unsafe fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) {
969 969
    {
970 970
        let mut a = testResolver();
971 971
        let program = "let x: i32 = 0; let ptr = &mut x;";
972 972
        let result = try resolveProgramStr(&mut a, program);
973 973
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
974 974
    } {
975 975
        let mut a = testResolver();
976 -
        let program = "let mut x: i32 = 0; let ptr = &mut x;";
976 +
        let program = "let mut x: i32 = 0; &mut x;";
977 977
        let result = try resolveProgramStr(&mut a, program);
978 978
        try expectNoErrors(&result);
979 979
    }
980 980
}
981 981
982 -
@test fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) {
982 +
@test unsafe fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) {
983 983
    {
984 984
        let mut a = testResolver();
985 985
        let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];";
986 986
        let result = try resolveProgramStr(&mut a, program);
987 987
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
988 988
    } {
989 989
        let mut a = testResolver();
990 -
        let program = "let mut xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];";
990 +
        let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];";
991 991
        let result = try resolveProgramStr(&mut a, program);
992 992
        try expectNoErrors(&result);
993 993
    }
994 994
}
995 995
996 -
@test fn testResolveSliceCannotAssignToArray() throws (testing::TestError) {
996 +
@test unsafe fn testResolveSliceCannotAssignToArray() throws (testing::TestError) {
997 997
    let mut a = testResolver();
998 998
    let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;";
999 999
    let result = try resolveProgramStr(&mut a, program);
1000 1000
    let err = try expectError(&result);
1001 1001
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1002 1002
        else throw testing::TestError::Failed;
1003 1003
}
1004 1004
1005 -
@test fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) {
1005 +
@test unsafe fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) {
1006 1006
    let mut a = testResolver();
1007 1007
    let program = "let xs: [u8; 2] = [1, 2]; xs[..];";
1008 1008
    let result = try resolveProgramStr(&mut a, program);
1009 1009
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1010 1010
}
1011 1011
1012 -
@test fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) {
1012 +
@test unsafe fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) {
1013 1013
    let mut a = testResolver();
1014 1014
    let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }";
1015 1015
    let result = try resolveProgramStr(&mut a, program);
1016 1016
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1017 1017
}
1018 1018
1019 -
@test fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) {
1019 +
@test unsafe fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) {
1020 1020
    {
1021 1021
        let mut a = testResolver();
1022 1022
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];";
1023 1023
        let result = try resolveProgramStr(&mut a, program);
1024 1024
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1033 1033
        let result = try resolveProgramStr(&mut a, program);
1034 1034
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1035 1035
    }
1036 1036
}
1037 1037
1038 -
@test fn testResolveArrayLenConstValue() throws (testing::TestError) {
1038 +
@test unsafe fn testResolveArrayLenConstValue() throws (testing::TestError) {
1039 1039
    let mut a = testResolver();
1040 1040
    let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;";
1041 1041
    let result = try resolveBlockStr(&mut a, program);
1042 1042
    try expectNoErrors(&result);
1043 1043
1050 1050
        else throw testing::TestError::Failed;
1051 1051
    try testing::expect(lenVal.magnitude == 3);
1052 1052
    try testing::expect(not lenVal.negative);
1053 1053
}
1054 1054
1055 -
@test fn testResolveIndexNonIndexable() throws (testing::TestError) {
1055 +
@test unsafe fn testResolveIndexNonIndexable() throws (testing::TestError) {
1056 1056
    let mut a = testResolver();
1057 1057
    let program = "let flag: bool = true; flag[0];";
1058 1058
    let result = try resolveProgramStr(&mut a, program);
1059 1059
    try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable);
1060 1060
}
1061 1061
1062 -
@test fn testResolveSliceFieldUnknown() throws (testing::TestError) {
1062 +
@test unsafe fn testResolveSliceFieldUnknown() throws (testing::TestError) {
1063 1063
    let mut a = testResolver();
1064 1064
    let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;";
1065 1065
    let result = try resolveProgramStr(&mut a, program);
1066 1066
    try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown"));
1067 1067
}
1068 1068
1069 -
@test fn testResolveArrayFieldUnknown() throws (testing::TestError) {
1069 +
@test unsafe fn testResolveArrayFieldUnknown() throws (testing::TestError) {
1070 1070
    let mut a = testResolver();
1071 1071
    let program = "let xs: [i32; 2] = [1, 2]; xs.field;";
1072 1072
    let result = try resolveProgramStr(&mut a, program);
1073 1073
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
1074 1074
}
1075 1075
1076 -
@test fn testResolveIfConditionRequiresBool() throws (testing::TestError) {
1076 +
@test unsafe fn testResolveIfConditionRequiresBool() throws (testing::TestError) {
1077 1077
    {
1078 1078
        let mut a = testResolver();
1079 1079
        let result = try resolveProgramStr(&mut a, "if 42 {}");
1080 1080
        let err = try expectError(&result);
1081 1081
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1084 1084
        let result = try resolveProgramStr(&mut a, "if true {}");
1085 1085
        try expectNoErrors(&result);
1086 1086
    }
1087 1087
}
1088 1088
1089 -
@test fn testResolveIfLetScopeBinding() throws (testing::TestError) {
1089 +
@test unsafe fn testResolveIfLetScopeBinding() throws (testing::TestError) {
1090 1090
    let mut a = testResolver();
1091 1091
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }");
1092 1092
    try expectNoErrors(&result);
1093 1093
1094 1094
    // Get the if-let statement and verify `x` has type `i32`.
1110 1110
        else throw testing::TestError::Failed;
1111 1111
1112 1112
    try testing::expect(valType == super::Type::I32);
1113 1113
}
1114 1114
1115 -
@test fn testResolveIfLetScopeBindingError() throws (testing::TestError) {
1115 +
@test unsafe fn testResolveIfLetScopeBindingError() throws (testing::TestError) {
1116 1116
    let mut a = testResolver();
1117 1117
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }");
1118 1118
    let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1119 1119
1120 1120
    // Verify the error comes from the else branch (offset 48).
1122 1122
        else throw testing::TestError::Failed;
1123 1123
    try testing::expect(errNode.span.offset == 48);
1124 1124
}
1125 1125
1126 1126
/// Tests that `if let` with a condition expression binds the variable in scope.
1127 -
@test fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) {
1127 +
@test unsafe fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) {
1128 1128
    let mut a = testResolver();
1129 1129
    let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }";
1130 1130
    let result = try resolveProgramStr(&mut a, program);
1131 1131
    try expectNoErrors(&result);
1132 1132
}
1133 1133
1134 -
@test fn testResolveWhileConditionRequiresBool() throws (testing::TestError) {
1134 +
@test unsafe fn testResolveWhileConditionRequiresBool() throws (testing::TestError) {
1135 1135
    {
1136 1136
        let mut a = testResolver();
1137 1137
        let result = try resolveProgramStr(&mut a, "while 1 {}");
1138 1138
        let err = try expectError(&result);
1139 1139
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1142 1142
        let result = try resolveProgramStr(&mut a, "while true {}");
1143 1143
        try expectNoErrors(&result);
1144 1144
    }
1145 1145
}
1146 1146
1147 -
@test fn testResolveWhileLetBindingScope() throws (testing::TestError) {
1147 +
@test unsafe fn testResolveWhileLetBindingScope() throws (testing::TestError) {
1148 1148
    {
1149 1149
        let mut a = testResolver();
1150 1150
        let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }";
1151 1151
        let result = try resolveProgramStr(&mut a, program);
1152 1152
        try expectNoErrors(&result);
1171 1171
        let result = try resolveProgramStr(&mut a, program);
1172 1172
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1173 1173
    }
1174 1174
}
1175 1175
1176 -
@test fn testResolveForArrayBindsElementType() throws (testing::TestError) {
1176 +
@test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) {
1177 1177
    let mut a = testResolver();
1178 1178
    let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }";
1179 1179
    let result = try resolveProgramStr(&mut a, program);
1180 1180
    try expectNoErrors(&result);
1181 1181
1194 1194
    let bindingTy = super::typeFor(&a, loopNode.binding)
1195 1195
        else throw testing::TestError::Failed;
1196 1196
    try testing::expect(bindingTy == super::Type::I32);
1197 1197
}
1198 1198
1199 -
@test fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) {
1199 +
@test unsafe fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) {
1200 1200
    let mut a = testResolver();
1201 1201
    let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }";
1202 1202
    let result = try resolveProgramStr(&mut a, program);
1203 1203
    try expectNoErrors(&result);
1204 1204
1224 1224
    let indexTy = super::typeFor(&a, indexNode)
1225 1225
        else throw testing::TestError::Failed;
1226 1226
    try testing::expect(indexTy == super::Type::U32);
1227 1227
}
1228 1228
1229 -
@test fn testResolveForSliceIterable() throws (testing::TestError) {
1229 +
@test unsafe fn testResolveForSliceIterable() throws (testing::TestError) {
1230 1230
    let mut a = testResolver();
1231 1231
    let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }";
1232 1232
    let result = try resolveProgramStr(&mut a, program);
1233 1233
    try expectNoErrors(&result);
1234 1234
1239 1239
    let bindingTy = super::typeFor(&a, loopNode.binding)
1240 1240
        else throw testing::TestError::Failed;
1241 1241
    try testing::expect(bindingTy == super::Type::I32);
1242 1242
}
1243 1243
1244 -
@test fn testResolveForRequiresIterable() throws (testing::TestError) {
1244 +
@test unsafe fn testResolveForRequiresIterable() throws (testing::TestError) {
1245 1245
    let mut a = testResolver();
1246 1246
    let result = try resolveProgramStr(&mut a, "for x in true { x; }");
1247 1247
    try expectErrorKind(&result, super::ErrorKind::ExpectedIterable);
1248 1248
}
1249 1249
1250 -
@test fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) {
1250 +
@test unsafe fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) {
1251 1251
    let mut a = testResolver();
1252 1252
    let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }");
1253 1253
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
1254 1254
}
1255 1255
1256 -
@test fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) {
1256 +
@test unsafe fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) {
1257 1257
    let mut a = testResolver();
1258 1258
    let program = "let val: i32 = 0; match val { case true => {} }";
1259 1259
    let result = try resolveProgramStr(&mut a, program);
1260 1260
    let err = try expectError(&result);
1261 1261
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1262 1262
}
1263 1263
1264 -
@test fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) {
1264 +
@test unsafe fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) {
1265 1265
    let mut a = testResolver();
1266 1266
    let program = "union First { A }  union Second { B } fn run(val: First) { match val { case Second::B => {} } }";
1267 1267
    let result = try resolveProgramStr(&mut a, program);
1268 1268
    let err = try expectError(&result);
1269 1269
1270 1270
    let firstTy = try getTypeInScopeOf(&a, result.root, "First");
1271 1271
    let secondTy = try getTypeInScopeOf(&a, result.root, "Second");
1272 1272
    try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy));
1273 1273
}
1274 1274
1275 -
@test fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) {
1275 +
@test unsafe fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) {
1276 1276
    let mut a = testResolver();
1277 1277
    let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1278 1278
    let result = try resolveProgramStr(&mut a, program);
1279 1279
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some"));
1280 1280
}
1281 1281
1282 -
@test fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) {
1282 +
@test unsafe fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) {
1283 1283
    let mut a = testResolver();
1284 1284
    let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1285 1285
    let result = try resolveProgramStr(&mut a, program);
1286 1286
    try expectNoErrors(&result);
1287 1287
}
1288 1288
1289 -
@test fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) {
1289 +
@test unsafe fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) {
1290 1290
    let mut a = testResolver();
1291 1291
    let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }";
1292 1292
    let result = try resolveProgramStr(&mut a, program);
1293 1293
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None"));
1294 1294
}
1295 1295
1296 -
@test fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) {
1296 +
@test unsafe fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) {
1297 1297
    let mut a = testResolver();
1298 1298
    let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }";
1299 1299
    let result = try resolveProgramStr(&mut a, program);
1300 1300
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
1301 1301
}
1302 1302
1303 -
@test fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) {
1303 +
@test unsafe fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) {
1304 1304
    {
1305 1305
        let mut a = testResolver();
1306 1306
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }";
1307 1307
        let result = try resolveProgramStr(&mut a, program);
1308 1308
        try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None"));
1312 1312
        let result = try resolveProgramStr(&mut a, program);
1313 1313
        try expectNoErrors(&result);
1314 1314
    }
1315 1315
}
1316 1316
1317 -
@test fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) {
1317 +
@test unsafe fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) {
1318 1318
    let mut a = testResolver();
1319 1319
    let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }";
1320 1320
    let result = try resolveProgramStr(&mut a, program);
1321 1321
    try expectNoErrors(&result);
1322 1322
}
1323 1323
1324 -
@test fn testResolveMatchUnionBindingScope() throws (testing::TestError) {
1324 +
@test unsafe fn testResolveMatchUnionBindingScope() throws (testing::TestError) {
1325 1325
    let mut a = testResolver();
1326 1326
    let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }";
1327 1327
    let result = try resolveProgramStr(&mut a, program);
1328 1328
    try expectNoErrors(&result);
1329 1329
1342 1342
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
1343 1343
        else throw testing::TestError::Failed;
1344 1344
    try testing::expect(payloadValType == super::Type::I32);
1345 1345
}
1346 1346
1347 -
@test fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) {
1347 +
@test unsafe fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) {
1348 1348
    let mut a = testResolver();
1349 1349
    let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }";
1350 1350
    let result = try resolveProgramStr(&mut a, program);
1351 1351
    let err = try expectError(&result);
1352 1352
    let optionTy = try getTypeInScopeOf(&a, result.root, "Opt");
1353 1353
    try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool);
1354 1354
}
1355 1355
1356 -
@test fn testResolveMatchGuardForms() throws (testing::TestError) {
1356 +
@test unsafe fn testResolveMatchGuardForms() throws (testing::TestError) {
1357 1357
    let mut a = testResolver();
1358 1358
    let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }";
1359 1359
    let result = try resolveProgramStr(&mut a, program);
1360 1360
    try expectNoErrors(&result);
1361 1361
}
1362 1362
1363 1363
/// Test that a binding prong binds the subject to the identifier.
1364 -
@test fn testResolveMatchBindingProng() throws (testing::TestError) {
1364 +
@test unsafe fn testResolveMatchBindingProng() throws (testing::TestError) {
1365 1365
    let mut a = testResolver();
1366 1366
    let program = "fn f(value: i32) -> i32 { match value { x => return x } }";
1367 1367
    let result = try resolveProgramStr(&mut a, program);
1368 1368
    try expectNoErrors(&result);
1369 1369
}
1370 1370
1371 1371
/// Test that a binding prong with guard can use the bound variable.
1372 -
@test fn testResolveMatchBindingProngGuard() throws (testing::TestError) {
1372 +
@test unsafe fn testResolveMatchBindingProngGuard() throws (testing::TestError) {
1373 1373
    let mut a = testResolver();
1374 1374
    let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }";
1375 1375
    let result = try resolveProgramStr(&mut a, program);
1376 1376
    try expectNoErrors(&result);
1377 1377
}
1378 1378
1379 1379
/// Test that a binding prong covers all union variants for exhaustiveness.
1380 -
@test fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) {
1380 +
@test unsafe fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) {
1381 1381
    let mut a = testResolver();
1382 1382
    let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }";
1383 1383
    let result = try resolveProgramStr(&mut a, program);
1384 1384
    try expectNoErrors(&result);
1385 1385
}
1386 1386
1387 1387
/// Test that `case x =>` fails if `x` is not in scope, since bare identifiers
1388 1388
/// in case patterns are values to compare against, not bindings.
1389 -
@test fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) {
1389 +
@test unsafe fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) {
1390 1390
    let mut a = testResolver();
1391 1391
    let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }";
1392 1392
    let result = try resolveProgramStr(&mut a, program);
1393 1393
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1394 1394
}
1395 1395
1396 1396
/// Test matching on optionals: exhaustiveness and type unwrapping.
1397 -
@test fn testResolveMatchOptional() throws (testing::TestError) {
1397 +
@test unsafe fn testResolveMatchOptional() throws (testing::TestError) {
1398 1398
    {
1399 1399
        // Exhaustive: binding + nil case.
1400 1400
        let mut a = testResolver();
1401 1401
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }";
1402 1402
        let result = try resolveProgramStr(&mut a, program);
1427 1427
        try expectNoErrors(&result);
1428 1428
    }
1429 1429
}
1430 1430
1431 1431
/// Test that match on non-union types requires exhaustiveness.
1432 -
@test fn testResolveMatchGenericExhaustive() throws (testing::TestError) {
1432 +
@test unsafe fn testResolveMatchGenericExhaustive() throws (testing::TestError) {
1433 1433
    {
1434 1434
        // Match on i32 without catch-all should error.
1435 1435
        let mut a = testResolver();
1436 1436
        let program = "fn f(x: i32) { match x { case 1 => {} } }";
1437 1437
        let result = try resolveProgramStr(&mut a, program);
1456 1456
        try expectNoErrors(&result);
1457 1457
    }
1458 1458
}
1459 1459
1460 1460
/// Test that match on bool requires both true and false cases.
1461 -
@test fn testResolveMatchBoolExhaustive() throws (testing::TestError) {
1461 +
@test unsafe fn testResolveMatchBoolExhaustive() throws (testing::TestError) {
1462 1462
    {
1463 1463
        // Match on bool with both cases is fine.
1464 1464
        let mut a = testResolver();
1465 1465
        let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }";
1466 1466
        let result = try resolveProgramStr(&mut a, program);
1490 1490
        let result = try resolveProgramStr(&mut a, program);
1491 1491
        try expectNoErrors(&result);
1492 1492
    }
1493 1493
}
1494 1494
1495 -
@test fn testResolveBreakRequiresLoop() throws (testing::TestError) {
1495 +
@test unsafe fn testResolveBreakRequiresLoop() throws (testing::TestError) {
1496 1496
    {
1497 1497
        let mut a = testResolver();
1498 1498
        let result = try resolveProgramStr(&mut a, "break;");
1499 1499
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1500 1500
    } {
1502 1502
        let result = try resolveProgramStr(&mut a, "loop { break }");
1503 1503
        try expectNoErrors(&result);
1504 1504
    }
1505 1505
}
1506 1506
1507 -
@test fn testResolveContinueRequiresLoop() throws (testing::TestError) {
1507 +
@test unsafe fn testResolveContinueRequiresLoop() throws (testing::TestError) {
1508 1508
    {
1509 1509
        let mut a = testResolver();
1510 1510
        let result = try resolveProgramStr(&mut a, "continue;");
1511 1511
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1512 1512
    } {
1514 1514
        let result = try resolveProgramStr(&mut a, "while true { continue }");
1515 1515
        try expectNoErrors(&result);
1516 1516
    }
1517 1517
}
1518 1518
1519 -
@test fn testResolveFnTypeVoidNoParams() throws (testing::TestError) {
1519 +
@test unsafe fn testResolveFnTypeVoidNoParams() throws (testing::TestError) {
1520 1520
    let mut a = testResolver();
1521 1521
    let result = try resolveProgramStr(&mut a, "fn f() {} f();");
1522 1522
    try expectNoErrors(&result);
1523 1523
1524 1524
    let blockNode = result.root;
1544 1544
            else throw testing::TestError::Failed;
1545 1545
        try expectType(&a, callExpr, *fnTy.returnType);
1546 1546
    }
1547 1547
}
1548 1548
1549 -
@test fn testResolveFnTypeReturnsValue() throws (testing::TestError) {
1549 +
@test unsafe fn testResolveFnTypeReturnsValue() throws (testing::TestError) {
1550 1550
    let mut a = testResolver();
1551 1551
    let program = "fn f() -> i32 { return 1; } f();";
1552 1552
    let result = try resolveProgramStr(&mut a, program);
1553 1553
    try expectNoErrors(&result);
1554 1554
1575 1575
            else throw testing::TestError::Failed;
1576 1576
        try expectType(&a, callExpr, *fnTy.returnType);
1577 1577
    }
1578 1578
}
1579 1579
1580 -
@test fn testResolveFnTypeSingleParam() throws (testing::TestError) {
1580 +
@test unsafe fn testResolveFnTypeSingleParam() throws (testing::TestError) {
1581 1581
    let mut a = testResolver();
1582 1582
    let program = "fn f(x: i8) {} let x: i8 = 1; f(x);";
1583 1583
    let result = try resolveProgramStr(&mut a, program);
1584 1584
    try expectNoErrors(&result);
1585 1585
1613 1613
            else throw testing::TestError::Failed;
1614 1614
        try expectType(&a, callExpr, *fnTy.returnType);
1615 1615
    }
1616 1616
}
1617 1617
1618 -
@test fn testResolveFnTypeMultipleParams() throws (testing::TestError) {
1618 +
@test unsafe fn testResolveFnTypeMultipleParams() throws (testing::TestError) {
1619 1619
    let mut a = testResolver();
1620 1620
    let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);";
1621 1621
    let result = try resolveProgramStr(&mut a, program);
1622 1622
    try expectNoErrors(&result);
1623 1623
1654 1654
            else throw testing::TestError::Failed;
1655 1655
        try expectType(&a, callExpr, *fnTy.returnType);
1656 1656
    }
1657 1657
}
1658 1658
1659 -
@test fn testResolveFnRecursiveCall() throws (testing::TestError) {
1659 +
@test unsafe fn testResolveFnRecursiveCall() throws (testing::TestError) {
1660 1660
    let mut a = testResolver();
1661 1661
    let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }";
1662 1662
    let result = try resolveProgramStr(&mut a, program);
1663 1663
    try expectNoErrors(&result);
1664 1664
1676 1676
        try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool);
1677 1677
        try testing::expect(*fnTy.returnType == super::Type::Bool);
1678 1678
    }
1679 1679
}
1680 1680
1681 -
@test fn testResolveFnCallMissingArgument() throws (testing::TestError) {
1681 +
@test unsafe fn testResolveFnCallMissingArgument() throws (testing::TestError) {
1682 1682
    let mut a = testResolver();
1683 1683
    let program = "fn f(x: i8) {} f();";
1684 1684
    let result = try resolveProgramStr(&mut a, program);
1685 1685
    // Expect an error when a required parameter is omitted.
1686 1686
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1687 1687
        expected: 1,
1688 1688
        actual: 0,
1689 1689
    }));
1690 1690
}
1691 1691
1692 -
@test fn testResolveFnCallExtraArgument() throws (testing::TestError) {
1692 +
@test unsafe fn testResolveFnCallExtraArgument() throws (testing::TestError) {
1693 1693
    let mut a = testResolver();
1694 1694
    let program = "fn f() {} f(1);";
1695 1695
    let result = try resolveProgramStr(&mut a, program);
1696 1696
    // Passing more arguments than declared should fail.
1697 1697
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1698 1698
        expected: 0,
1699 1699
        actual: 1,
1700 1700
    }));
1701 1701
}
1702 1702
1703 -
@test fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) {
1703 +
@test unsafe fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) {
1704 1704
    let mut a = testResolver();
1705 1705
    let program = "fn f(x: i8) {} f(true);";
1706 1706
    let result = try resolveProgramStr(&mut a, program);
1707 1707
    let err = try expectError(&result);
1708 1708
    // The argument type (bool) should not match the parameter type (i8).
1709 1709
    try expectTypeMismatch(err, super::Type::I8, super::Type::Bool);
1710 1710
}
1711 1711
1712 -
@test fn testResolveFnReturnTypeMismatch() throws (testing::TestError) {
1712 +
@test unsafe fn testResolveFnReturnTypeMismatch() throws (testing::TestError) {
1713 1713
    let mut a = testResolver();
1714 1714
    let program = "fn f() -> i32 { return true; }";
1715 1715
    let result = try resolveProgramStr(&mut a, program);
1716 1716
    let err = try expectError(&result);
1717 1717
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1718 1718
}
1719 1719
1720 -
@test fn testResolveFnReturnVoid() throws (testing::TestError) {
1720 +
@test unsafe fn testResolveFnReturnVoid() throws (testing::TestError) {
1721 1721
    {
1722 1722
        let mut a = testResolver();
1723 1723
        let result = try resolveProgramStr(&mut a, "fn f() { return; }");
1724 1724
        try expectNoErrors(&result);
1725 1725
    } {
1728 1728
        let err = try expectError(&result);
1729 1729
        try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
1730 1730
    }
1731 1731
}
1732 1732
1733 -
@test fn testResolveFnMissingReturn() throws (testing::TestError) {
1733 +
@test unsafe fn testResolveFnMissingReturn() throws (testing::TestError) {
1734 1734
    {
1735 1735
        let mut a = testResolver();
1736 1736
        let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}");
1737 1737
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1738 1738
    } {
1741 1741
        let result = try resolveProgramStr(&mut a, program);
1742 1742
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1743 1743
    }
1744 1744
}
1745 1745
1746 -
@test fn testResolveFnAllPathsReturn() throws (testing::TestError) {
1746 +
@test unsafe fn testResolveFnAllPathsReturn() throws (testing::TestError) {
1747 1747
    let mut a = testResolver();
1748 1748
    let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }";
1749 1749
    let result = try resolveProgramStr(&mut a, program);
1750 1750
    try expectNoErrors(&result);
1751 1751
}
1752 1752
1753 1753
/// Test that match statements with returns in all branches don't require a
1754 1754
/// return at the end of the function.
1755 -
@test fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) {
1755 +
@test unsafe fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) {
1756 1756
    {
1757 1757
        // Union match with all variants returning.
1758 1758
        let mut a = testResolver();
1759 1759
        let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }";
1760 1760
        let result = try resolveProgramStr(&mut a, program);
1772 1772
        let result = try resolveProgramStr(&mut a, program);
1773 1773
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1774 1774
    }
1775 1775
}
1776 1776
1777 -
@test fn testResolveAssign() throws (testing::TestError) {
1777 +
@test unsafe fn testResolveAssign() throws (testing::TestError) {
1778 1778
    {
1779 1779
        let mut a = testResolver();
1780 1780
        let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;");
1781 1781
        try expectNoErrors(&result);
1782 1782
    } {
1801 1801
        let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;");
1802 1802
        try expectNoErrors(&result);
1803 1803
    }
1804 1804
}
1805 1805
1806 -
@test fn testResolveAssignSubscript() throws (testing::TestError) {
1806 +
@test unsafe fn testResolveAssignSubscript() throws (testing::TestError) {
1807 1807
    {
1808 1808
        let mut a = testResolver();
1809 1809
        let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
1810 1810
        let result = try resolveProgramStr(&mut a, program);
1811 1811
        try expectNoErrors(&result);
1812 1812
    }
1813 1813
    {
1814 1814
        let mut a = testResolver();
1815 -
        let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;";
1815 +
        let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;";
1816 1816
        let result = try resolveProgramStr(&mut a, program);
1817 1817
        try expectNoErrors(&result);
1818 1818
    }
1819 1819
    {
1820 1820
        let mut a = testResolver();
1821 -
        let program = "let mut xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;";
1821 +
        let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;";
1822 1822
        let result = try resolveProgramStr(&mut a, program);
1823 1823
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1824 1824
    }
1825 1825
    {
1826 1826
        let mut a = testResolver();
1828 1828
        let result = try resolveProgramStr(&mut a, program);
1829 1829
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1830 1830
    }
1831 1831
    {
1832 1832
        let mut a = testResolver();
1833 -
        let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;";
1833 +
        let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;";
1834 1834
        let result = try resolveProgramStr(&mut a, program);
1835 1835
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1836 1836
    }
1837 1837
}
1838 1838
1839 -
@test fn testResolveAssignIntegerLits() throws (testing::TestError) {
1839 +
@test unsafe fn testResolveAssignIntegerLits() throws (testing::TestError) {
1840 1840
    try expectAnalyzeOk("let x: i8 = 127;");
1841 1841
    try expectAnalyzeOk("let x: i8 = 0x7F;");
1842 1842
    try expectAnalyzeOk("let x: i8 = -128;");
1843 1843
    try expectAnalyzeOk("let x: u8 = 255;");
1844 1844
    try expectAnalyzeOk("let x: u8 = 0b11111111;");
1873 1873
    try expectIntMismatch("let x: i64 = -9223372036854775809;", super::Type::I64);
1874 1874
    try expectIntMismatch("constant LIMIT: u8 = 512;", super::Type::U8);
1875 1875
    try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8);
1876 1876
}
1877 1877
1878 -
@test fn testNilCoercions() throws (testing::TestError) {
1878 +
@test unsafe fn testNilCoercions() throws (testing::TestError) {
1879 1879
    {
1880 1880
        let mut a = testResolver();
1881 1881
        let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;");
1882 1882
        try expectNoErrors(&result);
1883 1883
    } {
1891 1891
        let result = try resolveProgramStr(&mut a, program);
1892 1892
        try expectNoErrors(&result);
1893 1893
    }
1894 1894
}
1895 1895
1896 -
@test fn testOptionalComparedWithNil() throws (testing::TestError) {
1896 +
@test unsafe fn testOptionalComparedWithNil() throws (testing::TestError) {
1897 1897
    let mut a = testResolver();
1898 1898
    let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;";
1899 1899
    let result = try resolveBlockStr(&mut a, program);
1900 1900
    try expectNoErrors(&result);
1901 1901
1903 1903
        let stmt = try getBlockStmt(result.root, i);
1904 1904
        try expectExprStmtType(&a, stmt, super::Type::Bool);
1905 1905
    }
1906 1906
}
1907 1907
1908 -
@test fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) {
1908 +
@test unsafe fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) {
1909 1909
    let mut a = testResolver();
1910 1910
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };";
1911 1911
    let result = try resolveProgramStr(&mut a, program);
1912 1912
    try expectNoErrors(&result);
1913 1913
}
1914 1914
1915 -
@test fn testResolveRecordLiteralMissingField() throws (testing::TestError) {
1915 +
@test unsafe fn testResolveRecordLiteralMissingField() throws (testing::TestError) {
1916 1916
    let mut a = testResolver();
1917 1917
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };";
1918 1918
    let result = try resolveProgramStr(&mut a, program);
1919 1919
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
1920 1920
}
1921 1921
1922 -
@test fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) {
1922 +
@test unsafe fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) {
1923 1923
    let mut a = testResolver();
1924 1924
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };";
1925 1925
    let result = try resolveProgramStr(&mut a, program);
1926 1926
    let err = try expectError(&result);
1927 1927
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1930 1930
        else throw testing::TestError::Failed;
1931 1931
    let case ast::NodeValue::Bool(_) = errNode.value
1932 1932
        else throw testing::TestError::Failed;
1933 1933
}
1934 1934
1935 -
@test fn testResolveRecordLiteralExtraField() throws (testing::TestError) {
1935 +
@test unsafe fn testResolveRecordLiteralExtraField() throws (testing::TestError) {
1936 1936
    let mut a = testResolver();
1937 1937
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };";
1938 1938
    let result = try resolveProgramStr(&mut a, program);
1939 1939
    let err = try expectError(&result);
1940 1940
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
1941 1941
        else throw testing::TestError::Failed;
1942 1942
}
1943 1943
1944 1944
/// Test that anonymous record literals with labels can be passed to functions expecting named records.
1945 -
@test fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) {
1945 +
@test unsafe fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) {
1946 1946
    let mut a = testResolver();
1947 1947
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });";
1948 1948
    let result = try resolveProgramStr(&mut a, program);
1949 1949
    try expectNoErrors(&result);
1950 1950
}
1951 1951
1952 1952
/// Test that anonymous record with wrong field name causes out of order error.
1953 -
@test fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) {
1953 +
@test unsafe fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) {
1954 1954
    let mut a = testResolver();
1955 1955
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });";
1956 1956
    let result = try resolveProgramStr(&mut a, program);
1957 1957
    let err = try expectError(&result);
1958 1958
    let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind
1959 1959
        else throw testing::TestError::Failed;
1960 1960
}
1961 1961
1962 1962
/// Test that anonymous record with wrong field type causes type mismatch.
1963 -
@test fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) {
1963 +
@test unsafe fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) {
1964 1964
    let mut a = testResolver();
1965 1965
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });";
1966 1966
    let result = try resolveProgramStr(&mut a, program);
1967 1967
    let err = try expectError(&result);
1968 1968
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1969 1969
        else throw testing::TestError::Failed;
1970 1970
}
1971 1971
1972 1972
/// Test that anonymous record with missing field causes a missing field error.
1973 -
@test fn testResolveAnonRecordMissingField() throws (testing::TestError) {
1973 +
@test unsafe fn testResolveAnonRecordMissingField() throws (testing::TestError) {
1974 1974
    let mut a = testResolver();
1975 1975
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });";
1976 1976
    let result = try resolveProgramStr(&mut a, program);
1977 1977
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
1978 1978
}
1979 1979
1980 1980
/// Test that anonymous record with extra field causes a count mismatch error.
1981 -
@test fn testResolveAnonRecordExtraField() throws (testing::TestError) {
1981 +
@test unsafe fn testResolveAnonRecordExtraField() throws (testing::TestError) {
1982 1982
    let mut a = testResolver();
1983 1983
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });";
1984 1984
    let result = try resolveProgramStr(&mut a, program);
1985 1985
    let err = try expectError(&result);
1986 1986
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
1987 1987
        else throw testing::TestError::Failed;
1988 1988
}
1989 1989
1990 1990
/// Test that anonymous record fields can be coerced (e.g., i32 to optional).
1991 -
@test fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) {
1991 +
@test unsafe fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) {
1992 1992
    let mut a = testResolver();
1993 1993
    let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });";
1994 1994
    let result = try resolveProgramStr(&mut a, program);
1995 1995
    try expectNoErrors(&result);
1996 1996
}
1997 1997
1998 1998
/// Test that arrays of anonymous records with labeled fields are allowed.
1999 -
@test fn testResolveAnonRecordArray() throws (testing::TestError) {
1999 +
@test unsafe fn testResolveAnonRecordArray() throws (testing::TestError) {
2000 2000
    let mut a = testResolver();
2001 2001
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];";
2002 2002
    let result = try resolveProgramStr(&mut a, program);
2003 2003
    try expectNoErrors(&result);
2004 2004
}
2005 2005
2006 2006
/// Test that arrays of anonymous records with extra fields cause count mismatch.
2007 -
@test fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) {
2007 +
@test unsafe fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) {
2008 2008
    let mut a = testResolver();
2009 2009
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];";
2010 2010
    let result = try resolveProgramStr(&mut a, program);
2011 2011
    let err = try expectError(&result);
2012 2012
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2013 2013
        else throw testing::TestError::Failed;
2014 2014
}
2015 2015
2016 2016
/// Test that unlabeled record declarations are analyzed correctly.
2017 -
@test fn testResolveUnlabeledRecordDecl() throws (testing::TestError) {
2017 +
@test unsafe fn testResolveUnlabeledRecordDecl() throws (testing::TestError) {
2018 2018
    let mut a = testResolver();
2019 2019
    let program = "record R(i32, bool);";
2020 2020
    let result = try resolveProgramStr(&mut a, program);
2021 2021
    try expectNoErrors(&result);
2022 2022
2028 2028
    try testing::expect(recordType.fields.len == 2);
2029 2029
    try testing::expect(recordType.fields[0].name == nil);
2030 2030
    try testing::expect(recordType.fields[1].name == nil);
2031 2031
}
2032 2032
2033 -
@test fn testResolveLabeledRecordDecl() throws (testing::TestError) {
2033 +
@test unsafe fn testResolveLabeledRecordDecl() throws (testing::TestError) {
2034 2034
    let mut a = testResolver();
2035 2035
    let program = "record R { x: i32, y: i32 }";
2036 2036
    let result = try resolveProgramStr(&mut a, program);
2037 2037
    try expectNoErrors(&result);
2038 2038
2043 2043
    try testing::expect(recordType.fields.len == 2);
2044 2044
    try testing::expect(recordType.fields[0].name <> nil);
2045 2045
    try testing::expect(recordType.fields[1].name <> nil);
2046 2046
}
2047 2047
2048 -
@test fn testResolveRecordFieldAccessValid() throws (testing::TestError) {
2048 +
@test unsafe fn testResolveRecordFieldAccessValid() throws (testing::TestError) {
2049 2049
    let mut a = testResolver();
2050 2050
    let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;";
2051 2051
    let result = try resolveProgramStr(&mut a, program);
2052 2052
    try expectNoErrors(&result);
2053 2053
2054 2054
    let fieldStmt = try getBlockStmt(result.root, 2);
2055 2055
    try expectExprStmtType(&a, fieldStmt, super::Type::U8);
2056 2056
}
2057 2057
2058 -
@test fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) {
2058 +
@test unsafe fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) {
2059 2059
    let mut a = testResolver();
2060 2060
    let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;";
2061 2061
    let result = try resolveProgramStr(&mut a, program);
2062 2062
    try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y"));
2063 2063
}
2064 2064
2065 -
@test fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) {
2065 +
@test unsafe fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) {
2066 2066
    let mut a = testResolver();
2067 2067
    let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;";
2068 2068
    let result = try resolveProgramStr(&mut a, program);
2069 2069
    try expectNoErrors(&result);
2070 2070
2071 2071
    let stmt = try getBlockStmt(result.root, 2);
2072 2072
    try expectExprStmtType(&a, stmt, super::Type::I32);
2073 2073
}
2074 2074
2075 -
@test fn testResolveRecordFieldAccessChained() throws (testing::TestError) {
2075 +
@test unsafe fn testResolveRecordFieldAccessChained() throws (testing::TestError) {
2076 2076
    let mut a = testResolver();
2077 2077
    let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;";
2078 2078
    let result = try resolveProgramStr(&mut a, program);
2079 2079
    try expectNoErrors(&result);
2080 2080
2081 2081
    let stmt = try getBlockStmt(result.root, 4);
2082 2082
    try expectExprStmtType(&a, stmt, super::Type::I32);
2083 2083
}
2084 2084
2085 -
@test fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) {
2085 +
@test unsafe fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) {
2086 2086
    let mut a = testResolver();
2087 2087
    let program = "let x: i32 = 42; x.field;";
2088 2088
    let result = try resolveBlockStr(&mut a, program);
2089 2089
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2090 2090
}
2091 2091
2092 -
@test fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) {
2092 +
@test unsafe fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) {
2093 2093
    let mut a = testResolver();
2094 2094
    let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;";
2095 2095
    let result = try resolveProgramStr(&mut a, program);
2096 2096
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
2097 2097
}
2098 2098
2099 -
@test fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) {
2099 +
@test unsafe fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) {
2100 2100
    let mut a = testResolver();
2101 2101
    let program = "let b: bool = true; b.field;";
2102 2102
    let result = try resolveProgramStr(&mut a, program);
2103 2103
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2104 2104
}
2105 2105
2106 -
@test fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) {
2106 +
@test unsafe fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) {
2107 2107
    let mut a = testResolver();
2108 2108
    let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;";
2109 2109
    let result = try resolveProgramStr(&mut a, program);
2110 2110
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2111 2111
}
2112 2112
2113 2113
/// Records may reference themselves through pointers without causing resolution errors.
2114 -
@test fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) {
2114 +
@test unsafe fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) {
2115 2115
    let mut a = testResolver();
2116 2116
    let program = "record A { next: *A }";
2117 2117
    let result = try resolveProgramStr(&mut a, program);
2118 2118
    try expectNoErrors(&result);
2119 2119
}
2120 2120
2121 2121
/// Mutually recursive records should resolve without infinite loops.
2122 -
@test fn testResolveRecordMutuallyRecursive() throws (testing::TestError) {
2122 +
@test unsafe fn testResolveRecordMutuallyRecursive() throws (testing::TestError) {
2123 2123
    let mut a = testResolver();
2124 2124
    let program = "record A { b: *B } record B { a: *A }";
2125 2125
    let result = try resolveProgramStr(&mut a, program);
2126 2126
    try expectNoErrors(&result);
2127 2127
}
2128 2128
2129 2129
/// Unions may reference themselves through pointers without causing resolution errors.
2130 -
@test fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) {
2130 +
@test unsafe fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) {
2131 2131
    let mut a = testResolver();
2132 2132
    let program = "union List { Cons(*List), Nil }";
2133 2133
    let result = try resolveProgramStr(&mut a, program);
2134 2134
    try expectNoErrors(&result);
2135 2135
}
2136 2136
2137 2137
/// Mutually recursive unions should resolve without infinite loops.
2138 -
@test fn testResolveUnionMutuallyRecursive() throws (testing::TestError) {
2138 +
@test unsafe fn testResolveUnionMutuallyRecursive() throws (testing::TestError) {
2139 2139
    let mut a = testResolver();
2140 2140
    let program = "union A { HasB(*B), None } union B { HasA(*A), None }";
2141 2141
    let result = try resolveProgramStr(&mut a, program);
2142 2142
    try expectNoErrors(&result);
2143 2143
}
2144 2144
2145 2145
/// Unions with record payloads containing slice references to self should resolve.
2146 2146
/// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`.
2147 -
@test fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) {
2147 +
@test unsafe fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) {
2148 2148
    let mut a = testResolver();
2149 2149
    let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }";
2150 2150
    let result = try resolveProgramStr(&mut a, program);
2151 2151
    try expectNoErrors(&result);
2152 2152
}
2153 2153
2154 -
@test fn testUndefinedCoercions() throws (testing::TestError) {
2154 +
@test unsafe fn testUndefinedCoercions() throws (testing::TestError) {
2155 2155
    {
2156 2156
        let mut a = testResolver();
2157 2157
        let result = try resolveBlockStr(&mut a, "let count: i32 = undefined;");
2158 2158
        try expectNoErrors(&result);
2159 2159
    } {
2172 2172
        let result = try resolveProgramStr(&mut a, program);
2173 2173
        try expectNoErrors(&result);
2174 2174
    }
2175 2175
}
2176 2176
2177 -
@test fn testResolveBlockVoid() throws (testing::TestError) {
2177 +
@test unsafe fn testResolveBlockVoid() throws (testing::TestError) {
2178 2178
    let mut a = testResolver();
2179 2179
    let result = try resolveProgramStr(&mut a, "{ 42; }");
2180 2180
    try expectNoErrors(&result);
2181 2181
2182 2182
    let block = try getBlockStmt(result.root, 0);
2183 2183
    try expectType(&a, block, super::Type::Void);
2184 2184
}
2185 2185
2186 -
@test fn testResolveBlockNever() throws (testing::TestError) {
2186 +
@test unsafe fn testResolveBlockNever() throws (testing::TestError) {
2187 2187
    let mut a = testResolver();
2188 2188
    let result = try resolveProgramStr(&mut a, "{ panic; }");
2189 2189
    try expectNoErrors(&result);
2190 2190
2191 2191
    let block = try getBlockStmt(result.root, 0);
2192 2192
    try expectType(&a, block, super::Type::Never);
2193 2193
}
2194 2194
2195 -
@test fn testResolveIfAllBranchesNever() throws (testing::TestError) {
2195 +
@test unsafe fn testResolveIfAllBranchesNever() throws (testing::TestError) {
2196 2196
    let mut a = testResolver();
2197 2197
    let program = "if true { panic; } else { panic; }";
2198 2198
    let result = try resolveProgramStr(&mut a, program);
2199 2199
    try expectNoErrors(&result);
2200 2200
2201 2201
    let stmt = try getBlockStmt(result.root, 0);
2202 2202
    try expectType(&a, stmt, super::Type::Never);
2203 2203
}
2204 2204
2205 -
@test fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) {
2205 +
@test unsafe fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) {
2206 2206
    let mut a = testResolver();
2207 2207
    let program = "if true { panic; } else {}";
2208 2208
    let result = try resolveProgramStr(&mut a, program);
2209 2209
    try expectNoErrors(&result);
2210 2210
2211 2211
    let stmt = try getBlockStmt(result.root, 0);
2212 2212
    try expectType(&a, stmt, super::Type::Void);
2213 2213
}
2214 2214
2215 -
@test fn testResolveLetElse() throws (testing::TestError) {
2215 +
@test unsafe fn testResolveLetElse() throws (testing::TestError) {
2216 2216
    let mut a = testResolver();
2217 2217
    let program = "let opt: ?i32 = 42; let value = opt else panic; value;";
2218 2218
    let result = try resolveProgramStr(&mut a, program);
2219 2219
    try expectNoErrors(&result);
2220 2220
2235 2235
    }
2236 2236
    // The let-else statement itself should be typed as void.
2237 2237
    try expectType(&a, letElseNode, super::Type::Void);
2238 2238
}
2239 2239
2240 -
@test fn testResolveLetElseDefaultValue() throws (testing::TestError) {
2240 +
@test unsafe fn testResolveLetElseDefaultValue() throws (testing::TestError) {
2241 2241
    let mut a = testResolver();
2242 2242
    let program = "let opt: ?i32 = nil; let value = opt else 42; value;";
2243 2243
    let result = try resolveProgramStr(&mut a, program);
2244 2244
    try expectNoErrors(&result);
2245 2245
}
2246 2246
2247 -
@test fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) {
2247 +
@test unsafe fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) {
2248 2248
    let mut a = testResolver();
2249 2249
    let program = "let opt: ?i32 = nil; let value = opt else {}; value;";
2250 2250
    let result = try resolveProgramStr(&mut a, program);
2251 2251
    let err = try expectError(&result);
2252 2252
    try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2253 2253
}
2254 2254
2255 -
@test fn testResolveLetElseRequiresOptional() throws (testing::TestError) {
2255 +
@test unsafe fn testResolveLetElseRequiresOptional() throws (testing::TestError) {
2256 2256
    let mut a = testResolver();
2257 2257
    let program = "let x: i32 = 42; let value = x else panic;";
2258 2258
    let result = try resolveProgramStr(&mut a, program);
2259 2259
    try expectErrorKind(&result, super::ErrorKind::ExpectedOptional);
2260 2260
}
2261 2261
2262 2262
/// Test that `if let mut` produces a mutable binding.
2263 -
@test fn testResolveIfLetMut() throws (testing::TestError) {
2263 +
@test unsafe fn testResolveIfLetMut() throws (testing::TestError) {
2264 2264
    let mut a = testResolver();
2265 2265
    let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }";
2266 2266
    let result = try resolveProgramStr(&mut a, program);
2267 2267
    try expectNoErrors(&result);
2268 2268
}
2269 2269
2270 2270
/// Test that `if let` (without mut) rejects assignment.
2271 -
@test fn testResolveIfLetImmutable() throws (testing::TestError) {
2271 +
@test unsafe fn testResolveIfLetImmutable() throws (testing::TestError) {
2272 2272
    let mut a = testResolver();
2273 2273
    let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }";
2274 2274
    let result = try resolveProgramStr(&mut a, program);
2275 2275
    let err = try expectError(&result);
2276 2276
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2277 2277
}
2278 2278
2279 2279
/// Test that `let mut ... else` produces a mutable binding.
2280 -
@test fn testResolveLetMutElse() throws (testing::TestError) {
2280 +
@test unsafe fn testResolveLetMutElse() throws (testing::TestError) {
2281 2281
    let mut a = testResolver();
2282 2282
    let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;";
2283 2283
    let result = try resolveProgramStr(&mut a, program);
2284 2284
    try expectNoErrors(&result);
2285 2285
}
2286 2286
2287 2287
/// Test that `let ... else` (without mut) rejects assignment.
2288 -
@test fn testResolveLetElseImmutable() throws (testing::TestError) {
2288 +
@test unsafe fn testResolveLetElseImmutable() throws (testing::TestError) {
2289 2289
    let mut a = testResolver();
2290 2290
    let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;";
2291 2291
    let result = try resolveProgramStr(&mut a, program);
2292 2292
    let err = try expectError(&result);
2293 2293
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2294 2294
}
2295 2295
2296 -
@test fn testResolveLetCaseElse() throws (testing::TestError) {
2296 +
@test unsafe fn testResolveLetCaseElse() throws (testing::TestError) {
2297 2297
    {
2298 2298
        let mut a = testResolver();
2299 2299
        let program = "let case _ = 1 else panic;";
2300 2300
        let result = try resolveProgramStr(&mut a, program);
2301 2301
        try expectNoErrors(&result);
2305 2305
        let result = try resolveProgramStr(&mut a, program);
2306 2306
        try expectNoErrors(&result);
2307 2307
    }
2308 2308
}
2309 2309
2310 -
@test fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) {
2310 +
@test unsafe fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) {
2311 2311
    let mut a = testResolver();
2312 2312
    let program = "let case _ = 1 else {};";
2313 2313
    let result = try resolveProgramStr(&mut a, program);
2314 2314
    let err = try expectError(&result);
2315 2315
    try expectTypeMismatch(err, super::Type::Int, super::Type::Void);
2316 2316
}
2317 2317
2318 -
@test fn testResolveTryValidPropagation() throws (testing::TestError) {
2318 +
@test unsafe fn testResolveTryValidPropagation() throws (testing::TestError) {
2319 2319
    let mut a = testResolver();
2320 2320
    let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }";
2321 2321
    let result = try resolveProgramStr(&mut a, program);
2322 2322
    try expectNoErrors(&result);
2323 2323
}
2324 2324
2325 -
@test fn testResolveTryRequiresThrowsClause() throws (testing::TestError) {
2325 +
@test unsafe fn testResolveTryRequiresThrowsClause() throws (testing::TestError) {
2326 2326
    let mut a = testResolver();
2327 2327
    let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }";
2328 2328
    let result = try resolveProgramStr(&mut a, program);
2329 2329
    try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows);
2330 2330
}
2331 2331
2332 -
@test fn testResolveTryIncompatibleError() throws (testing::TestError) {
2332 +
@test unsafe fn testResolveTryIncompatibleError() throws (testing::TestError) {
2333 2333
    let mut a = testResolver();
2334 2334
    let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }";
2335 2335
    let result = try resolveProgramStr(&mut a, program);
2336 2336
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
2337 2337
}
2338 2338
2339 -
@test fn testResolveTryNonThrowing() throws (testing::TestError) {
2339 +
@test unsafe fn testResolveTryNonThrowing() throws (testing::TestError) {
2340 2340
    let mut a = testResolver();
2341 2341
    let program = "fn safe() {} fn caller() throws (i32) { try safe() }";
2342 2342
    let result = try resolveProgramStr(&mut a, program);
2343 2343
    try expectErrorKind(&result, super::ErrorKind::TryNonThrowing);
2344 2344
}
2345 2345
2346 -
@test fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) {
2346 +
@test unsafe fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) {
2347 2347
    let mut a = testResolver();
2348 2348
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }";
2349 2349
    let result = try resolveProgramStr(&mut a, program);
2350 2350
    try expectNoErrors(&result);
2351 2351
}
2352 2352
2353 -
@test fn testResolveTryCatchBlockDiverges() throws (testing::TestError) {
2353 +
@test unsafe fn testResolveTryCatchBlockDiverges() throws (testing::TestError) {
2354 2354
    let mut a = testResolver();
2355 2355
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }";
2356 2356
    let result = try resolveProgramStr(&mut a, program);
2357 2357
    try expectNoErrors(&result);
2358 2358
}
2359 2359
2360 -
@test fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) {
2360 +
@test unsafe fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) {
2361 2361
    let mut a = testResolver();
2362 2362
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }";
2363 2363
    let result = try resolveProgramStr(&mut a, program);
2364 2364
    let err = try expectError(&result);
2365 2365
    try expectTypeMismatch(err, super::Type::U32, super::Type::Void);
2366 2366
}
2367 2367
2368 -
@test fn testResolveCallMissingTry() throws (testing::TestError) {
2368 +
@test unsafe fn testResolveCallMissingTry() throws (testing::TestError) {
2369 2369
    let mut a = testResolver();
2370 2370
    let program = "fn fallible() throws (i32) {} fn caller() { fallible() }";
2371 2371
    let result = try resolveProgramStr(&mut a, program);
2372 2372
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
2373 2373
}
2374 2374
2375 2375
/// Test that `try?` converts errors to optionals without requiring caller to throw.
2376 -
@test fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) {
2376 +
@test unsafe fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) {
2377 2377
    // `try?` should wrap the return type in optional and not require caller to throw.
2378 2378
    {
2379 2379
        let mut a = testResolver();
2380 2380
        let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }";
2381 2381
        let result = try resolveProgramStr(&mut a, program);
2395 2395
        let result = try resolveProgramStr(&mut a, program);
2396 2396
        try expectNoErrors(&result);
2397 2397
    }
2398 2398
}
2399 2399
2400 -
@test fn testResolveThrowValid() throws (testing::TestError) {
2400 +
@test unsafe fn testResolveThrowValid() throws (testing::TestError) {
2401 2401
    let mut a = testResolver();
2402 2402
    let program = "fn fail() throws (i32) { throw 1; }";
2403 2403
    let result = try resolveProgramStr(&mut a, program);
2404 2404
    try expectNoErrors(&result);
2405 2405
}
2406 2406
2407 -
@test fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) {
2407 +
@test unsafe fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) {
2408 2408
    let mut a = testResolver();
2409 2409
    let program = "fn fail() { throw 1; }";
2410 2410
    let result = try resolveProgramStr(&mut a, program);
2411 2411
    try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows);
2412 2412
}
2413 2413
2414 -
@test fn testResolveThrowIncompatibleError() throws (testing::TestError) {
2414 +
@test unsafe fn testResolveThrowIncompatibleError() throws (testing::TestError) {
2415 2415
    let mut a = testResolver();
2416 2416
    let program = "fn fail() throws (i32) { throw true; }";
2417 2417
    let result = try resolveProgramStr(&mut a, program);
2418 2418
    try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError);
2419 2419
}
2420 2420
2421 2421
// Binary operation tests //////////////////////////////////////////////////////
2422 2422
2423 -
@test fn testResolveBinaryOpArithmetic() throws (testing::TestError) {
2423 +
@test unsafe fn testResolveBinaryOpArithmetic() throws (testing::TestError) {
2424 2424
    {
2425 2425
        let mut a = testResolver();
2426 2426
        let result = try resolveExprStr(&mut a, "4 + 4");
2427 2427
        try expectNoErrors(&result);
2428 2428
        try expectType(&a, result.root, super::Type::Int);
2476 2476
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2477 2477
        try expectExprStmtType(&a, stmt, super::Type::I32);
2478 2478
    }
2479 2479
}
2480 2480
2481 -
@test fn testResolveBinaryOpComparison() throws (testing::TestError) {
2481 +
@test unsafe fn testResolveBinaryOpComparison() throws (testing::TestError) {
2482 2482
    {
2483 2483
        let mut a = testResolver();
2484 2484
        let result = try resolveExprStr(&mut a, "5 == 5");
2485 2485
        try expectNoErrors(&result);
2486 2486
        try expectType(&a, result.root, super::Type::Bool);
2532 2532
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2533 2533
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2534 2534
    }
2535 2535
}
2536 2536
2537 -
@test fn testResolveBinaryOpLogical() throws (testing::TestError) {
2537 +
@test unsafe fn testResolveBinaryOpLogical() throws (testing::TestError) {
2538 2538
    {
2539 2539
        let mut a = testResolver();
2540 2540
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;");
2541 2541
        try expectNoErrors(&result);
2542 2542
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2553 2553
        try expectNoErrors(&result);
2554 2554
        try expectType(&a, result.root, super::Type::Bool);
2555 2555
    }
2556 2556
}
2557 2557
2558 -
@test fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) {
2558 +
@test unsafe fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) {
2559 2559
    {
2560 2560
        let mut a = testResolver();
2561 2561
        let result = try resolveProgramStr(&mut a, "4 + true");
2562 2562
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2563 2563
    } {
2585 2585
        let result = try resolveProgramStr(&mut a, "1 + (true * 3)");
2586 2586
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2587 2587
    }
2588 2588
}
2589 2589
2590 -
@test fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) {
2590 +
@test unsafe fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) {
2591 2591
    {
2592 2592
        let mut a = testResolver();
2593 2593
        let result = try resolveProgramStr(&mut a, "42 and true");
2594 2594
        let err = try expectError(&result);
2595 2595
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2604 2604
        let err = try expectError(&result);
2605 2605
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2606 2606
    }
2607 2607
}
2608 2608
2609 -
@test fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) {
2609 +
@test unsafe fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) {
2610 2610
    let mut a = testResolver();
2611 2611
    let result = try resolveProgramStr(&mut a, "true < false");
2612 2612
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2613 2613
}
2614 2614
2615 2615
// Unary operation tests ///////////////////////////////////////////////////////
2616 2616
2617 -
@test fn testResolveUnaryOpNot() throws (testing::TestError) {
2617 +
@test unsafe fn testResolveUnaryOpNot() throws (testing::TestError) {
2618 2618
    {
2619 2619
        let mut a = testResolver();
2620 2620
        let result = try resolveExprStr(&mut a, "not true");
2621 2621
        try expectNoErrors(&result);
2622 2622
        try expectType(&a, result.root, super::Type::Bool);
2642 2642
        let err = try expectError(&result);
2643 2643
        try expectTypeMismatch(err, super::Type::Bool, super::Type::I32);
2644 2644
    }
2645 2645
}
2646 2646
2647 -
@test fn testResolveUnaryOpNeg() throws (testing::TestError) {
2647 +
@test unsafe fn testResolveUnaryOpNeg() throws (testing::TestError) {
2648 2648
    {
2649 2649
        let mut a = testResolver();
2650 2650
        let result = try resolveExprStr(&mut a, "-42");
2651 2651
        try expectNoErrors(&result);
2652 2652
        try expectType(&a, result.root, super::Type::Int);
2676 2676
        let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;");
2677 2677
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2678 2678
    }
2679 2679
}
2680 2680
2681 -
@test fn testResolveUnaryOpBitNot() throws (testing::TestError) {
2681 +
@test unsafe fn testResolveUnaryOpBitNot() throws (testing::TestError) {
2682 2682
    {
2683 2683
        let mut a = testResolver();
2684 2684
        let result = try resolveExprStr(&mut a, "~42");
2685 2685
        try expectNoErrors(&result);
2686 2686
        try expectType(&a, result.root, super::Type::Int);
2710 2710
        let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;");
2711 2711
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2712 2712
    }
2713 2713
}
2714 2714
2715 -
@test fn testResolveUnaryOpNested() throws (testing::TestError) {
2715 +
@test unsafe fn testResolveUnaryOpNested() throws (testing::TestError) {
2716 2716
    {
2717 2717
        let mut a = testResolver();
2718 2718
        let result = try resolveExprStr(&mut a, "not not true");
2719 2719
        try expectNoErrors(&result);
2720 2720
        try expectType(&a, result.root, super::Type::Bool);
2742 2742
//     try expectNoErrors(&result);
2743 2743
// }
2744 2744
2745 2745
// Dereference tests //////////////////////////////////////////////////////////
2746 2746
2747 -
@test fn testResolveDeref() throws (testing::TestError) {
2747 +
@test unsafe fn testResolveDeref() throws (testing::TestError) {
2748 2748
    {
2749 2749
        let mut a = testResolver();
2750 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 42; let ptr: *i32 = &x; *ptr;");
2750 +
        let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;");
2751 2751
        try expectNoErrors(&result);
2752 2752
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2753 2753
        try expectExprStmtType(&a, stmt, super::Type::I32);
2754 2754
    } {
2755 2755
        let mut a = testResolver();
2760 2760
        let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;");
2761 2761
        try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
2762 2762
    }
2763 2763
}
2764 2764
2765 -
@test fn testResolveAssignDeref() throws (testing::TestError) {
2765 +
@test unsafe fn testResolveAssignDeref() throws (testing::TestError) {
2766 2766
    {
2767 2767
        let mut a = testResolver();
2768 -
        let program = "let mut x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;";
2768 +
        let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;";
2769 2769
        let result = try resolveProgramStr(&mut a, program);
2770 2770
        try expectNoErrors(&result);
2771 2771
    } {
2772 2772
        let mut a = testResolver();
2773 -
        let program = "let mut x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;";
2773 +
        let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;";
2774 2774
        let result = try resolveProgramStr(&mut a, program);
2775 2775
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2776 2776
    } {
2777 2777
        let mut a = testResolver();
2778 -
        let program = "let mut x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;";
2778 +
        let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;";
2779 2779
        let result = try resolveProgramStr(&mut a, program);
2780 2780
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2781 2781
    } {
2782 2782
        let mut a = testResolver();
2783 -
        let program = "let mut x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;";
2783 +
        let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;";
2784 2784
        let result = try resolveProgramStr(&mut a, program);
2785 2785
        try expectNoErrors(&result);
2786 2786
    }
2787 2787
}
2788 2788
2789 2789
// Type inference tests ///////////////////////////////////////////////////////
2790 2790
2791 -
@test fn testResolveBasicTypeInference() throws (testing::TestError) {
2791 +
@test unsafe fn testResolveBasicTypeInference() throws (testing::TestError) {
2792 2792
    {
2793 2793
        // Boolean literals are unambiguous.
2794 2794
        let mut a = testResolver();
2795 2795
        let result = try resolveProgramStr(&mut a, "let x = true; x;");
2796 2796
        try expectNoErrors(&result);
2805 2805
    }
2806 2806
}
2807 2807
2808 2808
// Union tests /////////////////////////////////////////////////////////////////
2809 2809
2810 -
@test fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) {
2810 +
@test unsafe fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) {
2811 2811
    let mut a = testResolver();
2812 2812
    let program = "union Status { Ok, Error } Status::Ok;";
2813 2813
    let result = try resolveProgramStr(&mut a, program);
2814 2814
2815 2815
    let ty = try getTypeInScopeOf(&a, result.root, "Status");
2824 2824
    let stmt = try getBlockStmt(result.root, 1);
2825 2825
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2826 2826
    try expectNoErrors(&result);
2827 2827
}
2828 2828
2829 -
@test fn testResolveUnionVariantWithPayload() throws (testing::TestError) {
2829 +
@test unsafe fn testResolveUnionVariantWithPayload() throws (testing::TestError) {
2830 2830
    let mut a = testResolver();
2831 2831
    let program = "union R { Ok(i32), Err(bool) } R::Ok(42);";
2832 2832
    let result = try resolveProgramStr(&mut a, program);
2833 2833
    try expectNoErrors(&result);
2834 2834
2844 2844
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2845 2845
2846 2846
    // TODO: Test payload type.
2847 2847
}
2848 2848
2849 -
@test fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) {
2849 +
@test unsafe fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) {
2850 2850
    let mut a = testResolver();
2851 2851
    let program = "union R { Ok = 7, Err = 11 } R::Ok;";
2852 2852
    let result = try resolveProgramStr(&mut a, program);
2853 2853
    try expectNoErrors(&result);
2854 2854
2855 2855
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2856 2856
    let stmt = try getBlockStmt(result.root, 1);
2857 2857
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2858 2858
}
2859 2859
2860 -
@test fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) {
2860 +
@test unsafe fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) {
2861 2861
    let mut a = testResolver();
2862 2862
    let program = "union R { Ok(i32), Error(bool) } R::Ok(true);";
2863 2863
    let result = try resolveProgramStr(&mut a, program);
2864 2864
    let err = try expectError(&result);
2865 2865
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2872 2872
        else throw testing::TestError::Failed;
2873 2873
    let case ast::NodeValue::Bool(_) = errNode.value
2874 2874
        else throw testing::TestError::Failed;
2875 2875
}
2876 2876
2877 -
@test fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) {
2877 +
@test unsafe fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) {
2878 2878
    let mut a = testResolver();
2879 2879
    let program = "union Status { Ok, Error } Status::Ok(42);";
2880 2880
    let result = try resolveProgramStr(&mut a, program);
2881 2881
    let err = try expectError(&result);
2882 2882
2886 2886
        else throw testing::TestError::Failed;
2887 2887
    let case ast::NodeValue::Call(_) = node.value
2888 2888
        else throw testing::TestError::Failed;
2889 2889
}
2890 2890
2891 -
@test fn testResolveUnionVariantUnknown() throws (testing::TestError) {
2891 +
@test unsafe fn testResolveUnionVariantUnknown() throws (testing::TestError) {
2892 2892
    let mut a = testResolver();
2893 2893
    let program = "union Status { Ok, Error } Status::Unknown;";
2894 2894
    let result = try resolveProgramStr(&mut a, program);
2895 2895
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
2896 2896
}
2897 2897
2898 -
@test fn testResolveScopeAccessUndefinedType() throws (testing::TestError) {
2898 +
@test unsafe fn testResolveScopeAccessUndefinedType() throws (testing::TestError) {
2899 2899
    let mut a = testResolver();
2900 2900
    let result = try resolveProgramStr(&mut a, "Unknown::X;");
2901 2901
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
2902 2902
}
2903 2903
2904 -
@test fn testResolveUnionVariantVoidPayload() throws (testing::TestError) {
2904 +
@test unsafe fn testResolveUnionVariantVoidPayload() throws (testing::TestError) {
2905 2905
    let mut a = testResolver();
2906 2906
    let program = "union R { Success(i32), Pending } R::Pending;";
2907 2907
    let result = try resolveProgramStr(&mut a, program);
2908 2908
    try expectNoErrors(&result);
2909 2909
2913 2913
2914 2914
    let stmt = try getBlockStmt(result.root, 1);
2915 2915
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2916 2916
}
2917 2917
2918 -
@test fn testResolveUnionVariantRecordPayload() throws (testing::TestError) {
2918 +
@test unsafe fn testResolveUnionVariantRecordPayload() throws (testing::TestError) {
2919 2919
    let mut a = testResolver();
2920 2920
    let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });";
2921 2921
    let result = try resolveProgramStr(&mut a, program);
2922 2922
    try expectNoErrors(&result);
2923 2923
2924 2924
    let ty = try getTypeInScopeOf(&a, result.root, "S");
2925 2925
    let stmt = try getBlockStmt(result.root, 2);
2926 2926
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2927 2927
}
2928 2928
2929 -
@test fn testResolveBuiltinSizeOf() throws (testing::TestError) {
2929 +
@test unsafe fn testResolveBuiltinSizeOf() throws (testing::TestError) {
2930 2930
    try resolveAndExpectConstExpr("@sizeOf(u8)", 1);
2931 2931
    try resolveAndExpectConstExpr("@sizeOf(u16)", 2);
2932 2932
    try resolveAndExpectConstExpr("@sizeOf(u32)", 4);
2933 2933
    try resolveAndExpectConstExpr("@sizeOf(i32)", 4);
2934 2934
    try resolveAndExpectConstExpr("@sizeOf(bool)", 1);
2951 2951
    try resolveAndExpectConstStmt("union T { A, B(u16), C }; @sizeOf(T);", 4);
2952 2952
    try resolveAndExpectConstStmt("union T { A(u32), B(u16), C(u16) }; @sizeOf(T);", 8);
2953 2953
    try resolveAndExpectConstStmt("union T { A(u32), B(u16), C([u8; 16]) }; @sizeOf(T);", 20);
2954 2954
}
2955 2955
2956 -
@test fn testResolveBuiltinAlignOf() throws (testing::TestError) {
2956 +
@test unsafe fn testResolveBuiltinAlignOf() throws (testing::TestError) {
2957 2957
    try resolveAndExpectConstExpr("@alignOf(u8)", 1);
2958 2958
    try resolveAndExpectConstExpr("@alignOf(u16)", 2);
2959 2959
    try resolveAndExpectConstExpr("@alignOf(u32)", 4);
2960 2960
    try resolveAndExpectConstExpr("@alignOf(i32)", 4);
2961 2961
    try resolveAndExpectConstExpr("@alignOf(bool)", 1);
2975 2975
    try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @alignOf(T);", 4);
2976 2976
    try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1);
2977 2977
    try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4);
2978 2978
}
2979 2979
2980 -
@test fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) {
2980 +
@test unsafe fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) {
2981 2981
    let mut a = testResolver();
2982 2982
    let program = "record T { x: u8, y: u32 } @sizeOf(T);";
2983 2983
    let result = try resolveProgramStr(&mut a, program);
2984 2984
    try expectNoErrors(&result);
2985 2985
2986 2986
    let stmt = try getBlockStmt(result.root, 1);
2987 2987
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
2988 2988
    try expectConstInt(&a, expr, 8);
2989 2989
}
2990 2990
2991 -
@test fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) {
2991 +
@test unsafe fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) {
2992 2992
    let mut a = testResolver();
2993 2993
    let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);";
2994 2994
    let result = try resolveProgramStr(&mut a, program);
2995 2995
    try expectNoErrors(&result);
2996 2996
2997 2997
    let stmt = try getBlockStmt(result.root, 1);
2998 2998
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
2999 2999
    try expectConstInt(&a, expr, 8);
3000 3000
}
3001 3001
3002 -
@test fn testResolveAlignAnnotation() throws (testing::TestError) {
3002 +
@test unsafe fn testResolveAlignAnnotation() throws (testing::TestError) {
3003 3003
    {
3004 3004
        let mut a = testResolver();
3005 3005
        let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;");
3006 3006
        try expectNoErrors(&result);
3007 3007
3027 3027
            else throw testing::TestError::Failed;
3028 3028
        try testing::expect(val == 7);
3029 3029
    }
3030 3030
}
3031 3031
3032 -
@test fn testResolveVoidAssignmentError() throws (testing::TestError) {
3032 +
@test unsafe fn testResolveVoidAssignmentError() throws (testing::TestError) {
3033 3033
    {
3034 3034
        let mut a = testResolver();
3035 3035
        let program = "fn voidFn() {} let _ = voidFn();";
3036 3036
        let result = try resolveProgramStr(&mut a, program);
3037 3037
        try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3045 3045
3046 3046
//
3047 3047
// Module Declaration Tests
3048 3048
//
3049 3049
3050 -
@test fn testResolveEmptyMod() throws (testing::TestError) {
3050 +
@test unsafe fn testResolveEmptyMod() throws (testing::TestError) {
3051 3051
    let mut a = testResolver();
3052 3052
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3053 3053
    let mut graph = &mut MODULE_GRAPH;
3054 3054
3055 3055
    let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena);
3056 3056
    let childId = try registerModule(graph, rootId, "child", "{}", &mut arena);
3057 3057
    let result = try resolveModuleTree(&mut a, rootId);
3058 3058
    try expectNoErrors(&result);
3059 3059
}
3060 3060
3061 -
@test fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) {
3061 +
@test unsafe fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) {
3062 3062
    let mut a = testResolver();
3063 3063
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3064 3064
3065 3065
    // Register root and util modules.
3066 3066
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena);
3072 3072
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3073 3073
        else throw testing::TestError::Failed;
3074 3074
    try testing::expect(mem::eq(name, "helper"));
3075 3075
}
3076 3076
3077 -
@test fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) {
3077 +
@test unsafe fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) {
3078 3078
    let mut a = testResolver();
3079 3079
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3080 3080
3081 3081
    // Register root and util modules.
3082 3082
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena);
3085 3085
    // Resolve should succeed: parent can access child.
3086 3086
    let result = try resolveModuleTree(&mut a, rootId);
3087 3087
    try expectNoErrors(&result);
3088 3088
}
3089 3089
3090 -
@test fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) {
3090 +
@test unsafe fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) {
3091 3091
    let mut a = testResolver();
3092 3092
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3093 3093
3094 3094
    // Register root with two sibling modules.
3095 3095
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3102 3102
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3103 3103
        else throw testing::TestError::Failed;
3104 3104
    try testing::expect(mem::eq(name, "patrick"));
3105 3105
}
3106 3106
3107 -
@test fn testResolveSiblingModulesViaRoot() throws (testing::TestError) {
3107 +
@test unsafe fn testResolveSiblingModulesViaRoot() throws (testing::TestError) {
3108 3108
    let mut a = testResolver();
3109 3109
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3110 3110
3111 3111
    // Register root with two sibling modules.
3112 3112
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3116 3116
    // Resolve should succeed: siblings can access each other via root.
3117 3117
    let result = try resolveModuleTree(&mut a, rootId);
3118 3118
    try expectNoErrors(&result);
3119 3119
}
3120 3120
3121 -
@test fn testResolveModuleMutualRecursion() throws (testing::TestError) {
3121 +
@test unsafe fn testResolveModuleMutualRecursion() throws (testing::TestError) {
3122 3122
    let mut a = testResolver();
3123 3123
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3124 3124
3125 3125
    // Register root with two sibling modules that call each other.
3126 3126
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena);
3130 3130
    // Resolve should succeed: cyclic use is allowed.
3131 3131
    let result = try resolveModuleTree(&mut a, rootId);
3132 3132
    try expectNoErrors(&result);
3133 3133
}
3134 3134
3135 -
@test fn testResolveAccessModuleType() throws (testing::TestError) {
3135 +
@test unsafe fn testResolveAccessModuleType() throws (testing::TestError) {
3136 3136
    let mut a = testResolver();
3137 3137
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3138 3138
3139 3139
    // Register root with types module containing a record.
3140 3140
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3144 3144
    // Resolve should succeed: types can be accessed.
3145 3145
    let result = try resolveModuleTree(&mut a, rootId);
3146 3146
    try expectNoErrors(&result);
3147 3147
}
3148 3148
3149 -
@test fn testResolveAccessModuleConstant() throws (testing::TestError) {
3149 +
@test unsafe fn testResolveAccessModuleConstant() throws (testing::TestError) {
3150 3150
    let mut a = testResolver();
3151 3151
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3152 3152
3153 3153
    // Register root with constants module.
3154 3154
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3158 3158
    // Resolve should succeed: constants can be accessed.
3159 3159
    let result = try resolveModuleTree(&mut a, rootId);
3160 3160
    try expectNoErrors(&result);
3161 3161
}
3162 3162
3163 -
@test fn testResolveRootSymbolMustBeImported() throws (testing::TestError) {
3163 +
@test unsafe fn testResolveRootSymbolMustBeImported() throws (testing::TestError) {
3164 3164
    let mut a = testResolver();
3165 3165
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3166 3166
3167 3167
    // Register deeply nested modules: `root::app::services::auth`.
3168 3168
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena);
3171 3171
    // Resolve should fail: the `root` module must be imported.
3172 3172
    let result = try resolveModuleTree(&mut a, rootId);
3173 3173
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root"));
3174 3174
}
3175 3175
3176 -
@test fn testResolveUseImportsNestedSymbol() throws (testing::TestError) {
3176 +
@test unsafe fn testResolveUseImportsNestedSymbol() throws (testing::TestError) {
3177 3177
    let mut a = testResolver();
3178 3178
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3179 3179
3180 3180
    // Register deeply nested modules: `root::app::services::auth`.
3181 3181
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena);
3188 3188
    // Resolve should succeed: use imports the module symbol.
3189 3189
    let result = try resolveModuleTree(&mut a, rootId);
3190 3190
    try expectNoErrors(&result);
3191 3191
}
3192 3192
3193 -
@test fn testResolveUseNonExistentModule() throws (testing::TestError) {
3193 +
@test unsafe fn testResolveUseNonExistentModule() throws (testing::TestError) {
3194 3194
    let mut a = testResolver();
3195 3195
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3196 3196
3197 3197
    // Register root with app trying to use a non-existent module.
3198 3198
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena);
3201 3201
    // Resolve should fail: module doesn't exist.
3202 3202
    let result = try resolveModuleTree(&mut a, rootId);
3203 3203
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown"));
3204 3204
}
3205 3205
3206 -
@test fn testResolveUsePrivateFn() throws (testing::TestError) {
3206 +
@test unsafe fn testResolveUsePrivateFn() throws (testing::TestError) {
3207 3207
    let mut a = testResolver();
3208 3208
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3209 3209
3210 3210
    // Register root with util module containing a private function.
3211 3211
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena);
3215 3215
    // Resolve should fail: function is not public.
3216 3216
    let result = try resolveModuleTree(&mut a, rootId);
3217 3217
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3218 3218
}
3219 3219
3220 -
@test fn testResolveUsePrivateMod() throws (testing::TestError) {
3220 +
@test unsafe fn testResolveUsePrivateMod() throws (testing::TestError) {
3221 3221
    let mut a = testResolver();
3222 3222
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3223 3223
3224 3224
    // Register root with public and private child modules.
3225 3225
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena);
3229 3229
    // Resolve should fail: module is not public.
3230 3230
    let result = try resolveModuleTree(&mut a, rootId);
3231 3231
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3232 3232
}
3233 3233
3234 -
@test fn testResolveUsePublicMod() throws (testing::TestError) {
3234 +
@test unsafe fn testResolveUsePublicMod() throws (testing::TestError) {
3235 3235
    let mut a = testResolver();
3236 3236
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3237 3237
3238 3238
    // Register root with public and private child modules.
3239 3239
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena);
3243 3243
    // Resolve should succeed: module is public.
3244 3244
    let result = try resolveModuleTree(&mut a, rootId);
3245 3245
    try expectNoErrors(&result);
3246 3246
}
3247 3247
3248 -
@test fn testResolveUseNonPublicType() throws (testing::TestError) {
3248 +
@test unsafe fn testResolveUseNonPublicType() throws (testing::TestError) {
3249 3249
    let mut a = testResolver();
3250 3250
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3251 3251
3252 3252
    // Register root with types module containing a private record.
3253 3253
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3257 3257
    // Resolve should fail: record is not public.
3258 3258
    let result = try resolveModuleTree(&mut a, rootId);
3259 3259
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv"));
3260 3260
}
3261 3261
3262 -
@test fn testResolveImportPublicType() throws (testing::TestError) {
3262 +
@test unsafe fn testResolveImportPublicType() throws (testing::TestError) {
3263 3263
    let mut a = testResolver();
3264 3264
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3265 3265
3266 3266
    // Register root with types module containing a public record.
3267 3267
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3271 3271
    // Resolve should succeed: record is public.
3272 3272
    let result = try resolveModuleTree(&mut a, rootId);
3273 3273
    try expectNoErrors(&result);
3274 3274
}
3275 3275
3276 -
@test fn testResolveUseNonPublicStatic() throws (testing::TestError) {
3276 +
@test unsafe fn testResolveUseNonPublicStatic() throws (testing::TestError) {
3277 3277
    let mut a = testResolver();
3278 3278
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3279 3279
3280 3280
    // Register root with statics module containing a private static.
3281 3281
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3285 3285
    // Resolve should fail: static is not public.
3286 3286
    let result = try resolveModuleTree(&mut a, rootId);
3287 3287
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE"));
3288 3288
}
3289 3289
3290 -
@test fn testResolveImportPublicStatic() throws (testing::TestError) {
3290 +
@test unsafe fn testResolveImportPublicStatic() throws (testing::TestError) {
3291 3291
    let mut a = testResolver();
3292 3292
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3293 3293
3294 3294
    // Register root with statics module containing a public static.
3295 3295
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3299 3299
    // Resolve should succeed: static is public.
3300 3300
    let result = try resolveModuleTree(&mut a, rootId);
3301 3301
    try expectNoErrors(&result);
3302 3302
}
3303 3303
3304 -
@test fn testResolveAccessSuper() throws (testing::TestError) {
3304 +
@test unsafe fn testResolveAccessSuper() throws (testing::TestError) {
3305 3305
    {
3306 3306
        let mut a = testResolver();
3307 3307
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3308 3308
3309 3309
        // Test function access.
3322 3322
        try expectNoErrors(&result);
3323 3323
    }
3324 3324
}
3325 3325
3326 3326
/// Test nested super access to union variants (e.g. `super::E::A`).
3327 -
@test fn testResolveSuperUnionVariant() throws (testing::TestError) {
3327 +
@test unsafe fn testResolveSuperUnionVariant() throws (testing::TestError) {
3328 3328
    let mut a = testResolver();
3329 3329
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3330 3330
3331 3331
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena);
3332 3332
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c",
3334 3334
        &mut arena);
3335 3335
    let result = try resolveModuleTree(&mut a, rootId);
3336 3336
    try expectNoErrors(&result);
3337 3337
}
3338 3338
3339 -
@test fn testResolveUseSuper() throws (testing::TestError) {
3339 +
@test unsafe fn testResolveUseSuper() throws (testing::TestError) {
3340 3340
    let mut a = testResolver();
3341 3341
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3342 3342
3343 3343
    // Register root with a function, and a child module that uses super to access it.
3344 3344
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena);
3348 3348
    // Resolve should succeed - super allows accessing parent module.
3349 3349
    let result = try resolveModuleTree(&mut a, rootId);
3350 3350
    try expectNoErrors(&result);
3351 3351
}
3352 3352
3353 -
@test fn testResolveModNotFound() throws (testing::TestError) {
3353 +
@test unsafe fn testResolveModNotFound() throws (testing::TestError) {
3354 3354
    let mut a = testResolver();
3355 3355
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3356 3356
3357 3357
    // Register root that declares a module that doesn't exist.
3358 3358
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena);
3363 3363
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3364 3364
        else throw testing::TestError::Failed;
3365 3365
    try testing::expect(mem::eq(name, "unknown"));
3366 3366
}
3367 3367
3368 -
@test fn testResolveDuplicateSubModule() throws (testing::TestError) {
3368 +
@test unsafe fn testResolveDuplicateSubModule() throws (testing::TestError) {
3369 3369
    let mut a = testResolver();
3370 3370
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3371 3371
3372 3372
    // Register root that declares a module twice.
3373 3373
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena);
3377 3377
    let result = try resolveModuleTree(&mut a, rootId);
3378 3378
    let err = try expectError(&result);
3379 3379
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
3380 3380
}
3381 3381
3382 -
@test fn testResolveUseSubModule() throws (testing::TestError) {
3382 +
@test unsafe fn testResolveUseSubModule() throws (testing::TestError) {
3383 3383
    let mut a = testResolver();
3384 3384
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3385 3385
3386 3386
    // Register root that declares and imports the same module.
3387 3387
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena);
3391 3391
    let result = try resolveModuleTree(&mut a, rootId);
3392 3392
    let err = try expectError(&result);
3393 3393
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
3394 3394
}
3395 3395
3396 -
@test fn testResolveDuplicateUse() throws (testing::TestError) {
3396 +
@test unsafe fn testResolveDuplicateUse() throws (testing::TestError) {
3397 3397
    let mut a = testResolver();
3398 3398
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3399 3399
3400 3400
    // Register a module that imports the same module twice.
3401 3401
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena);
3406 3406
    let err = try expectError(&result);
3407 3407
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root"));
3408 3408
}
3409 3409
3410 3410
/// Test that opaque pointers are allowed in record fields.
3411 -
@test fn testOpaquePointerInRecordField() throws (testing::TestError) {
3411 +
@test unsafe fn testOpaquePointerInRecordField() throws (testing::TestError) {
3412 3412
    let mut a = testResolver();
3413 3413
    let result = try resolveProgramStr(&mut a, "record T { x: *opaque }");
3414 3414
    try expectNoErrors(&result);
3415 3415
}
3416 3416
3417 3417
/// You cannot use `@sizeOf` or `@alignOf` on opaque type.
3418 -
@test fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) {
3418 +
@test unsafe fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) {
3419 3419
    let mut a = testResolver();
3420 3420
3421 3421
    let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)");
3422 3422
    let err1 = try expectError(&result1);
3423 3423
    try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed);
3426 3426
    let err2 = try expectError(&result2);
3427 3427
    try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed);
3428 3428
}
3429 3429
3430 3430
/// Test that immutable slice/pointer parameters cannot be borrowed mutably.
3431 -
@test fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) {
3431 +
@test unsafe fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) {
3432 3432
    let mut a = testResolver();
3433 3433
    let program = "fn f(p: *i32) { let x = &mut *p; }";
3434 3434
    let result = try resolveProgramStr(&mut a, program);
3435 3435
    let err = try expectError(&result);
3436 3436
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3437 3437
}
3438 3438
3439 3439
/// Test that immutable slice parameters cannot be borrowed mutably.
3440 -
@test fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) {
3440 +
@test unsafe fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) {
3441 3441
    let mut a = testResolver();
3442 3442
    let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }";
3443 3443
    let result = try resolveProgramStr(&mut a, program);
3444 3444
    let err = try expectError(&result);
3445 3445
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3446 3446
}
3447 3447
3448 3448
/// Test that mutable pointer parameters can be borrowed mutably.
3449 -
@test fn testMutableBorrowFromMutablePointer() throws (testing::TestError) {
3449 +
@test unsafe fn testMutableBorrowFromMutablePointer() throws (testing::TestError) {
3450 3450
    let mut a = testResolver();
3451 3451
    let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }";
3452 3452
    let result = try resolveProgramStr(&mut a, program);
3453 3453
    try expectNoErrors(&result);
3454 3454
}
3455 3455
3456 3456
/// Test that mutable slice parameters can be borrowed mutably.
3457 -
@test fn testMutableBorrowFromMutableSlice() throws (testing::TestError) {
3457 +
@test unsafe fn testMutableBorrowFromMutableSlice() throws (testing::TestError) {
3458 3458
    let mut a = testResolver();
3459 3459
    let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }";
3460 3460
    let result = try resolveProgramStr(&mut a, program);
3461 3461
    try expectNoErrors(&result);
3462 3462
}
3463 3463
3464 3464
/// Test borrowing mutably from a field access on a call returning `*mut`.
3465 -
@test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) {
3465 +
@test unsafe fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) {
3466 3466
    let mut a = testResolver();
3467 -
    let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }";
3467 +
    let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }";
3468 3468
    let result = try resolveProgramStr(&mut a, program);
3469 3469
    try expectNoErrors(&result);
3470 3470
}
3471 3471
3472 3472
/// Test that calls returning immutable pointers cannot be mutably borrowed.
3473 -
@test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) {
3473 +
@test unsafe fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) {
3474 3474
    let mut a = testResolver();
3475 -
    let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { let b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }";
3475 +
    let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }";
3476 3476
    let result = try resolveProgramStr(&mut a, program);
3477 3477
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3478 3478
}
3479 3479
3480 3480
/// Test borrowing mutably from a public static through scope access.
3481 -
@test fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) {
3481 +
@test unsafe fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) {
3482 3482
    let mut a = testResolver();
3483 3483
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3484 3484
3485 3485
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3486 3486
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena);
3489 3489
    let result = try resolveModuleTree(&mut a, rootId);
3490 3490
    try expectNoErrors(&result);
3491 3491
}
3492 3492
3493 3493
/// Test that constants through scope access cannot be mutably borrowed.
3494 -
@test fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) {
3494 +
@test unsafe fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) {
3495 3495
    let mut a = testResolver();
3496 3496
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3497 3497
3498 3498
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3499 3499
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena);
3502 3502
    let result = try resolveModuleTree(&mut a, rootId);
3503 3503
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3504 3504
}
3505 3505
3506 3506
/// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer.
3507 -
@test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) {
3507 +
@test unsafe fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) {
3508 3508
    let mut a = testResolver();
3509 -
    let program = "fn f() { let mut x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }";
3509 +
    let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }";
3510 3510
    let result = try resolveProgramStr(&mut a, program);
3511 3511
    let err = try expectError(&result);
3512 3512
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3513 3513
}
3514 3514
3515 3515
/// Test that mutable pointer to immutable slice cannot be assigned through index.
3516 3516
/// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but
3517 3517
/// the inner slice is immutable, so we shouldn't be able to mutate the elements.
3518 -
@test fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) {
3518 +
@test unsafe fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) {
3519 3519
    let mut a = testResolver();
3520 3520
    let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }";
3521 3521
    let result = try resolveProgramStr(&mut a, program);
3522 3522
3523 3523
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3524 3524
}
3525 3525
3526 3526
/// Test that mutable slice parameters can be assigned through index.
3527 -
@test fn testAssignThroughMutableSliceParam() throws (testing::TestError) {
3527 +
@test unsafe fn testAssignThroughMutableSliceParam() throws (testing::TestError) {
3528 3528
    {
3529 3529
        // Mutable slice param: direct assignment should work
3530 3530
        let mut a = testResolver();
3531 3531
        let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }";
3532 3532
        let result = try resolveProgramStr(&mut a, program);
3539 3539
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3540 3540
    }
3541 3541
}
3542 3542
3543 3543
/// Test range end type coercion with assignable types.
3544 -
@test fn testRangeEndTypeCoercion() throws (testing::TestError) {
3544 +
@test unsafe fn testRangeEndTypeCoercion() throws (testing::TestError) {
3545 3545
    {
3546 3546
        let mut a = testResolver();
3547 3547
        let program = "fn f(end: u32) { for i in 0..end {} }";
3548 3548
        let result = try resolveProgramStr(&mut a, program);
3549 3549
        try expectNoErrors(&result);
3554 3554
        try expectNoErrors(&result);
3555 3555
    }
3556 3556
}
3557 3557
3558 3558
/// Mixed-width range bounds require an explicit cast.
3559 -
@test fn testRangeEndTypeSubType() throws (testing::TestError) {
3559 +
@test unsafe fn testRangeEndTypeSubType() throws (testing::TestError) {
3560 3560
    {
3561 3561
        let mut a = testResolver();
3562 3562
        let program = "fn f(start: i8, end: u32) { for i in start..end {} }";
3563 3563
        let result = try resolveProgramStr(&mut a, program);
3564 3564
        let err = try expectError(&result);
3570 3570
        try expectNoErrors(&result);
3571 3571
    }
3572 3572
}
3573 3573
3574 3574
/// Test that try-catch expressions in statement context accept mismatched types.
3575 -
@test fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) {
3575 +
@test unsafe fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) {
3576 3576
    let mut a = testResolver();
3577 3577
    let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3578 3578
    let result = try resolveProgramStr(&mut a, program);
3579 3579
    try expectNoErrors(&result);
3580 3580
}
3581 3581
3582 3582
/// Test that try-catch blocks in value context require divergence or void.
3583 -
@test fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) {
3583 +
@test unsafe fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) {
3584 3584
    let mut a = testResolver();
3585 3585
    let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3586 3586
    let result = try resolveProgramStr(&mut a, program);
3587 3587
    let err = try expectError(&result);
3588 3588
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Void);
3589 3589
}
3590 3590
3591 3591
/// Test that try-catch blocks in value context work when they diverge.
3592 -
@test fn testTryCatchInValueContextDiverges() throws (testing::TestError) {
3592 +
@test unsafe fn testTryCatchInValueContextDiverges() throws (testing::TestError) {
3593 3593
    let mut a = testResolver();
3594 3594
    let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }";
3595 3595
    let result = try resolveProgramStr(&mut a, program);
3596 3596
    try expectNoErrors(&result);
3597 3597
}
3598 3598
3599 3599
/// Test that `try?` lifts result type to optional.
3600 -
@test fn testTryOptionalLiftsToOptional() throws (testing::TestError) {
3600 +
@test unsafe fn testTryOptionalLiftsToOptional() throws (testing::TestError) {
3601 3601
    let mut a = testResolver();
3602 3602
    let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }";
3603 3603
    let result = try resolveProgramStr(&mut a, program);
3604 3604
    try expectNoErrors(&result);
3605 3605
}
3606 3606
3607 3607
/// Test that record fields can be assigned if the record binding is mutable.
3608 -
@test fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) {
3608 +
@test unsafe fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) {
3609 3609
    let mut a = testResolver();
3610 3610
    let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }";
3611 3611
    let result = try resolveProgramStr(&mut a, program);
3612 3612
    try expectNoErrors(&result);
3613 3613
}
3614 3614
3615 3615
/// Test that record fields cannot be assigned if the record binding is immutable.
3616 -
@test fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) {
3616 +
@test unsafe fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) {
3617 3617
    let mut a = testResolver();
3618 3618
    let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }";
3619 3619
    let result = try resolveProgramStr(&mut a, program);
3620 3620
    let err = try expectError(&result);
3621 3621
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3622 3622
}
3623 3623
3624 3624
/// Test that record fields can be assigned through a mutable pointer.
3625 -
@test fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) {
3625 +
@test unsafe fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) {
3626 3626
    let mut a = testResolver();
3627 3627
    let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }";
3628 3628
    let result = try resolveProgramStr(&mut a, program);
3629 3629
    try expectNoErrors(&result);
3630 3630
}
3631 3631
3632 3632
/// Test that record fields cannot be assigned through an immutable pointer.
3633 -
@test fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) {
3633 +
@test unsafe fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) {
3634 3634
    let mut a = testResolver();
3635 3635
    let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }";
3636 3636
    let result = try resolveProgramStr(&mut a, program);
3637 3637
    let err = try expectError(&result);
3638 3638
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3639 3639
}
3640 3640
3641 3641
// Opaque pointer tests.
3642 3642
3643 3643
/// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast.
3644 -
@test fn testOpaquePointerAutoCoercion() throws (testing::TestError) {
3644 +
@test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) {
3645 3645
    let mut a = testResolver();
3646 -
    let result = try resolveProgramStr(&mut a, "fn f(x: i32) { let mut ptr: *i32 = &x; let o: *opaque = ptr; set ptr = o as *i32; }");
3646 +
    let result = try resolveProgramStr(&mut a, "fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }");
3647 3647
    try expectNoErrors(&result);
3648 3648
}
3649 3649
3650 3650
/// You cannot assign an opaque pointer to a non-opaque pointer without a cast.
3651 -
@test fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) {
3651 +
@test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) {
3652 3652
    let mut a = testResolver();
3653 -
    let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let ptr: *i32 = o; }");
3653 +
    let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }");
3654 3654
    let err = try expectError(&result);
3655 3655
    let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
3656 3656
        else throw testing::TestError::Failed;
3657 3657
    let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected
3658 3658
        else throw testing::TestError::Failed;
3662 3662
    try testing::expect(*expectedTarget == super::Type::I32);
3663 3663
    try testing::expect(*actualTarget == super::Type::Opaque);
3664 3664
}
3665 3665
3666 3666
/// You cannot have a value of type `opaque` (function parameter).
3667 -
@test fn testOpaqueValue() throws (testing::TestError) {
3667 +
@test unsafe fn testOpaqueValue() throws (testing::TestError) {
3668 3668
    {
3669 3669
        let mut a = testResolver();
3670 3670
        let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}");
3671 3671
        let err = try expectError(&result);
3672 3672
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3682 3682
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3683 3683
    }
3684 3684
}
3685 3685
3686 3686
/// You cannot dereference an opaque pointer, you have to cast it first.
3687 -
@test fn testOpaquePointerNoDereference() throws (testing::TestError) {
3687 +
@test unsafe fn testOpaquePointerNoDereference() throws (testing::TestError) {
3688 3688
    let mut a = testResolver();
3689 -
    let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = *o; }");
3689 +
    let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }");
3690 3690
    let err = try expectError(&result);
3691 3691
    try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref);
3692 3692
}
3693 3693
3694 3694
/// Test that you can dereference after casting.
3695 -
@test fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) {
3695 +
@test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) {
3696 3696
    let mut a = testResolver();
3697 3697
    let result = try resolveProgramStr(&mut a, "fn f() { let o: *opaque = undefined; let x = *(o as *i32); }");
3698 3698
    try expectNoErrors(&result);
3699 3699
}
3700 3700
3701 3701
/// You cannot do pointer arithmetic with an opaque pointer.
3702 -
@test fn testOpaquePointerNoArithmetic() throws (testing::TestError) {
3702 +
@test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) {
3703 3703
    {
3704 3704
        let mut a = testResolver();
3705 -
        let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o + 1; }");
3705 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }");
3706 3706
        let err = try expectError(&result);
3707 3707
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3708 3708
    } {
3709 3709
        let mut a = testResolver();
3710 -
        let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 + o; }");
3710 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }");
3711 3711
        let err = try expectError(&result);
3712 3712
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3713 3713
    } {
3714 3714
        let mut a = testResolver();
3715 -
        let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o - 1; }");
3715 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }");
3716 3716
        let err = try expectError(&result);
3717 3717
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3718 3718
    } {
3719 3719
        let mut a = testResolver();
3720 -
        let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 - o; }");
3720 +
        let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }");
3721 3721
        let err = try expectError(&result);
3722 3722
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3723 3723
    }
3724 3724
}
3725 3725
3726 3726
// Wildcard import/reexport tests.
3727 3727
3728 3728
/// Test transitive re-export.
3729 -
@test fn testWildcardReexportTransitive() throws (testing::TestError) {
3729 +
@test unsafe fn testWildcardReexportTransitive() throws (testing::TestError) {
3730 3730
    let mut a = testResolver();
3731 3731
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3732 3732
3733 3733
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena);
3734 3734
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena);
3739 3739
    let result = try resolveModuleTree(&mut a, rootId);
3740 3740
    try expectNoErrors(&result);
3741 3741
}
3742 3742
3743 3743
/// Test that wildcard import can access public symbols.
3744 -
@test fn testWildcardImportPublicOnly() throws (testing::TestError) {
3744 +
@test unsafe fn testWildcardImportPublicOnly() throws (testing::TestError) {
3745 3745
    let mut a = testResolver();
3746 3746
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3747 3747
3748 3748
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3749 3749
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
3752 3752
    let result = try resolveModuleTree(&mut a, rootId);
3753 3753
    try expectNoErrors(&result);
3754 3754
}
3755 3755
3756 3756
/// Test that wildcard import cannot access private symbols.
3757 -
@test fn testWildcardImportSkipsPrivate() throws (testing::TestError) {
3757 +
@test unsafe fn testWildcardImportSkipsPrivate() throws (testing::TestError) {
3758 3758
    let mut a = testResolver();
3759 3759
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3760 3760
3761 3761
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3762 3762
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
3765 3765
    let result = try resolveModuleTree(&mut a, rootId);
3766 3766
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3767 3767
}
3768 3768
3769 3769
/// Test that a constant array can use another constant as its length.
3770 -
@test fn testConstArrayWithConstLength() throws (testing::TestError) {
3770 +
@test unsafe fn testConstArrayWithConstLength() throws (testing::TestError) {
3771 3771
    let mut a = testResolver();
3772 3772
    let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];";
3773 3773
    let result = try resolveProgramStr(&mut a, program);
3774 3774
    try expectNoErrors(&result);
3775 3775
3781 3781
        else throw testing::TestError::Failed;
3782 3782
    try testing::expect(arrType.length == 3);
3783 3783
}
3784 3784
3785 3785
/// Test that a record field can use a constant as its array length.
3786 -
@test fn testRecordFieldWithConstArrayLength() throws (testing::TestError) {
3786 +
@test unsafe fn testRecordFieldWithConstArrayLength() throws (testing::TestError) {
3787 3787
    let mut a = testResolver();
3788 3788
    let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }";
3789 3789
    let result = try resolveProgramStr(&mut a, program);
3790 3790
    try expectNoErrors(&result);
3791 3791
}
3792 3792
3793 3793
/// Test that a constant can have a record literal value (lazy record body resolution).
3794 -
@test fn testConstWithRecordLiteral() throws (testing::TestError) {
3794 +
@test unsafe fn testConstWithRecordLiteral() throws (testing::TestError) {
3795 3795
    let mut a = testResolver();
3796 3796
    let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };";
3797 3797
    let result = try resolveProgramStr(&mut a, program);
3798 3798
    try expectNoErrors(&result);
3799 3799
}
3800 3800
3801 3801
/// Test that a constant can have a union variant value (lazy union body resolution).
3802 -
@test fn testConstWithUnionVariant() throws (testing::TestError) {
3802 +
@test unsafe fn testConstWithUnionVariant() throws (testing::TestError) {
3803 3803
    let mut a = testResolver();
3804 3804
    let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;";
3805 3805
    let result = try resolveProgramStr(&mut a, program);
3806 3806
    try expectNoErrors(&result);
3807 3807
}
3808 3808
3809 3809
/// Test that record field types can reference imported types.
3810 3810
///
3811 3811
/// This tests that `use` statements are processed before record body resolution,
3812 3812
/// allowing record fields to use types from imported modules.
3813 -
@test fn testRecordFieldUsesImportedType() throws (testing::TestError) {
3813 +
@test unsafe fn testRecordFieldUsesImportedType() throws (testing::TestError) {
3814 3814
    let mut a = testResolver();
3815 3815
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3816 3816
3817 3817
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena);
3818 3818
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena);
3824 3824
3825 3825
/// Test that imported constants can be used in array size expressions.
3826 3826
///
3827 3827
/// This tests that constant values are propagated through scope access expressions,
3828 3828
/// enabling compile-time evaluation of array sizes using imported constants.
3829 -
@test fn testImportedConstantInArraySize() throws (testing::TestError) {
3829 +
@test unsafe fn testImportedConstantInArraySize() throws (testing::TestError) {
3830 3830
    let mut a = testResolver();
3831 3831
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3832 3832
3833 3833
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3834 3834
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena);
3840 3840
3841 3841
/// Test that `if let case` binds payload variables in the then branch.
3842 3842
///
3843 3843
/// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should
3844 3844
/// be bound to the payload value within the then branch scope.
3845 -
@test fn testResolveIfCaseBindsPayload() throws (testing::TestError) {
3845 +
@test unsafe fn testResolveIfCaseBindsPayload() throws (testing::TestError) {
3846 3846
    let mut a = testResolver();
3847 3847
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }";
3848 3848
    let result = try resolveProgramStr(&mut a, program);
3849 3849
    try expectNoErrors(&result);
3850 3850
}
3851 3851
3852 3852
/// Test that `if let case` payload binding is scoped to the then branch.
3853 3853
///
3854 3854
/// The payload variable should not be accessible outside the then branch.
3855 -
@test fn testResolveIfCasePayloadScopeError() throws (testing::TestError) {
3855 +
@test unsafe fn testResolveIfCasePayloadScopeError() throws (testing::TestError) {
3856 3856
    let mut a = testResolver();
3857 3857
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }";
3858 3858
    let result = try resolveProgramStr(&mut a, program);
3859 3859
    let err = try expectError(&result);
3860 3860
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3864 3864
3865 3865
/// Test that `let case` binds payload variables in the current scope.
3866 3866
///
3867 3867
/// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x`
3868 3868
/// should be bound in the scope after the statement.
3869 -
@test fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) {
3869 +
@test unsafe fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) {
3870 3870
    let mut a = testResolver();
3871 3871
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }";
3872 3872
    let result = try resolveProgramStr(&mut a, program);
3873 3873
    try expectNoErrors(&result);
3874 3874
}
3875 3875
3876 3876
/// Test that function pointers with identical signatures are assignable.
3877 3877
///
3878 3878
/// Two function types with the same parameters, return type, and throw list
3879 3879
/// should be considered structurally equal, even if they are separate allocations.
3880 -
@test fn testFnPointerAssignability() throws (testing::TestError) {
3880 +
@test unsafe fn testFnPointerAssignability() throws (testing::TestError) {
3881 3881
    let mut a = testResolver();
3882 3882
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);";
3883 3883
    let result = try resolveProgramStr(&mut a, program);
3884 3884
    try expectNoErrors(&result);
3885 3885
}
3886 3886
3887 3887
/// Test that function pointers with different parameter types are not assignable.
3888 -
@test fn testFnPointerParamMismatch() throws (testing::TestError) {
3888 +
@test unsafe fn testFnPointerParamMismatch() throws (testing::TestError) {
3889 3889
    let mut a = testResolver();
3890 3890
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);";
3891 3891
    let result = try resolveProgramStr(&mut a, program);
3892 3892
    let err = try expectError(&result);
3893 3893
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3894 3894
        else throw testing::TestError::Failed;
3895 3895
}
3896 3896
3897 3897
/// Test that function pointers with different return types are not assignable.
3898 -
@test fn testFnPointerReturnMismatch() throws (testing::TestError) {
3898 +
@test unsafe fn testFnPointerReturnMismatch() throws (testing::TestError) {
3899 3899
    let mut a = testResolver();
3900 3900
    let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);";
3901 3901
    let result = try resolveProgramStr(&mut a, program);
3902 3902
    let err = try expectError(&result);
3903 3903
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3906 3906
3907 3907
/// Test that named records use nominal typing, not structural.
3908 3908
///
3909 3909
/// Two different named record types with identical fields should NOT be
3910 3910
/// assignable to each other, because they are distinct nominal types.
3911 -
@test fn testNamedRecordNominalTyping() throws (testing::TestError) {
3911 +
@test unsafe fn testNamedRecordNominalTyping() throws (testing::TestError) {
3912 3912
    let mut a = testResolver();
3913 3913
    let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);";
3914 3914
    let result = try resolveProgramStr(&mut a, program);
3915 3915
    let err = try expectError(&result);
3916 3916
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3917 3917
        else throw testing::TestError::Failed;
3918 3918
}
3919 3919
3920 3920
/// Test that union variants with labeled record payloads can be constructed.
3921 -
@test fn testUnionVariantAnonRecordPayload() throws (testing::TestError) {
3921 +
@test unsafe fn testUnionVariantAnonRecordPayload() throws (testing::TestError) {
3922 3922
    let mut a = testResolver();
3923 3923
    let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };";
3924 3924
    let result = try resolveProgramStr(&mut a, program);
3925 3925
    try expectNoErrors(&result);
3926 3926
}
3927 3927
3928 3928
/// Test that unlabeled record literals with positional fields work correctly.
3929 3929
///
3930 3930
/// When a record is declared with positional fields (e.g., `record R(i32, bool)`),
3931 3931
/// the literal must use constructor call syntax with positional arguments.
3932 -
@test fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) {
3932 +
@test unsafe fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) {
3933 3933
    let mut a = testResolver();
3934 3934
    let program = "record R(i32, bool); let r: R = R(1, true);";
3935 3935
    let result = try resolveProgramStr(&mut a, program);
3936 3936
    try expectNoErrors(&result);
3937 3937
}
3938 3938
3939 3939
/// Test that using brace syntax for an unlabeled record causes an error.
3940 -
@test fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) {
3940 +
@test unsafe fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) {
3941 3941
    let mut a = testResolver();
3942 3942
    let program = "record R(i32); let r = R { x: 1 };";
3943 3943
    let result = try resolveProgramStr(&mut a, program);
3944 3944
    try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch);
3945 3945
}
3946 3946
3947 3947
/// Test that providing too many fields for an unlabeled record causes count mismatch.
3948 -
@test fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) {
3948 +
@test unsafe fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) {
3949 3949
    let mut a = testResolver();
3950 3950
    let program = "record R(i32, bool); let r = R(1, true, 3);";
3951 3951
    let result = try resolveProgramStr(&mut a, program);
3952 3952
    let err = try expectError(&result);
3953 3953
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
3954 3954
        else throw testing::TestError::Failed;
3955 3955
}
3956 3956
3957 3957
/// Test that match pattern with wrong number of bindings causes count mismatch.
3958 -
@test fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) {
3958 +
@test unsafe fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) {
3959 3959
    let mut a = testResolver();
3960 3960
    let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }";
3961 3961
    let result = try resolveProgramStr(&mut a, program);
3962 3962
    let err = try expectError(&result);
3963 3963
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
3964 3964
        else throw testing::TestError::Failed;
3965 3965
}
3966 3966
3967 3967
/// Test that shorthand field syntax works in record literals.
3968 3968
/// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`.
3969 -
@test fn testResolveRecordLiteralShorthand() throws (testing::TestError) {
3969 +
@test unsafe fn testResolveRecordLiteralShorthand() throws (testing::TestError) {
3970 3970
    let mut a = testResolver();
3971 3971
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }";
3972 3972
    let result = try resolveProgramStr(&mut a, program);
3973 3973
    try expectNoErrors(&result);
3974 3974
}
3975 3975
3976 3976
/// Test shorthand field syntax with mixed explicit and shorthand fields.
3977 -
@test fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) {
3977 +
@test unsafe fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) {
3978 3978
    let mut a = testResolver();
3979 3979
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }";
3980 3980
    let result = try resolveProgramStr(&mut a, program);
3981 3981
    try expectNoErrors(&result);
3982 3982
}
3983 3983
3984 3984
/// Test record-style union variant patterns with shorthand syntax.
3985 -
@test fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) {
3985 +
@test unsafe fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) {
3986 3986
    let mut a = testResolver();
3987 3987
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }";
3988 3988
    let result = try resolveProgramStr(&mut a, program);
3989 3989
    try expectNoErrors(&result);
3990 3990
}
3991 3991
3992 3992
/// Test record pattern with mixed shorthand and explicit labels.
3993 -
@test fn testResolveMatchRecordPatternMixed() throws (testing::TestError) {
3993 +
@test unsafe fn testResolveMatchRecordPatternMixed() throws (testing::TestError) {
3994 3994
    let mut a = testResolver();
3995 3995
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }";
3996 3996
    let result = try resolveProgramStr(&mut a, program);
3997 3997
    try expectNoErrors(&result);
3998 3998
}
3999 3999
4000 4000
/// Test record pattern with fields in reverse order.
4001 -
@test fn testResolveMatchRecordPatternReversed() throws (testing::TestError) {
4001 +
@test unsafe fn testResolveMatchRecordPatternReversed() throws (testing::TestError) {
4002 4002
    let mut a = testResolver();
4003 4003
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }";
4004 4004
    let result = try resolveProgramStr(&mut a, program);
4005 4005
    try expectNoErrors(&result);
4006 4006
}
4007 4007
4008 4008
/// Test record pattern with shorthand syntax in reverse order.
4009 4009
/// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order.
4010 -
@test fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) {
4010 +
@test unsafe fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) {
4011 4011
    let mut a = testResolver();
4012 4012
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }";
4013 4013
    let result = try resolveProgramStr(&mut a, program);
4014 4014
    try expectNoErrors(&result);
4015 4015
}
4016 4016
4017 4017
/// Test record pattern with `..` ignoring fields.
4018 -
@test fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) {
4018 +
@test unsafe fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) {
4019 4019
    {
4020 4020
        let mut a = testResolver();
4021 4021
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }";
4022 4022
        let result = try resolveProgramStr(&mut a, program);
4023 4023
        try expectNoErrors(&result);
4043 4043
        try expectNoErrors(&result);
4044 4044
    }
4045 4045
}
4046 4046
4047 4047
/// Test standalone record pattern matching with unlabeled patterns.
4048 -
@test fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) {
4048 +
@test unsafe fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) {
4049 4049
    let mut a = testResolver();
4050 4050
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }";
4051 4051
    let result = try resolveProgramStr(&mut a, program);
4052 4052
    try expectNoErrors(&result);
4053 4053
}
4054 4054
4055 4055
/// Test standalone record pattern matching with labeled patterns.
4056 4056
/// Pattern syntax: `T { x }` matches a named record and binds x to the field.
4057 -
@test fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) {
4057 +
@test unsafe fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) {
4058 4058
    let mut a = testResolver();
4059 4059
    let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }";
4060 4060
    let result = try resolveProgramStr(&mut a, program);
4061 4061
    try expectNoErrors(&result);
4062 4062
}
4063 4063
4064 4064
/// Test standalone record pattern with multiple fields.
4065 4065
/// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields.
4066 -
@test fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) {
4066 +
@test unsafe fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) {
4067 4067
    let mut a = testResolver();
4068 4068
    let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }";
4069 4069
    let result = try resolveProgramStr(&mut a, program);
4070 4070
    try expectNoErrors(&result);
4071 4071
}
4072 4072
4073 4073
/// Test standalone record pattern with wrong field count.
4074 4074
/// Pattern `S(x, y)` should fail for a single-field record.
4075 -
@test fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) {
4075 +
@test unsafe fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) {
4076 4076
    let mut a = testResolver();
4077 4077
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }";
4078 4078
    let result = try resolveProgramStr(&mut a, program);
4079 4079
    let err = try expectError(&result);
4080 4080
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4081 4081
        else throw testing::TestError::Failed;
4082 4082
}
4083 4083
4084 4084
/// Test array pattern matching with element bindings.
4085 4085
/// Pattern syntax: `[x, y]` matches an array and binds elements.
4086 -
@test fn testResolveMatchArrayPattern() throws (testing::TestError) {
4086 +
@test unsafe fn testResolveMatchArrayPattern() throws (testing::TestError) {
4087 4087
    let mut a = testResolver();
4088 4088
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }";
4089 4089
    let result = try resolveProgramStr(&mut a, program);
4090 4090
    try expectNoErrors(&result);
4091 4091
}
4092 4092
4093 4093
/// Test array pattern with placeholder elements.
4094 4094
/// Pattern syntax: `[_, y]` ignores first element.
4095 -
@test fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) {
4095 +
@test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) {
4096 4096
    let mut a = testResolver();
4097 4097
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }";
4098 4098
    let result = try resolveProgramStr(&mut a, program);
4099 4099
    try expectNoErrors(&result);
4100 4100
}
4101 4101
4102 4102
/// Test identifier pattern that binds the whole value.
4103 4103
/// Pattern syntax: `x` matches any value and binds it.
4104 -
@test fn testResolveMatchIdentPattern() throws (testing::TestError) {
4104 +
@test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) {
4105 4105
    let mut a = testResolver();
4106 4106
    let program = "fn f(val: i32) -> i32 { match val { x => return x } }";
4107 4107
    let result = try resolveProgramStr(&mut a, program);
4108 4108
    try expectNoErrors(&result);
4109 4109
}
4110 4110
4111 4111
/// Test numeric literal pattern matching.
4112 -
@test fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) {
4112 +
@test unsafe fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) {
4113 4113
    let mut a = testResolver();
4114 4114
    let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }";
4115 4115
    let result = try resolveProgramStr(&mut a, program);
4116 4116
    try expectNoErrors(&result);
4117 4117
}
4118 4118
4119 4119
/// Test string literal pattern matching.
4120 -
@test fn testResolveMatchStringLiteralPattern() throws (testing::TestError) {
4120 +
@test unsafe fn testResolveMatchStringLiteralPattern() throws (testing::TestError) {
4121 4121
    let mut a = testResolver();
4122 4122
    let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }";
4123 4123
    let result = try resolveProgramStr(&mut a, program);
4124 4124
    try expectNoErrors(&result);
4125 4125
}
4126 4126
4127 4127
/// Test boolean literal pattern matching.
4128 -
@test fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) {
4128 +
@test unsafe fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) {
4129 4129
    let mut a = testResolver();
4130 4130
    let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }";
4131 4131
    let result = try resolveProgramStr(&mut a, program);
4132 4132
    try expectNoErrors(&result);
4133 4133
}
4134 4134
4135 4135
/// Test @sliceOf with correct arguments succeeds.
4136 -
@test fn testResolveSliceOfCorrect() throws (testing::TestError) {
4136 +
@test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) {
4137 4137
    // Immutable pointer.
4138 4138
    {
4139 4139
        let mut a = testResolver();
4140 4140
        let program = "fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }";
4141 4141
        let result = try resolveProgramStr(&mut a, program);
4149 4149
        try expectNoErrors(&result);
4150 4150
    }
4151 4151
}
4152 4152
4153 4153
/// Test @sliceOf with wrong argument count produces an error.
4154 -
@test fn testResolveSliceOfWrongArgCount() throws (testing::TestError) {
4154 +
@test unsafe fn testResolveSliceOfWrongArgCount() throws (testing::TestError) {
4155 4155
    // No arguments.
4156 4156
    {
4157 4157
        let mut a = testResolver();
4158 4158
        let program = "fn f() -> *[u8] { return @sliceOf(); }";
4159 4159
        let result = try resolveProgramStr(&mut a, program);
4186 4186
        try testing::expect(mismatch.actual == 4);
4187 4187
    }
4188 4188
}
4189 4189
4190 4190
/// Test @sliceOf with wrong argument types produces errors.
4191 -
@test fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) {
4191 +
@test unsafe fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) {
4192 4192
    // Non-pointer first argument.
4193 4193
    {
4194 4194
        let mut a = testResolver();
4195 4195
        let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }";
4196 4196
        let result = try resolveProgramStr(&mut a, program);
4226 4226
            else throw testing::TestError::Failed;
4227 4227
    }
4228 4228
}
4229 4229
4230 4230
/// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds.
4231 -
@test fn testResolveSliceOfWithCap() throws (testing::TestError) {
4231 +
@test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) {
4232 4232
    {
4233 4233
        let mut a = testResolver();
4234 4234
        let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4235 4235
        let result = try resolveProgramStr(&mut a, program);
4236 4236
        try expectNoErrors(&result);
4243 4243
        try expectNoErrors(&result);
4244 4244
    }
4245 4245
}
4246 4246
4247 4247
/// Test @sliceOf with 3 arguments but wrong cap type.
4248 -
@test fn testResolveSliceOfCapWrongType() throws (testing::TestError) {
4248 +
@test unsafe fn testResolveSliceOfCapWrongType() throws (testing::TestError) {
4249 4249
    let mut a = testResolver();
4250 4250
    let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4251 4251
    let result = try resolveProgramStr(&mut a, program);
4252 4252
    let err = try expectError(&result);
4253 4253
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4254 4254
        else throw testing::TestError::Failed;
4255 4255
}
4256 4256
4257 4257
/// Test .cap field access on slices resolves to u32.
4258 -
@test fn testResolveSliceCapField() throws (testing::TestError) {
4258 +
@test unsafe fn testResolveSliceCapField() throws (testing::TestError) {
4259 4259
    let mut a = testResolver();
4260 4260
    let program = "fn f(s: *[u8]) -> u32 { return s.cap; }";
4261 4261
    let result = try resolveProgramStr(&mut a, program);
4262 4262
    try expectNoErrors(&result);
4263 4263
}
4264 4264
4265 4265
/// Test `.append()` on immutable slice produces an error.
4266 -
@test fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4266 +
@test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4267 4267
    let mut a = testResolver();
4268 4268
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }";
4269 4269
    let result = try resolveProgramStr(&mut a, program);
4270 4270
    let err = try expectError(&result);
4271 4271
    let case super::ErrorKind::ImmutableBinding = err.kind
4272 4272
        else throw testing::TestError::Failed;
4273 4273
}
4274 4274
4275 4275
/// Test `.append()` with wrong argument count produces an error.
4276 -
@test fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4276 +
@test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4277 4277
    // Too few arguments.
4278 4278
    {
4279 4279
        let mut a = testResolver();
4280 4280
        let program = "fn f(s: *mut [i32]) { s.append(1); }";
4281 4281
        let result = try resolveProgramStr(&mut a, program);
4297 4297
        try testing::expect(m.actual == 3);
4298 4298
    }
4299 4299
}
4300 4300
4301 4301
/// Test `.append()` with correct arguments succeeds.
4302 -
@test fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4302 +
@test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4303 4303
    let mut a = testResolver();
4304 4304
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }";
4305 4305
    let result = try resolveProgramStr(&mut a, program);
4306 4306
    try expectNoErrors(&result);
4307 4307
}
4308 4308
4309 4309
/// Test `.append()` with wrong element type produces an error.
4310 -
@test fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4310 +
@test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4311 4311
    let mut a = testResolver();
4312 4312
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }";
4313 4313
    let result = try resolveProgramStr(&mut a, program);
4314 4314
    let err = try expectError(&result);
4315 4315
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4316 4316
        else throw testing::TestError::Failed;
4317 4317
}
4318 4318
4319 4319
/// Test `.delete()` on immutable slice produces an error.
4320 -
@test fn testResolveSliceDeleteImmutable() throws (testing::TestError) {
4320 +
@test unsafe fn testResolveSliceDeleteImmutable() throws (testing::TestError) {
4321 4321
    let mut a = testResolver();
4322 4322
    let program = "fn f(s: *[i32]) { s.delete(0); }";
4323 4323
    let result = try resolveProgramStr(&mut a, program);
4324 4324
    let err = try expectError(&result);
4325 4325
    let case super::ErrorKind::ImmutableBinding = err.kind
4326 4326
        else throw testing::TestError::Failed;
4327 4327
}
4328 4328
4329 4329
/// Test `.delete()` with wrong argument count produces an error.
4330 -
@test fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) {
4330 +
@test unsafe fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) {
4331 4331
    // No arguments.
4332 4332
    {
4333 4333
        let mut a = testResolver();
4334 4334
        let program = "fn f(s: *mut [i32]) { s.delete(); }";
4335 4335
        let result = try resolveProgramStr(&mut a, program);
4351 4351
        try testing::expect(m.actual == 2);
4352 4352
    }
4353 4353
}
4354 4354
4355 4355
/// Test `.delete()` with correct arguments succeeds.
4356 -
@test fn testResolveSliceDeleteCorrect() throws (testing::TestError) {
4356 +
@test unsafe fn testResolveSliceDeleteCorrect() throws (testing::TestError) {
4357 4357
    let mut a = testResolver();
4358 4358
    let program = "fn f(s: *mut [i32]) { s.delete(0); }";
4359 4359
    let result = try resolveProgramStr(&mut a, program);
4360 4360
    try expectNoErrors(&result);
4361 4361
}
4362 4362
4363 4363
/// Test `.delete()` with wrong argument type produces an error.
4364 -
@test fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) {
4364 +
@test unsafe fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) {
4365 4365
    let mut a = testResolver();
4366 4366
    let program = "fn f(s: *mut [i32]) { s.delete(true); }";
4367 4367
    let result = try resolveProgramStr(&mut a, program);
4368 4368
    let err = try expectError(&result);
4369 4369
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4370 4370
        else throw testing::TestError::Failed;
4371 4371
}
4372 4372
4373 4373
/// Test `match &opt` produces immutable pointer bindings.
4374 -
@test fn testResolveMatchRefUnionBinding() throws (testing::TestError) {
4374 +
@test unsafe fn testResolveMatchRefUnionBinding() throws (testing::TestError) {
4375 4375
    let mut a = testResolver();
4376 4376
    let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }";
4377 4377
    let result = try resolveProgramStr(&mut a, program);
4378 4378
    try expectNoErrors(&result);
4379 4379
4394 4394
    try testing::expect(not mutable);
4395 4395
    try testing::expect(*target == super::Type::I32);
4396 4396
}
4397 4397
4398 4398
/// Test `match &mut opt` produces mutable pointer bindings.
4399 -
@test fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) {
4399 +
@test unsafe fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) {
4400 4400
    let mut a = testResolver();
4401 4401
    let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }";
4402 4402
    let result = try resolveProgramStr(&mut a, program);
4403 4403
    try expectNoErrors(&result);
4404 4404
4419 4419
    try testing::expect(mutable);
4420 4420
    try testing::expect(*target == super::Type::I32);
4421 4421
}
4422 4422
4423 4423
/// Non-constant integer widening must use an explicit cast.
4424 -
@test fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) {
4424 +
@test unsafe fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) {
4425 4425
    {
4426 4426
        let mut a = testResolver();
4427 4427
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;");
4428 4428
        let err = try expectError(&result);
4429 4429
        try expectTypeMismatch(err, super::Type::U32, super::Type::U8);
4452 4452
        try expectNoErrors(&result);
4453 4453
    }
4454 4454
}
4455 4455
4456 4456
/// Mixed-width integer binary ops require an explicit cast.
4457 -
@test fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) {
4457 +
@test unsafe fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) {
4458 4458
    {
4459 4459
        let mut a = testResolver();
4460 4460
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;");
4461 4461
        let err = try expectError(&result);
4462 4462
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
4484 4484
        try expectNoErrors(&result);
4485 4485
    }
4486 4486
}
4487 4487
4488 4488
/// A mutable slice pointer should be assignable to an immutable slice pointer.
4489 -
@test fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) {
4489 +
@test unsafe fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) {
4490 4490
    let mut a = testResolver();
4491 -
    let result = try resolveBlockStr(&mut a, "let mut arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;");
4491 +
    let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;");
4492 4492
    try expectNoErrors(&result);
4493 4493
}
4494 4494
4495 4495
/// Comprehensive tests for `as` cast expressions.
4496 -
@test fn testResolveAsCasts() throws (testing::TestError) {
4496 +
@test unsafe fn testResolveAsCasts() throws (testing::TestError) {
4497 4497
    { // Pointer to numeric.
4498 4498
        let mut a = testResolver();
4499 -
        let result = try resolveBlockStr(&mut a, "let x: i32 = 0; let p = &x; p as u32;");
4499 +
        let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;");
4500 4500
        try expectNoErrors(&result);
4501 4501
    } { // Function pointer to numeric.
4502 4502
        let mut a = testResolver();
4503 4503
        let result = try resolveBlockStr(&mut a, "let f: fn() = undefined; f as u32;");
4504 4504
        try expectNoErrors(&result);
4556 4556
        try expectNoErrors(&result);
4557 4557
    }
4558 4558
}
4559 4559
4560 4560
/// Tests for invalid `as` casts that should be rejected.
4561 -
@test fn testResolveAsCastsInvalid() throws (testing::TestError) {
4561 +
@test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) {
4562 4562
    { // Pointer to slice is invalid.
4563 4563
        let mut a = testResolver();
4564 4564
        let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *[i32];");
4565 4565
        let err = try expectError(&result);
4566 4566
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4597 4597
            else throw testing::TestError::Failed;
4598 4598
    }
4599 4599
}
4600 4600
4601 4601
/// Test that catch binding is available in catch block scope.
4602 -
@test fn testResolveTryCatchBinding() throws (testing::TestError) {
4602 +
@test unsafe fn testResolveTryCatchBinding() throws (testing::TestError) {
4603 4603
    {
4604 4604
        let mut a = testResolver();
4605 4605
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }";
4606 4606
        let result = try resolveProgramStr(&mut a, program);
4607 4607
        try expectNoErrors(&result);
4622 4622
        try expectNoErrors(&result);
4623 4623
    }
4624 4624
}
4625 4625
4626 4626
/// Test that duplicate union variant patterns are detected.
4627 -
@test fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) {
4627 +
@test unsafe fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) {
4628 4628
    {
4629 4629
        let mut a = testResolver();
4630 4630
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }";
4631 4631
        let result = try resolveProgramStr(&mut a, program);
4632 4632
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4638 4638
        try expectNoErrors(&result);
4639 4639
    }
4640 4640
}
4641 4641
4642 4642
/// Test that duplicate bool patterns are detected.
4643 -
@test fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) {
4643 +
@test unsafe fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) {
4644 4644
    {
4645 4645
        let mut a = testResolver();
4646 4646
        let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }";
4647 4647
        let result = try resolveProgramStr(&mut a, program);
4648 4648
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4653 4653
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4654 4654
    }
4655 4655
}
4656 4656
4657 4657
/// Test that duplicate nil patterns in optional match are detected.
4658 -
@test fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) {
4658 +
@test unsafe fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) {
4659 4659
    {
4660 4660
        let mut a = testResolver();
4661 4661
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }";
4662 4662
        let result = try resolveProgramStr(&mut a, program);
4663 4663
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4669 4669
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4670 4670
    }
4671 4671
}
4672 4672
4673 4673
/// Test that guarded match arms are not considered duplicates.
4674 -
@test fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) {
4674 +
@test unsafe fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) {
4675 4675
    {
4676 4676
        // Guarded union variant followed by same variant is fine.
4677 4677
        let mut a = testResolver();
4678 4678
        let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }";
4679 4679
        let result = try resolveProgramStr(&mut a, program);
4698 4698
        try expectNoErrors(&result);
4699 4699
    }
4700 4700
}
4701 4701
4702 4702
/// Test that unreachable else is detected when all union variants are covered.
4703 -
@test fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) {
4703 +
@test unsafe fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) {
4704 4704
    {
4705 4705
        let mut a = testResolver();
4706 4706
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }";
4707 4707
        let result = try resolveProgramStr(&mut a, program);
4708 4708
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4714 4714
        try expectNoErrors(&result);
4715 4715
    }
4716 4716
}
4717 4717
4718 4718
/// Test that unreachable else is detected when both bool cases are covered.
4719 -
@test fn testResolveMatchUnreachableElseBool() throws (testing::TestError) {
4719 +
@test unsafe fn testResolveMatchUnreachableElseBool() throws (testing::TestError) {
4720 4720
    {
4721 4721
        let mut a = testResolver();
4722 4722
        let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }";
4723 4723
        let result = try resolveProgramStr(&mut a, program);
4724 4724
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4730 4730
        try expectNoErrors(&result);
4731 4731
    }
4732 4732
}
4733 4733
4734 4734
/// Test that unreachable else is detected when both optional cases are covered.
4735 -
@test fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) {
4735 +
@test unsafe fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) {
4736 4736
    {
4737 4737
        let mut a = testResolver();
4738 4738
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }";
4739 4739
        let result = try resolveProgramStr(&mut a, program);
4740 4740
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4747 4747
    }
4748 4748
}
4749 4749
4750 4750
// --- Multi-error typed catch tests ---
4751 4751
4752 -
@test fn testTypedCatchExhaustive() throws (testing::TestError) {
4752 +
@test unsafe fn testTypedCatchExhaustive() throws (testing::TestError) {
4753 4753
    let mut a = testResolver();
4754 4754
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }";
4755 4755
    let result = try resolveProgramStr(&mut a, program);
4756 4756
    try expectNoErrors(&result);
4757 4757
}
4758 4758
4759 -
@test fn testTypedCatchNonExhaustive() throws (testing::TestError) {
4759 +
@test unsafe fn testTypedCatchNonExhaustive() throws (testing::TestError) {
4760 4760
    let mut a = testResolver();
4761 4761
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }";
4762 4762
    let result = try resolveProgramStr(&mut a, program);
4763 4763
    try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
4764 4764
}
4765 4765
4766 -
@test fn testTypedCatchDuplicate() throws (testing::TestError) {
4766 +
@test unsafe fn testTypedCatchDuplicate() throws (testing::TestError) {
4767 4767
    let mut a = testResolver();
4768 4768
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }";
4769 4769
    let result = try resolveProgramStr(&mut a, program);
4770 4770
    try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType);
4771 4771
}
4772 4772
4773 -
@test fn testTypedCatchWithCatchAll() throws (testing::TestError) {
4773 +
@test unsafe fn testTypedCatchWithCatchAll() throws (testing::TestError) {
4774 4774
    let mut a = testResolver();
4775 4775
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }";
4776 4776
    let result = try resolveProgramStr(&mut a, program);
4777 4777
    try expectNoErrors(&result);
4778 4778
}
4779 4779
4780 -
@test fn testTypedCatchWrongType() throws (testing::TestError) {
4780 +
@test unsafe fn testTypedCatchWrongType() throws (testing::TestError) {
4781 4781
    let mut a = testResolver();
4782 4782
    let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }";
4783 4783
    let result = try resolveProgramStr(&mut a, program);
4784 4784
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
4785 4785
}
4786 4786
4787 -
@test fn testInferredCatchMultiError() throws (testing::TestError) {
4787 +
@test unsafe fn testInferredCatchMultiError() throws (testing::TestError) {
4788 4788
    let mut a = testResolver();
4789 4789
    let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }";
4790 4790
    let result = try resolveProgramStr(&mut a, program);
4791 4791
    try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError);
4792 4792
}
4793 4793
4794 -
@test fn testResolveInstanceMissingMethod() throws (testing::TestError) {
4794 +
@test unsafe fn testResolveInstanceMissingMethod() throws (testing::TestError) {
4795 4795
    let mut a = testResolver();
4796 4796
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}";
4797 4797
    let result = try resolveProgramStr(&mut a, program);
4798 4798
    try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f"));
4799 4799
}
4800 4800
4801 -
@test fn testResolveInstanceUnknownMethod() throws (testing::TestError) {
4801 +
@test unsafe fn testResolveInstanceUnknownMethod() throws (testing::TestError) {
4802 4802
    let mut a = testResolver();
4803 4803
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }";
4804 4804
    let result = try resolveProgramStr(&mut a, program);
4805 4805
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
4806 4806
}
4807 4807
4808 -
@test fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) {
4808 +
@test unsafe fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) {
4809 4809
    let mut a = testResolver();
4810 4810
    let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }";
4811 4811
    let result = try resolveProgramStr(&mut a, program);
4812 4812
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add"));
4813 4813
}
4814 4814
4815 -
@test fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) {
4815 +
@test unsafe fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) {
4816 4816
    let mut a = testResolver();
4817 4817
    let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }";
4818 4818
    let result = try resolveProgramStr(&mut a, program);
4819 4819
    let err = try expectError(&result);
4820 4820
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4821 4821
        else throw testing::TestError::Failed;
4822 4822
}
4823 4823
4824 -
@test fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) {
4824 +
@test unsafe fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) {
4825 4825
    let mut a = testResolver();
4826 4826
    let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }";
4827 4827
    let result = try resolveProgramStr(&mut a, program);
4828 4828
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
4829 4829
}
4830 4830
4831 4831
/// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type).
4832 4832
/// The instance method could mutate through what was originally an immutable pointer.
4833 -
@test fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) {
4833 +
@test unsafe fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) {
4834 4834
    let mut a = testResolver();
4835 4835
    let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }";
4836 4836
    let result = try resolveProgramStr(&mut a, program);
4837 4837
    // Should reject: instance declares *mut receiver but trait only requires immutable.
4838 4838
    try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch);
4839 4839
}
4840 4840
4841 4841
/// Instance method declares different parameter types than the trait.
4842 4842
/// The resolver should reject the mismatch rather than silently using the trait's types.
4843 -
@test fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) {
4843 +
@test unsafe fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) {
4844 4844
    let mut a = testResolver();
4845 4845
    let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }";
4846 4846
    let result = try resolveProgramStr(&mut a, program);
4847 4847
    // Should reject: instance param type u8 doesn't match trait param type i32.
4848 4848
    let err = try expectError(&result);
4849 4849
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4850 4850
        else throw testing::TestError::Failed;
4851 4851
}
4852 4852
4853 4853
/// Duplicate instance declarations for the same (trait, type) pair should be rejected.
4854 -
@test fn testResolveInstanceDuplicateRejected() throws (testing::TestError) {
4854 +
@test unsafe fn testResolveInstanceDuplicateRejected() throws (testing::TestError) {
4855 4855
    let mut a = testResolver();
4856 4856
    let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }";
4857 4857
    let result = try resolveProgramStr(&mut a, program);
4858 4858
    // Should reject: duplicate instance for (Adder, Counter).
4859 4859
    try expectErrorKind(&result, super::ErrorKind::DuplicateInstance);
4860 4860
}
4861 4861
4862 4862
/// Trait method receiver must point to the declaring trait type.
4863 -
@test fn testResolveTraitReceiverMismatch() throws (testing::TestError) {
4863 +
@test unsafe fn testResolveTraitReceiverMismatch() throws (testing::TestError) {
4864 4864
    let mut a = testResolver();
4865 4865
    let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }";
4866 4866
    let result = try resolveProgramStr(&mut a, program);
4867 4867
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
4868 4868
}
4869 4869
4870 4870
/// Using a trait name as a value expression should be rejected.
4871 -
@test fn testResolveTraitNameAsValueRejected() throws (testing::TestError) {
4871 +
@test unsafe fn testResolveTraitNameAsValueRejected() throws (testing::TestError) {
4872 4872
    let mut a = testResolver();
4873 4873
    let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }";
4874 4874
    let result = try resolveProgramStr(&mut a, program);
4875 4875
    try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName);
4876 4876
}
4877 4877
4878 4878
/// Cross-module trait: coerce to trait object and dispatch from a different module.
4879 -
@test fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) {
4879 +
@test unsafe fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) {
4880 4880
    let mut a = testResolver();
4881 4881
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4882 4882
4883 4883
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena);
4884 4884
    let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
4885 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4885 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4886 4886
4887 4887
    let result = try resolveModuleTree(&mut a, rootId);
4888 4888
    try expectNoErrors(&result);
4889 4889
}
4890 4890
4891 4891
/// Instance in a different module from trait and type.
4892 -
@test fn testResolveInstanceCrossModule() throws (testing::TestError) {
4892 +
@test unsafe fn testResolveInstanceCrossModule() throws (testing::TestError) {
4893 4893
    let mut a = testResolver();
4894 4894
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
4895 4895
4896 4896
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena);
4897 4897
    let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena);
4898 4898
    let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena);
4899 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4899 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena);
4900 4900
4901 4901
    let result = try resolveModuleTree(&mut a, rootId);
4902 4902
    try expectNoErrors(&result);
4903 4903
}
4904 4904
4905 4905
/// Calling a mutable-receiver trait method on an immutable trait object
4906 4906
/// must be rejected.
4907 -
@test fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) {
4907 +
@test unsafe fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) {
4908 4908
    let mut a = testResolver();
4909 4909
    let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }";
4910 4910
    let result = try resolveProgramStr(&mut a, program);
4911 4911
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
4912 4912
}
4913 4913
4914 4914
/// Immutable methods on an immutable trait object should be accepted.
4915 -
@test fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) {
4915 +
@test unsafe fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) {
4916 4916
    let mut a = testResolver();
4917 4917
    let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }";
4918 4918
    let result = try resolveProgramStr(&mut a, program);
4919 4919
    try expectNoErrors(&result);
4920 4920
}
4921 4921
4922 4922
/// Both mutable and immutable methods on a mutable trait object should work.
4923 -
@test fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) {
4923 +
@test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) {
4924 4924
    let mut a = testResolver();
4925 4925
    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(); }";
4926 4926
    let result = try resolveProgramStr(&mut a, program);
4927 4927
    try expectNoErrors(&result);
4928 4928
}
4929 4929
4930 4930
/// Instance method body type must match the trait return type.
4931 4931
/// The trait declares `-> i32` but the body returns `bool`.
4932 -
@test fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) {
4932 +
@test unsafe fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) {
4933 4933
    let mut a = testResolver();
4934 4934
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }";
4935 4935
    let result = try resolveProgramStr(&mut a, program);
4936 4936
    let err = try expectError(&result);
4937 4937
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4938 4938
        else throw testing::TestError::Failed;
4939 4939
}
4940 4940
4941 4941
/// Diamond supertrait inheritance: traits B and C both extend A.
4942 4942
/// Declaring them independently should work fine.
4943 -
@test fn testResolveTraitDiamondSupertrait() throws (testing::TestError) {
4943 +
@test unsafe fn testResolveTraitDiamondSupertrait() throws (testing::TestError) {
4944 4944
    let mut a = testResolver();
4945 4945
    let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }";
4946 4946
    let result = try resolveProgramStr(&mut a, program);
4947 4947
    try expectNoErrors(&result);
4948 4948
}
4949 4949
4950 4950
/// Diamond supertrait with a combined trait that would cause duplicate
4951 4951
/// method names should be detected.
4952 -
@test fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) {
4952 +
@test unsafe fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) {
4953 4953
    let mut a = testResolver();
4954 4954
    let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }";
4955 4955
    let result = try resolveProgramStr(&mut a, program);
4956 4956
    // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`.
4957 4957
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f"));
4958 4958
}
4959 4959
4960 4960
/// Supertrait instance must exist when declaring a combined trait instance.
4961 -
@test fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) {
4961 +
@test unsafe fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) {
4962 4962
    let mut a = testResolver();
4963 4963
    let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }";
4964 4964
    let result = try resolveProgramStr(&mut a, program);
4965 4965
    try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base"));
4966 4966
}
4967 4967
4968 4968
/// Instance method omits return type when the trait declares `-> i32`.
4969 4969
/// This is rejected -- the return type must be stated explicitly.
4970 -
@test fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) {
4970 +
@test unsafe fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) {
4971 4971
    let mut a = testResolver();
4972 4972
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }";
4973 4973
    let result = try resolveProgramStr(&mut a, program);
4974 4974
    let err = try expectError(&result);
4975 4975
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4976 4976
        else throw testing::TestError::Failed;
4977 4977
}
4978 4978
4979 4979
/// Instance method declares throws but the trait method does not throw.
4980 -
@test fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) {
4980 +
@test unsafe fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) {
4981 4981
    let mut a = testResolver();
4982 4982
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }";
4983 4983
    let result = try resolveProgramStr(&mut a, program);
4984 4984
    let err = try expectError(&result);
4985 4985
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
4986 4986
        else throw testing::TestError::Failed;
4987 4987
}
4988 4988
4989 4989
/// Instance method declares a different throws type than the trait.
4990 -
@test fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) {
4990 +
@test unsafe fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) {
4991 4991
    let mut a = testResolver();
4992 4992
    let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }";
4993 4993
    let result = try resolveProgramStr(&mut a, program);
4994 4994
    let err = try expectError(&result);
4995 4995
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4996 4996
        else throw testing::TestError::Failed;
4997 4997
}
4998 4998
4999 4999
/// Instance method omits throws clause when trait declares throws.
5000 5000
/// This is rejected -- the throws clause must match exactly.
5001 -
@test fn testResolveInstanceThrowsOmitted() throws (testing::TestError) {
5001 +
@test unsafe fn testResolveInstanceThrowsOmitted() throws (testing::TestError) {
5002 5002
    let mut a = testResolver();
5003 5003
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }";
5004 5004
    let result = try resolveProgramStr(&mut a, program);
5005 5005
    let err = try expectError(&result);
5006 5006
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
5007 5007
        else throw testing::TestError::Failed;
5008 5008
}
5009 5009
5010 5010
/// Instance method correctly matches the trait's throws clause.
5011 -
@test fn testResolveInstanceThrowsMatch() throws (testing::TestError) {
5011 +
@test unsafe fn testResolveInstanceThrowsMatch() throws (testing::TestError) {
5012 5012
    let mut a = testResolver();
5013 5013
    let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }";
5014 5014
    let result = try resolveProgramStr(&mut a, program);
5015 5015
    try expectNoErrors(&result);
5016 5016
}
5017 5017
5018 5018
// Constant expression folding tests //////////////////////////////////////////
5019 5019
5020 5020
/// Resolve a program and verify that the constant at the given statement index
5021 5021
/// has the expected integer magnitude.
5022 -
fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64)
5022 +
unsafe fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64)
5023 5023
    throws (testing::TestError)
5024 5024
{
5025 5025
    let mut a = testResolver();
5026 5026
    let result = try resolveProgramStr(&mut a, program);
5027 5027
    try expectNoErrors(&result);
5036 5036
        else throw testing::TestError::Failed;
5037 5037
    try testing::expect(intVal.magnitude == expected);
5038 5038
}
5039 5039
5040 5040
/// Test arithmetic constant folding: add, sub, mul, div.
5041 -
@test fn testConstExprArithmetic() throws (testing::TestError) {
5041 +
@test unsafe fn testConstExprArithmetic() throws (testing::TestError) {
5042 5042
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30);
5043 5043
    try expectConstFold("constant A: i32 = 50; constant B: i32 = 20; constant C: i32 = A - B;", 2, 30);
5044 5044
    try expectConstFold("constant A: i32 = 6; constant B: i32 = 7; constant C: i32 = A * B;", 2, 42);
5045 5045
    try expectConstFold("constant A: i32 = 100; constant B: i32 = 5; constant C: i32 = A / B;", 2, 20);
5046 5046
}
5047 5047
5048 5048
/// Test bitwise constant folding: and, or, xor.
5049 -
@test fn testConstExprBitwise() throws (testing::TestError) {
5049 +
@test unsafe fn testConstExprBitwise() throws (testing::TestError) {
5050 5050
    try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A & B;", 2, 0x0F);
5051 5051
    try expectConstFold("constant A: i32 = 0xF0; constant B: i32 = 0x0F; constant C: i32 = A | B;", 2, 0xFF);
5052 5052
    try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A ^ B;", 2, 0xF0);
5053 5053
}
5054 5054
5055 5055
/// Test shift constant folding.
5056 -
@test fn testConstExprShift() throws (testing::TestError) {
5056 +
@test unsafe fn testConstExprShift() throws (testing::TestError) {
5057 5057
    try expectConstFold("constant A: i32 = 1; constant B: i32 = A << 4;", 1, 16);
5058 5058
    try expectConstFold("constant A: i32 = 32; constant B: i32 = A >> 2;", 1, 8);
5059 5059
}
5060 5060
5061 5061
/// Test chained constant expressions (C depends on A + B, D depends on C).
5062 -
@test fn testConstExprChained() throws (testing::TestError) {
5062 +
@test unsafe fn testConstExprChained() throws (testing::TestError) {
5063 5063
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60);
5064 5064
}
5065 5065
5066 5066
/// Test constant expression used as array size.
5067 -
@test fn testConstExprAsArraySize() throws (testing::TestError) {
5067 +
@test unsafe fn testConstExprAsArraySize() throws (testing::TestError) {
5068 5068
    let mut a = testResolver();
5069 5069
    let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];";
5070 5070
    let result = try resolveProgramStr(&mut a, program);
5071 5071
    try expectNoErrors(&result);
5072 5072
5078 5078
    try testing::expect(arrType.length == 5);
5079 5079
}
5080 5080
5081 5081
/// Test cross-module constant expression: a constant in one module references
5082 5082
/// a constant from another module via scope access.
5083 -
@test fn testCrossModuleConstExpr() throws (testing::TestError) {
5083 +
@test unsafe fn testCrossModuleConstExpr() throws (testing::TestError) {
5084 5084
    let mut a = testResolver();
5085 5085
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5086 5086
5087 5087
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5088 5088
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena);
5091 5091
    let result = try resolveModuleTree(&mut a, rootId);
5092 5092
    try expectNoErrors(&result);
5093 5093
}
5094 5094
5095 5095
/// Test cross-module constant expression used as array size.
5096 -
@test fn testCrossModuleConstExprArraySize() throws (testing::TestError) {
5096 +
@test unsafe fn testCrossModuleConstExprArraySize() throws (testing::TestError) {
5097 5097
    let mut a = testResolver();
5098 5098
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5099 5099
5100 5100
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5101 5101
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena);
5104 5104
    let result = try resolveModuleTree(&mut a, rootId);
5105 5105
    try expectNoErrors(&result);
5106 5106
}
5107 5107
5108 5108
/// Test that non-constant expressions in constant declarations are still rejected.
5109 -
@test fn testConstExprNonConstRejected() throws (testing::TestError) {
5109 +
@test unsafe fn testConstExprNonConstRejected() throws (testing::TestError) {
5110 5110
    let mut a = testResolver();
5111 5111
    let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;";
5112 5112
    let result = try resolveProgramStr(&mut a, program);
5113 5113
    let err = try expectError(&result);
5114 5114
    let case super::ErrorKind::ConstExprRequired = err.kind
5115 5115
        else throw testing::TestError::Failed;
5116 5116
}
5117 5117
5118 5118
/// Test unary negation in constant expressions.
5119 -
@test fn testConstExprUnaryNeg() throws (testing::TestError) {
5119 +
@test unsafe fn testConstExprUnaryNeg() throws (testing::TestError) {
5120 5120
    let mut a = testResolver();
5121 5121
    let program = "constant A: i32 = 10; constant B: i32 = -A;";
5122 5122
    let result = try resolveProgramStr(&mut a, program);
5123 5123
    try expectNoErrors(&result);
5124 5124
}
5125 5125
5126 5126
/// Test unary not in constant expressions.
5127 -
@test fn testConstExprUnaryNot() throws (testing::TestError) {
5127 +
@test unsafe fn testConstExprUnaryNot() throws (testing::TestError) {
5128 5128
    let mut a = testResolver();
5129 5129
    let program = "constant A: bool = true; constant B: bool = not A;";
5130 5130
    let result = try resolveProgramStr(&mut a, program);
5131 5131
    try expectNoErrors(&result);
5132 5132
}
5133 5133
5134 5134
/// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining.
5135 -
@test fn testConstExprCast() throws (testing::TestError) {
5135 +
@test unsafe fn testConstExprCast() throws (testing::TestError) {
5136 5136
    try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42);
5137 5137
    try expectConstFold("constant A: u64 = 10; constant B: u8 = A as u8;", 1, 10);
5138 5138
    try expectConstFold("constant A: i32 = 7; constant B: u32 = A as u32;", 1, 7);
5139 5139
    try expectConstFold("constant A: u32 = 100; constant B: i32 = A as i32;", 1, 100);
5140 5140
    try expectConstFold("constant A: u8 = 5; constant B: u64 = (A as u32) as u64;", 1, 5);
5145 5145
    try expectConstFold("constant A: u32 = (3 + 4) as u32 + 1;", 0, 8);
5146 5146
    try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14);
5147 5147
}
5148 5148
5149 5149
/// Test `as` cast in constant expressions used as array size.
5150 -
@test fn testConstExprCastAsArraySize() throws (testing::TestError) {
5150 +
@test unsafe fn testConstExprCastAsArraySize() throws (testing::TestError) {
5151 5151
    let mut a = testResolver();
5152 5152
    let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];";
5153 5153
    let result = try resolveProgramStr(&mut a, program);
5154 5154
    try expectNoErrors(&result);
5155 5155
5160 5160
        else throw testing::TestError::Failed;
5161 5161
    try testing::expect(arrType.length == 4);
5162 5162
}
5163 5163
5164 5164
/// Test unsuffixed integer literals in constant expressions.
5165 -
@test fn testConstExprUnsuffixedLiterals() throws (testing::TestError) {
5165 +
@test unsafe fn testConstExprUnsuffixedLiterals() throws (testing::TestError) {
5166 5166
    try expectConstFold("constant A: u32 = 4 * 4;", 0, 16);
5167 5167
    try expectConstFold("constant B: u32 = 10; constant C: u32 = B * 2;", 1, 20);
5168 5168
    try expectConstFold("constant D: u32 = 3 + 7;", 0, 10);
5169 5169
    try expectConstFold("constant E: u32 = 2 * 3 + 4;", 0, 10);
5170 5170
    try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7);
5171 5171
}
5172 5172
5173 5173
/// References cannot escape through return types.
5174 -
@test fn testRefReturnRejected() throws (testing::TestError) {
5174 +
@test unsafe fn testRefReturnRejected() throws (testing::TestError) {
5175 5175
    let mut a = testResolver();
5176 5176
    let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }";
5177 5177
    let result = try resolveProgramStr(&mut a, program);
5178 5178
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5179 5179
}
5180 5180
5181 5181
/// Case-pattern fallbacks must terminate instead of synthesizing bindings.
5182 -
@test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5182 +
@test unsafe fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5183 5183
    let mut a = testResolver();
5184 5184
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
5185 5185
    let result = try resolveProgramStr(&mut a, program);
5186 5186
    try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
5187 5187
}
5188 5188
5189 5189
/// Case bindings are unavailable on the pattern-failure path.
5190 -
@test fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5190 +
@test unsafe fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5191 5191
    let mut a = testResolver();
5192 5192
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
5193 5193
    let result = try resolveProgramStr(&mut a, program);
5194 5194
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
5195 5195
}
5196 5196
5197 5197
/// Unsafe pointer dereference requires an unsafe declaration.
5198 -
@test fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5198 +
@test unsafe fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5199 5199
    let mut a = testResolver();
5200 5200
    let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5201 5201
    let result = try resolveProgramStr(&mut a, program);
5202 5202
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5203 5203
}
5204 5204
5205 5205
/// Unsafe pointers remain freely copyable inside an unsafe declaration.
5206 -
@test fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5206 +
@test unsafe fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5207 5207
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5208 5208
    try expectAnalyzeOk(program);
5209 5209
}
5210 5210
5211 5211
/// Safe code cannot call a function that accepts unsafe operations.
5212 -
@test fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5212 +
@test unsafe fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5213 5213
    let mut a = testResolver();
5214 5214
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
5215 5215
    let result = try resolveProgramStr(&mut a, program);
5216 5216
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5217 5217
}
5218 5218
5219 5219
/// Unsafe function values retain their call-site safety requirement.
5220 -
@test fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5220 +
@test unsafe fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5221 5221
    let mut a = testResolver();
5222 5222
    let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
5223 5223
    let result = try resolveProgramStr(&mut a, program);
5224 5224
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5225 5225
}
5226 5226
5227 5227
/// References cannot be embedded in aggregate fields.
5228 -
@test fn testRefFieldRejected() throws (testing::TestError) {
5228 +
@test unsafe fn testRefFieldRejected() throws (testing::TestError) {
5229 5229
    let mut a = testResolver();
5230 5230
    let program = "record Marker: Once {} record Bad { value: &u32 }";
5231 5231
    let result = try resolveProgramStr(&mut a, program);
5232 5232
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5233 5233
}
5234 5234
5235 5235
/// Trait methods may use reference receivers.
5236 -
@test fn testTraitRefReceiver() throws (testing::TestError) {
5236 +
@test unsafe fn testTraitRefReceiver() throws (testing::TestError) {
5237 5237
    let program = "record Marker: Once {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }";
5238 5238
    try expectAnalyzeOk(program);
5239 5239
}
5240 5240
5241 5241
/// Trait implementations must preserve the receiver pointer class.
5242 -
@test fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5242 +
@test unsafe fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5243 5243
    let mut a = testResolver();
5244 5244
    let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
5245 5245
    let result = try resolveProgramStr(&mut a, program);
5246 5246
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
5247 5247
}
5248 5248
5249 5249
/// Unmarked composite values may be discarded.
5250 -
@test fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) {
5250 +
@test unsafe fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) {
5251 5251
    let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }";
5252 5252
    try expectAnalyzeOk(program);
5253 5253
}
5254 5254
5255 5255
/// A by-value use moves an unmarked composite value.
5256 -
@test fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) {
5256 +
@test unsafe fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) {
5257 5257
    let mut a = testResolver();
5258 5258
    let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }";
5259 5259
    let result = try resolveProgramStr(&mut a, program);
5260 5260
    try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value"));
5261 5261
}
5262 5262
5263 5263
/// Affine values may move on only one branch when not used later.
5264 -
@test fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) {
5264 +
@test unsafe fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) {
5265 5265
    let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }";
5266 5266
    try expectAnalyzeOk(program);
5267 5267
}
5268 5268
5269 5269
/// A `Copy` composite remains available after a by-value use.
5270 -
@test fn testCopyCompositeMayBeReused() throws (testing::TestError) {
5270 +
@test unsafe fn testCopyCompositeMayBeReused() throws (testing::TestError) {
5271 5271
    let program = "record Value: Copy { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }";
5272 5272
    try expectAnalyzeOk(program);
5273 5273
}
5274 5274
5275 5275
/// A `Copy` composite may contain only copy values.
5276 -
@test fn testCopyCompositeRejectsAffineField() throws (testing::TestError) {
5276 +
@test unsafe fn testCopyCompositeRejectsAffineField() throws (testing::TestError) {
5277 5277
    let mut a = testResolver();
5278 5278
    let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }";
5279 5279
    let result = try resolveProgramStr(&mut a, program);
5280 5280
    try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy);
5281 5281
}
5282 5282
5283 5283
/// A composite cannot carry conflicting ownership markers.
5284 -
@test fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) {
5284 +
@test unsafe fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) {
5285 5285
    let mut a = testResolver();
5286 5286
    let program = "record Value: Copy + Once { number: u32 }";
5287 5287
    let result = try resolveProgramStr(&mut a, program);
5288 5288
    try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers);
5289 5289
}
5290 5290
5291 5291
/// Linear composites still require one consuming use.
5292 -
@test fn testLinearCompositeMustBeConsumed() throws (testing::TestError) {
5292 +
@test unsafe fn testLinearCompositeMustBeConsumed() throws (testing::TestError) {
5293 5293
    let mut a = testResolver();
5294 5294
    let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }";
5295 5295
    let result = try resolveProgramStr(&mut a, program);
5296 5296
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token"));
5297 5297
}
5298 5298
5299 5299
/// The compiler-known marker cannot be derived more than once.
5300 -
@test fn testDuplicateOnceMarkerRejected() throws (testing::TestError) {
5300 +
@test unsafe fn testDuplicateOnceMarkerRejected() throws (testing::TestError) {
5301 5301
    let mut a = testResolver();
5302 5302
    let program = "record Token: Once + Once { value: u32 }";
5303 5303
    let result = try resolveProgramStr(&mut a, program);
5304 5304
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once"));
5305 5305
}
5306 5306
5307 -
/// A `Once` marker does not change legacy pointer inference.
5308 -
@test fn testOnceMarkerKeepsLegacyPointerInference() throws (testing::TestError) {
5309 -
    let program = "record Marker: Once {} fn run() { let value: u32 = 0; let pointer: *u32 = &value; pointer; }";
5310 -
    try expectAnalyzeOk(program);
5307 +
/// Stack storage cannot produce a safe stored pointer or slice.
5308 +
@test unsafe fn testStackPointerRejected() throws (testing::TestError) {
5309 +
    let programs = &[
5310 +
        "fn run() { let value: u32 = 0; let pointer: *u32 = &value; }",
5311 +
        "fn run() -> *u32 { let value: u32 = 0; return &value; }",
5312 +
        "fn run(value: u32) -> *u32 { return &value; }",
5313 +
        "record Cell { value: u32 } fn run() { let cell = Cell { value: 1 }; let pointer: *u32 = &cell.value; }",
5314 +
        "fn run() { let values = [1, 2]; let pointer: *i32 = &values[0]; }",
5315 +
        "fn run() { let values = [1, 2]; let slice: *[i32] = &values[..]; }",
5316 +
        "fn run(value: i32) { let slice: *[i32] = &[value]; }",
5317 +
        "fn run(value: &u32) -> *u32 { return value; }",
5318 +
        "fn run(value: &u32) -> *u32 { return &*value; }",
5319 +
        "fn run(values: &[u32]) -> *[u32] { return values; }",
5320 +
        "fn run(values: &[u32]) -> *[u32] { return &values[..]; }",
5321 +
        "fn run(values: &[u32]) -> *u32 { return values.ptr; }",
5322 +
        "fn run() -> *i32 { return &[1, 2][0]; }",
5323 +
        "fn run() -> *[i32] { return &[1, 2][..]; }",
5324 +
        "fn run(value: *u32) -> **u32 { return &value; }",
5325 +
        "fn run(values: *[u32]) -> *u32 { return &values.len; }",
5326 +
        "unsafe fn run(value: *unsafe u32) -> *u32 { return &*value; }",
5327 +
        "fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }",
5328 +
        "record Cell { value: u32 } fn run(value: &Cell) -> *u32 { return &value.value; }",
5329 +
        "record Cell { value: *u32 } fn run(value: &u32) -> Cell { return Cell { value }; }",
5330 +
        "fn run(value: &u32) -> *[u32] { return @sliceOf(value, 1); }",
5331 +
    ];
5332 +
    for program in programs {
5333 +
        let mut resolver = testResolver();
5334 +
        let result = try resolveProgramStr(&mut resolver, program);
5335 +
        let _ = try expectError(&result);
5336 +
    }
5337 +
}
5338 +
5339 +
/// Stack values can be borrowed for a call.
5340 +
@test unsafe fn testStackBorrowAllowed() throws (testing::TestError) {
5341 +
    try expectAnalyzeOk("fn read(value: &u32) -> u32 { return *value; } fn run() -> u32 { let value: u32 = 7; return read(&value); }");
5342 +
    try expectAnalyzeOk("fn write(values: &mut [u32]) { set values[0] = 7; } fn run() { let mut values: [u32; 2] = [1, 2]; write(&mut values[..]); }");
5343 +
}
5344 +
5345 +
/// Unsafe declarations can store raw pointers to stack values.
5346 +
@test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) {
5347 +
    try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }");
5348 +
    try expectAnalyzeOk("unsafe fn run() { let mut values: [u32; 2] = [1, 2]; let slice: *unsafe mut [u32] = &mut values[..] as *unsafe mut [u32]; set slice[0] = 7; }");
5349 +
    try expectAnalyzeOk("unsafe fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }");
5350 +
    try expectAnalyzeOk("unsafe fn run() { let values: [u32; 2] = [1, 2]; let slice: *unsafe [u32] = &values[..]; }");
5351 +
}
5352 +
5353 +
/// Permanent storage can produce safe stored pointers and slices.
5354 +
@test unsafe fn testPermanentPointerAllowed() throws (testing::TestError) {
5355 +
    try expectAnalyzeOk("static VALUE: u32 = 7; fn run() -> *u32 { return &VALUE; }");
5356 +
    try expectAnalyzeOk("static VALUES: [u32; 2] = [1, 2]; fn run() -> *[u32] { return &VALUES[..]; }");
5357 +
    try expectAnalyzeOk("fn run() -> *[u32] { return &[1, 2]; }");
5358 +
    try expectAnalyzeOk("fn run(value: *u32) -> *u32 { return &*value; }");
5359 +
    try expectAnalyzeOk("fn run(values: *[u32]) -> *[u32] { return &values[..]; }");
5360 +
    try expectAnalyzeOk("record Cell { value: u32 } fn run(value: *Cell) -> *u32 { return &value.value; }");
5361 +
    try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return &values[0]; }");
5362 +
    try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return values.ptr; }");
5363 +
    try expectAnalyzeOk("fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }");
5311 5364
}
5312 5365
5313 5366
/// References are rejected from every nested or storable type position.
5314 -
@test fn testNestedRefPositionsRejected() throws (testing::TestError) {
5367 +
@test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) {
5315 5368
    {
5316 5369
        let mut a = testResolver();
5317 5370
        let program = "record Marker: Once {} union Bad { Value(&u32) }";
5318 5371
        let result = try resolveProgramStr(&mut a, program);
5319 5372
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5344 5397
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5345 5398
    }
5346 5399
}
5347 5400
5348 5401
/// Function pointer parameter references remain call-scoped and valid.
5349 -
@test fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5402 +
@test unsafe fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5350 5403
    let program = "record Marker: Once {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }";
5351 5404
    try expectAnalyzeOk(program);
5352 5405
}
5353 5406
5354 5407
/// Pointer and slice casts cannot change reference ownership.
5355 -
@test fn testRefCastClassPreserved() throws (testing::TestError) {
5408 +
@test unsafe fn testRefCastClassPreserved() throws (testing::TestError) {
5356 5409
    {
5357 5410
        let mut a = testResolver();
5358 5411
        let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }";
5359 5412
        let result = try resolveProgramStr(&mut a, program);
5360 5413
        let err = try expectError(&result);
5369 5422
            else throw testing::TestError::Failed;
5370 5423
    }
5371 5424
}
5372 5425
5373 5426
/// Every operation that interprets an unsafe address requires an unsafe declaration.
5374 -
@test fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5427 +
@test unsafe fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5375 5428
    {
5376 5429
        let mut a = testResolver();
5377 5430
        let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
5378 5431
        let result = try resolveProgramStr(&mut a, program);
5379 5432
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5409 5462
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5410 5463
    }
5411 5464
}
5412 5465
5413 5466
/// Unsafe declarations may compose unsafe operations and calls.
5414 -
@test fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5467 +
@test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5415 5468
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }";
5416 5469
    try expectAnalyzeOk(program);
5417 5470
}
5418 5471
5419 5472
/// Unsafe code may drop a checked reference to an unsafe pointer.
5420 -
@test fn testUnsafePointerFromReference() throws (testing::TestError) {
5473 +
@test unsafe fn testUnsafePointerFromReference() throws (testing::TestError) {
5421 5474
    let program = "record Marker: Once {} unsafe fn store(pointer: *unsafe mut u32) { set *pointer = 42; } unsafe fn run() { let mut value: u32 = 0; store(&mut value as *unsafe mut u32); }";
5422 5475
    try expectAnalyzeOk(program);
5423 5476
}
5424 5477
5425 5478
/// Dropping a reference to an unsafe pointer cannot add mutability.
5426 -
@test fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5479 +
@test unsafe fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5427 5480
    let mut a = testResolver();
5428 5481
    let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
5429 5482
    let result = try resolveProgramStr(&mut a, program);
5430 5483
    let err = try expectError(&result);
5431 5484
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5432 5485
        else throw testing::TestError::Failed;
5433 5486
}
5434 5487
5435 5488
/// Recursive cast validation cannot hide a checked-to-unsafe transition.
5436 -
@test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5489 +
@test unsafe fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5437 5490
    let mut a = testResolver();
5438 5491
    let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }";
5439 5492
    let result = try resolveProgramStr(&mut a, program);
5440 5493
    let err = try expectError(&result);
5441 5494
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5442 5495
        else throw testing::TestError::Failed;
5443 5496
}
5444 5497
5445 5498
/// Unsafe code may drop a checked slice reference to an unsafe slice.
5446 -
@test fn testUnsafeSliceFromReference() throws (testing::TestError) {
5499 +
@test unsafe fn testUnsafeSliceFromReference() throws (testing::TestError) {
5447 5500
    let program = "record Marker: Once {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }";
5448 5501
    try expectAnalyzeOk(program);
5449 5502
}
5450 5503
5451 5504
/// Slice casts cannot add mutability.
5452 -
@test fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5505 +
@test unsafe fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5453 5506
    let mut a = testResolver();
5454 5507
    let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }";
5455 5508
    let result = try resolveProgramStr(&mut a, program);
5456 5509
    let err = try expectError(&result);
5457 5510
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5458 5511
        else throw testing::TestError::Failed;
5459 5512
}
5460 5513
5461 5514
/// Mutable unsafe receivers do not create checked exclusive loans.
5462 -
@test fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5515 +
@test unsafe fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5463 5516
    let program = "record Marker: Once {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }";
5464 5517
    try expectAnalyzeOk(program);
5465 5518
}
5466 5519
5467 5520
/// Unsafe instance-method attributes enable unsafe operations in the body.
5468 -
@test fn testUnsafeInstanceMethodBody() throws (testing::TestError) {
5521 +
@test unsafe fn testUnsafeInstanceMethodBody() throws (testing::TestError) {
5469 5522
    let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (*unsafe Read) get() -> u32; } instance Read for Value { unsafe fn (value: *unsafe Value) get() -> u32 { return value.number; } }";
5470 5523
    try expectAnalyzeOk(program);
5471 5524
}
5472 5525
5473 5526
/// Unsafe instance methods cannot implement safe trait contracts.
5474 -
@test fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5527 +
@test unsafe fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5475 5528
    let mut a = testResolver();
5476 5529
    let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
5477 5530
    let result = try resolveProgramStr(&mut a, program);
5478 5531
    try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
5479 5532
}
5480 5533
5481 5534
/// Unsafe trait methods retain their call-site requirement through dispatch.
5482 -
@test fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5535 +
@test unsafe fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5483 5536
    let mut a = testResolver();
5484 5537
    let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }";
5485 5538
    let result = try resolveProgramStr(&mut a, program);
5486 5539
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5487 5540
}
5541 +
5542 +
/// An unsafe callback retains its requirement at an indirect call.
5543 +
@test unsafe fn testUnsafeCallbackCallRejected() throws (testing::TestError) {
5544 +
    let mut a = testResolver();
5545 +
    let result = try resolveProgramStr(&mut a,
5546 +
        "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }");
5547 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5548 +
}
5549 +
5550 +
/// An unsafe function cannot enter a safe callback slot.
5551 +
@test unsafe fn testUnsafeCallbackAssignmentRejected() throws (testing::TestError) {
5552 +
    let mut a = testResolver();
5553 +
    let result = try resolveProgramStr(&mut a,
5554 +
        "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }");
5555 +
    let err = try expectError(&result);
5556 +
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5557 +
        else throw testing::TestError::Failed;
5558 +
}
5559 +
5560 +
/// A safe function can enter an unsafe callback slot.
5561 +
@test unsafe fn testSafeCallbackIntoUnsafeSlot() throws (testing::TestError) {
5562 +
    try expectAnalyzeOk(
5563 +
        "fn load() -> u32 { return 1; } unsafe fn run() -> u32 { let callback: unsafe fn() -> u32 = load; return callback(); }");
5564 +
}
5565 +
5566 +
/// Borrowed callback storage must preserve its function safety type.
5567 +
@test unsafe fn testBorrowedCallbackSafetyInvariant() throws (testing::TestError) {
5568 +
    let mut a = testResolver();
5569 +
    let result = try resolveProgramStr(&mut a,
5570 +
        "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }");
5571 +
    let err = try expectError(&result);
5572 +
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5573 +
        else throw testing::TestError::Failed;
5574 +
}
lib/std/lang/scanner.rad +13 -13
236 236
237 237
    return Scanner { sourceLoc, source, token: 0, cursor: 0, pool };
238 238
}
239 239
240 240
/// Check if we've reached the end of input.
241 -
export fn isEof(s: *Scanner) -> bool {
241 +
export fn isEof(s: &Scanner) -> bool {
242 242
    return s.cursor >= s.source.len;
243 243
}
244 244
245 245
/// Get the current character, if any.
246 -
export fn current(s: *Scanner) -> ?u8 {
246 +
export fn current(s: &Scanner) -> ?u8 {
247 247
    if isEof(s) {
248 248
        return nil;
249 249
    }
250 250
    return s.source[s.cursor];
251 251
}
252 252
253 253
/// Peek at the next character without advancing the scanner.
254 -
fn peek(s: *Scanner) -> ?u8 {
254 +
fn peek(s: &Scanner) -> ?u8 {
255 255
    if s.cursor + 1 >= s.source.len {
256 256
        return nil;
257 257
    }
258 258
    return s.source[s.cursor + 1];
259 259
}
260 260
261 261
/// Advance scanner and return the character that was consumed.
262 -
fn advance(s: *mut Scanner) -> u8 {
262 +
fn advance(s: &mut Scanner) -> u8 {
263 263
    let ch = s.source[s.cursor];
264 264
    set s.cursor += 1;
265 265
    return ch;
266 266
}
267 267
268 268
/// Consume the expected character if it matches the current position.
269 -
fn consume(s: *mut Scanner, expected: u8) -> bool {
269 +
fn consume(s: &mut Scanner, expected: u8) -> bool {
270 270
    if let c = current(s); c == expected {
271 271
        advance(s);
272 272
        return true;
273 273
    }
274 274
    return false;
275 275
}
276 276
277 277
/// Create a token from the current scanner state.
278 -
fn tok(s: *Scanner, kind: TokenKind) -> Token {
278 +
fn tok(s: &Scanner, kind: TokenKind) -> Token {
279 279
    return Token { kind, source: &s.source[s.token..s.cursor], offset: s.token };
280 280
}
281 281
282 282
/// Create an invalid token with the given message.
283 283
export fn invalid(offset: u32, message: *[u8]) -> Token {
284 284
    return Token { kind: TokenKind::Invalid, source: message, offset };
285 285
}
286 286
287 287
/// Skip whitespace characters and line comments.
288 -
fn skipWhitespace(s: *mut Scanner) {
288 +
fn skipWhitespace(s: &mut Scanner) {
289 289
    while let ch = current(s) {
290 290
        match ch {
291 291
            case ' ', '\n', '\r', '\t' => advance(s),
292 292
            case '/' => {
293 293
                if let c = peek(s); c == '/' {
302 302
        }
303 303
    }
304 304
}
305 305
306 306
/// Scan numeric literal (decimal, hex, or binary).
307 -
fn scanNumber(s: *mut Scanner) -> Token {
307 +
fn scanNumber(s: &mut Scanner) -> Token {
308 308
    // Check for hex literal (`0x` or `0X` prefix).
309 309
    if s.source[s.cursor - 1] == '0' {
310 310
        if let ch = current(s); ch == 'x' or ch == 'X' {
311 311
            advance(s);
312 312
            // Must have at least one hex digit after `0x`.
345 345
        }
346 346
    }
347 347
    return tok(s, TokenKind::Number);
348 348
}
349 349
350 -
fn scanDelimited(s: *mut Scanner, delim: u8, kind: TokenKind) -> ?Token {
350 +
fn scanDelimited(s: &mut Scanner, delim: u8, kind: TokenKind) -> ?Token {
351 351
    while let ch = current(s); ch <> delim {
352 352
        if not char::isPrint(ch) {
353 353
            return invalid(s.token, "invalid character");
354 354
        }
355 355
        if consume(s, '\\') { // Consume escapes
364 364
    }
365 365
    return tok(s, kind);
366 366
}
367 367
368 368
/// Scan string literal enclosed in double quotes.
369 -
fn scanString(s: *mut Scanner) -> Token {
369 +
fn scanString(s: &mut Scanner) -> Token {
370 370
    if let tok = scanDelimited(s, '"', TokenKind::String) {
371 371
        return tok;
372 372
    }
373 373
    return invalid(s.token, "unterminated string");
374 374
}
375 375
376 376
/// Scan character literal enclosed in single quotes.
377 -
fn scanChar(s: *mut Scanner) -> Token {
377 +
fn scanChar(s: &mut Scanner) -> Token {
378 378
    if let tok = scanDelimited(s, '\'', TokenKind::Char) {
379 379
        return tok;
380 380
    }
381 381
    return invalid(s.token, "unterminated character");
382 382
}
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 +
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);
414 414
    }
415 415
    return tok(s, kind);
416 416
}
417 417
418 418
/// Scan the next token.
419 -
export fn next(s: *mut Scanner) -> Token {
419 +
export 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/sexpr.rad +14 -14
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: *mut u32 },
14 +
    Buffer { buf: *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.
31 31
    /// A block with a name, inline items, and child statements on separate lines.
32 32
    Block { name: *[u8], items: *[Expr], children: *[Expr] },
33 33
}
34 34
35 35
/// Allocate an array of Expr in the arena.
36 -
export fn allocExprs(arena: *mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) {
36 +
export fn allocExprs(arena: &mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) {
37 37
    if len == 0 {
38 38
        throw alloc::AllocError::OutOfMemory;
39 39
    }
40 40
    let ptr = try alloc::allocSlice(arena, @sizeOf(Expr), @alignOf(Expr), len);
41 41
    return ptr as *mut [Expr];
42 42
}
43 43
44 44
/// Allocate and copy items into the arena.
45 -
export fn allocItems(a: *mut alloc::Arena, items: *[Expr]) -> *[Expr] {
45 +
export fn allocItems(a: &mut alloc::Arena, items: &[Expr]) -> *[Expr] {
46 46
    if items.len == 0 {
47 47
        return &[];
48 48
    }
49 49
    let buf = try! allocExprs(a, items.len);
50 50
    for item, i in items {
62 62
export fn str(s: *[u8]) -> Expr {
63 63
    return Expr::Str(s);
64 64
}
65 65
66 66
/// Shorthand for creating a list.
67 -
export fn list(a: *mut alloc::Arena, head: *[u8], tail: *[Expr]) -> Expr {
67 +
export fn list(a: &mut alloc::Arena, head: *[u8], tail: &[Expr]) -> Expr {
68 68
    return Expr::List { head, tail: allocItems(a, tail), multiline: false };
69 69
}
70 70
71 71
/// Shorthand for creating a bracket-delimited vector.
72 -
export fn vec(a: *mut alloc::Arena, items: *[Expr]) -> Expr {
72 +
export fn vec(a: &mut alloc::Arena, items: &[Expr]) -> Expr {
73 73
    return Expr::Vec { items: allocItems(a, items) };
74 74
}
75 75
76 76
/// Shorthand for creating a block with inline items and child expressions.
77 -
export fn block(a: *mut alloc::Arena, name: *[u8], items: *[Expr], children: *[Expr]) -> Expr {
77 +
export fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr {
78 78
    return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) };
79 79
}
80 80
81 81
/// Write a string to the output target.
82 -
export fn write(out: *mut Output, s: *[u8]) {
82 +
export unsafe fn write(out: &mut Output, s: &[u8]) {
83 83
    match *out {
84 84
        case Output::Stdout => io::print(s),
85 85
        case Output::Buffer { buf, pos } => {
86 86
            let remaining = buf.len - *pos;
87 87
            let toWrite = remaining if s.len > remaining else s.len;
92 92
        }
93 93
    }
94 94
}
95 95
96 96
/// Emit indentation to the output target.
97 -
fn indentTo(out: *mut Output, depth: u32) {
97 +
unsafe fn indentTo(out: &mut Output, depth: u32) {
98 98
    for _ in 0..depth {
99 99
        write(out, "  ");
100 100
    }
101 101
}
102 102
103 103
/// Print a single character with escaping to the output target.
104 -
export fn printEscapedTo(out: *mut Output, c: u8) {
104 +
export unsafe fn printEscapedTo(out: &mut Output, c: u8) {
105 105
    match c {
106 106
        case '\n' => write(out, "\\n"),
107 107
        case '\r' => write(out, "\\r"),
108 108
        case '\t' => write(out, "\\t"),
109 109
        case '\\' => write(out, "\\\\"),
111 111
        else => write(out, &[c]),
112 112
    }
113 113
}
114 114
115 115
/// Print a quoted string with escape sequences to the output target.
116 -
export fn printStringTo(out: *mut Output, s: *[u8]) {
116 +
export unsafe fn printStringTo(out: &mut Output, s: &[u8]) {
117 117
    write(out, "\"");
118 118
    for i in 0..s.len {
119 119
        printEscapedTo(out, s[i]);
120 120
    }
121 121
    write(out, "\"");
122 122
}
123 123
124 124
/// Print a character literal with escape sequences to the output target.
125 -
export fn printCharTo(out: *mut Output, c: u8) {
125 +
export unsafe fn printCharTo(out: &mut Output, c: u8) {
126 126
    write(out, "'");
127 127
    printEscapedTo(out, c);
128 128
    write(out, "'");
129 129
}
130 130
131 131
/// Print an S-expression to the given output target at the given depth.
132 -
export fn printTo(expr: Expr, depth: u32, out: *mut Output) {
132 +
export unsafe fn printTo(expr: Expr, depth: u32, out: &mut Output) {
133 133
    match expr {
134 134
        case Expr::Null => {},
135 135
        case Expr::Sym(s) => write(out, s),
136 136
        case Expr::Str(s) => printStringTo(out, s),
137 137
        case Expr::Char(c) => printCharTo(out, c),
193 193
        }
194 194
    }
195 195
}
196 196
197 197
/// Print an S-expression to stdout at the given indentation depth.
198 -
export fn print(expr: Expr, depth: u32) {
198 +
export unsafe fn print(expr: Expr, depth: u32) {
199 199
    let mut out = Output::Stdout;
200 200
    printTo(expr, depth, &mut out);
201 201
}
202 202
203 203
/// Emit indentation for `depth` levels to stdout.
204 -
export fn indent(depth: u32) {
204 +
export unsafe fn indent(depth: u32) {
205 205
    let mut out = Output::Stdout;
206 206
    indentTo(&mut out, depth);
207 207
}
208 208
lib/std/mem.rad +4 -4
3 3
    /// Buffer is too small.
4 4
    BufferTooSmall,
5 5
}
6 6
7 7
/// Copy bytes between two slices. Returns the number of bytes copied.
8 -
export fn copy(into: *mut [u8], from: *[u8]) -> u32 throws (MemoryError) {
8 +
export fn copy(into: &mut [u8], from: &[u8]) -> u32 throws (MemoryError) {
9 9
    if into.len < from.len {
10 10
        throw MemoryError::BufferTooSmall;
11 11
    }
12 12
    for x, i in from {
13 13
        set into[i] = x;
15 15
    return from.len;
16 16
}
17 17
18 18
/// Strip a byte-level prefix from the input, and return the suffix.
19 19
/// Returns `nil` if the prefix wasn't found.
20 -
export fn stripPrefix(prefix: *[u8], input: *[u8]) -> ?*[u8] {
20 +
export fn stripPrefix(prefix: &[u8], input: *[u8]) -> ?*[u8] {
21 21
    if prefix.len == 0 {
22 22
        return input;
23 23
    }
24 24
    if prefix.len > input.len {
25 25
        return nil;
52 52
    }
53 53
    return count;
54 54
}
55 55
56 56
/// Check whether two byte slices have the same length and contents.
57 -
export fn eq(a: *[u8], b: *[u8]) -> bool {
57 +
export fn eq(a: &[u8], b: &[u8]) -> bool {
58 58
    if a.len <> b.len {
59 59
        return false;
60 60
    }
61 61
    if a.ptr == b.ptr {
62 62
        return true;
70 70
}
71 71
72 72
/// Compare two byte slices lexicographically.
73 73
///
74 74
/// Returns `-1` when `a < b`, `1` when `a > b`, and `0` when equal.
75 -
export fn cmp(a: *[u8], b: *[u8]) -> i32 {
75 +
export fn cmp(a: &[u8], b: &[u8]) -> i32 {
76 76
    let aLen = a.len;
77 77
    let bLen = b.len;
78 78
79 79
    let common = bLen if bLen < aLen else aLen;
80 80
    for i in 0..common {
lib/std/sys/unix.rad +18 -14
31 31
/// Special value representing current working directory for `openat()`.
32 32
constant AT_FDCWD: i64 = -100;
33 33
34 34
/// Opens a file at the given path and returns a file descriptor.
35 35
/// Returns a negative value on error.
36 -
export fn open(path: *[u8], flags: OpenFlags) -> i64 {
36 +
export fn open(path: &[u8], flags: OpenFlags) -> i64 {
37 37
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0);
38 38
}
39 39
40 40
/// Opens a file at the given path with mode, returns a file descriptor.
41 -
export fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 {
41 +
export fn openOpts(path: &[u8], flags: OpenFlags, mode: i64) -> i64 {
42 42
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode);
43 43
}
44 44
45 45
/// Reads from a file descriptor into the provided buffer.
46 46
/// Returns the number of bytes read, or a negative value on error.
47 -
export fn read(fd: i64, buf: *mut [u8]) -> i64 {
47 +
export fn read(fd: i64, buf: &mut [u8]) -> i64 {
48 48
    return intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0);
49 49
}
50 50
51 51
/// Reads from a file descriptor until EOF or buffer is full.
52 52
/// Returns the total number of bytes read, or a negative value on error.
53 -
export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 {
53 +
export fn readToEnd(fd: i64, buf: &mut [u8]) -> i64 {
54 54
    let mut total: u32 = 0;
55 55
    while total < buf.len {
56 -
        let chunk = &mut buf[total..];
57 -
        let n = read(fd, chunk);
56 +
        let n = read(fd, &mut buf[total..]);
58 57
59 58
        if n < 0 {
60 59
            return n;
61 60
        }
62 61
        if n == 0 {
67 66
    return total as i64;
68 67
}
69 68
70 69
/// Writes to a file descriptor from the provided buffer.
71 70
/// Returns the number of bytes written, or a negative value on error.
72 -
export fn write(fd: i64, buf: *[u8]) -> i64 {
71 +
export fn write(fd: i64, buf: &[u8]) -> i64 {
73 72
    return intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0);
74 73
}
75 74
76 75
/// Writes the entire contents of a buffer to a file descriptor.
77 76
/// Returns `false` when the descriptor cannot accept the full buffer.
78 -
export fn writeAll(fd: i64, data: *[u8]) -> bool {
77 +
export fn writeAll(fd: i64, data: &[u8]) -> bool {
79 78
    let mut written: u32 = 0;
80 79
    while written < data.len {
81 80
        let n = write(fd, &data[written..]);
82 81
        if n <= 0 {
83 82
            return false;
92 91
export fn close(fd: i64) -> i64 {
93 92
    return intrinsics::ecall(57, fd, 0, 0, 0);
94 93
}
95 94
96 95
/// Reads the entire contents of a file at the given path into the provided buffer.
97 -
/// Returns a slice containing the data read, or `nil` on error.
98 -
export fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] {
96 +
/// Return the number of bytes read, or `nil` on error.
97 +
export fn readFile(path: &[u8], buf: &mut [u8]) -> ?u32 {
99 98
    let fd = open(path, O_RDONLY);
100 99
    if fd < 0 {
101 100
        return nil;
102 101
    }
103 102
    let n = readToEnd(fd, buf);
106 105
        return nil;
107 106
    }
108 107
    if n < 0 {
109 108
        return nil;
110 109
    }
111 -
    return &buf[..n as u32];
110 +
    return n as u32;
112 111
}
113 112
114 113
/// Exit the current process with the given status code.
115 114
export fn exit(status: i64) {
116 115
    intrinsics::ecall(93, status, 0, 0, 0);
117 116
}
118 117
119 118
/// Writes the entire contents of a buffer to a file at the given path.
120 119
/// Creates the file if it doesn't exist, truncates if it does.
121 120
/// Returns `true` on success.
122 -
export fn writeFile(path: *[u8], data: *[u8]) -> bool {
123 -
    return writeFileParts(path, &[data]);
121 +
export fn writeFile(path: &[u8], data: &[u8]) -> bool {
122 +
    let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC);
123 +
    let fd = openOpts(path, flags, 420);
124 +
    if fd < 0 { return false; }
125 +
    let written = writeAll(fd, data);
126 +
    let closed = close(fd) == 0;
127 +
    return written and closed;
124 128
}
125 129
126 130
/// Writes each buffer to a file at the given path.
127 131
/// Creates the file if it doesn't exist, truncates if it does.
128 132
/// Returns `true` only when every part and the final close succeed.
129 -
export fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool {
133 +
export fn writeFileParts(path: &[u8], parts: &[*[u8]]) -> bool {
130 134
    let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC);
131 135
    let fd = openOpts(path, flags, 420); // 0644 in octal.
132 136
    if fd < 0 {
133 137
        return false;
134 138
    }
lib/std/testing.rad +8 -8
16 16
17 17
/// Descriptor for a single test case, holding its module path, name, and entry point.
18 18
export record TestInfo: Copy {
19 19
    module: *[u8],
20 20
    name: *[u8],
21 -
    func: fn() throws (TestError),
21 +
    func: unsafe fn() throws (TestError),
22 22
}
23 23
24 24
/// Construct a [`TestInfo`]. Used by the compiler's synthetic test harness.
25 -
export fn test(module: *[u8], name: *[u8], func: fn() throws (TestError)) -> TestInfo {
25 +
export fn test(module: *[u8], name: *[u8], func: unsafe fn() throws (TestError)) -> TestInfo {
26 26
    return TestInfo { module, name, func };
27 27
}
28 28
29 29
/// Run all tests and return `0` on success or `1` if any test failed.
30 -
export fn runAllTests(tests: *[TestInfo]) -> i32 {
30 +
export unsafe fn runAllTests(tests: &[TestInfo]) -> i32 {
31 31
    let mut ctx: Ctx = Ctx { passed: 0, failed: 0 };
32 32
33 33
    io::print("Running ");
34 34
    io::printU32(tests.len);
35 35
    io::print(" test(s)...\n\n");
43 43
        return 1;
44 44
    }
45 45
    return 0;
46 46
}
47 47
48 -
fn runTest(
49 -
    ctx: *mut Ctx,
48 +
unsafe fn runTest(
49 +
    ctx: &mut Ctx,
50 50
    module: *[u8],
51 51
    name: *[u8],
52 -
    testFn: fn () throws (TestError)
52 +
    testFn: unsafe fn() throws (TestError)
53 53
) {
54 54
    io::print("test ");
55 55
    io::print(module);
56 56
    io::print("::");
57 57
    io::print(name);
68 68
        io::printLn("FAILED");
69 69
        set ctx.failed += 1;
70 70
    }
71 71
}
72 72
73 -
fn printTestSummary(ctx: *Ctx) {
73 +
fn printTestSummary(ctx: &Ctx) {
74 74
    if (ctx.failed > 0) {
75 75
        io::print("\ntest result: FAILED. ");
76 76
        io::printU32(ctx.passed);
77 77
        io::print(" passed; ");
78 78
        io::printU32(ctx.failed);
83 83
        io::printLn(" passed; 0 failed");
84 84
    }
85 85
}
86 86
87 87
/// Assert that two byte slices are equal.
88 -
export fn expectBytesEq(left: *[u8], right: *[u8])
88 +
export fn expectBytesEq(left: &[u8], right: &[u8])
89 89
    throws (TestError)
90 90
{
91 91
    try expect(mem::eq(left, right));
92 92
}
93 93
lib/std/tests.rad +53 -53
15 15
16 16
// fmt /////////////////////////////////////////////////////////////////////////
17 17
18 18
@test fn testFormatU32Zero() throws (testing::TestError) {
19 19
    let mut buffer: [u8; 11] = [0; 11];
20 -
    let result: *[u8] = fmt::formatU32(0, &mut buffer[..]);
21 -
    try testing::expect(result.len == 1);
22 -
    try testing::expectBytesEq(result, "0");
20 +
    let start = fmt::formatU32(0, &mut buffer[..]);
21 +
    try testing::expect(buffer.len - start == 1);
22 +
    try testing::expectBytesEq(&buffer[start..], "0");
23 23
}
24 24
25 25
@test fn testFormatU32Basic() throws (testing::TestError) {
26 26
    let mut buffer: [u8; 11] = [0; 11];
27 -
    let result: *[u8] = fmt::formatU32(123, &mut buffer[..]);
28 -
    try testing::expect(result.len == 3);
29 -
    try testing::expectBytesEq(result, "123");
27 +
    let start = fmt::formatU32(123, &mut buffer[..]);
28 +
    try testing::expect(buffer.len - start == 3);
29 +
    try testing::expectBytesEq(&buffer[start..], "123");
30 30
}
31 31
32 32
@test fn testFormatU32Large() throws (testing::TestError) {
33 33
    let mut buffer: [u8; 11] = [0; 11];
34 -
    let result: *[u8] = fmt::formatU32(90000, &mut buffer[..]);
35 -
    try testing::expect(result.len == 5);
36 -
    try testing::expectBytesEq(result, "90000");
34 +
    let start = fmt::formatU32(90000, &mut buffer[..]);
35 +
    try testing::expect(buffer.len - start == 5);
36 +
    try testing::expectBytesEq(&buffer[start..], "90000");
37 37
}
38 38
39 39
@test fn testFormatU32Max() throws (testing::TestError) {
40 40
    let mut buffer: [u8; 11] = [0; 11];
41 -
    let result: *[u8] = fmt::formatU32(4294967295, &mut buffer[..]);
42 -
    try testing::expect(result.len == 10);
43 -
    try testing::expectBytesEq(result, "4294967295");
41 +
    let start = fmt::formatU32(4294967295, &mut buffer[..]);
42 +
    try testing::expect(buffer.len - start == 10);
43 +
    try testing::expectBytesEq(&buffer[start..], "4294967295");
44 44
}
45 45
46 46
@test fn testFormatI32Positive() throws (testing::TestError) {
47 47
    let mut buffer: [u8; 11] = [0; 11];
48 -
    let result: *[u8] = fmt::formatI32(456, &mut buffer[..]);
49 -
    try testing::expect(result.len == 3);
50 -
    try testing::expectBytesEq(result, "456");
48 +
    let start = fmt::formatI32(456, &mut buffer[..]);
49 +
    try testing::expect(buffer.len - start == 3);
50 +
    try testing::expectBytesEq(&buffer[start..], "456");
51 51
}
52 52
53 53
@test fn testFormatI32Negative() throws (testing::TestError) {
54 54
    let mut buffer: [u8; 11] = [0; 11];
55 -
    let result: *[u8] = fmt::formatI32(-789, &mut buffer[..]);
56 -
    try testing::expect(result.len == 4);
57 -
    try testing::expectBytesEq(result, "-789");
55 +
    let start = fmt::formatI32(-789, &mut buffer[..]);
56 +
    try testing::expect(buffer.len - start == 4);
57 +
    try testing::expectBytesEq(&buffer[start..], "-789");
58 58
}
59 59
60 60
@test fn testFormatI32Zero() throws (testing::TestError) {
61 61
    let mut buffer: [u8; 11] = [0; 11];
62 -
    let result: *[u8] = fmt::formatI32(0, &mut buffer[..]);
63 -
    try testing::expect(result.len == 1);
64 -
    try testing::expectBytesEq(result, "0");
62 +
    let start = fmt::formatI32(0, &mut buffer[..]);
63 +
    try testing::expect(buffer.len - start == 1);
64 +
    try testing::expectBytesEq(&buffer[start..], "0");
65 65
}
66 66
67 67
@test fn testFormatI32Max() throws (testing::TestError) {
68 68
    let mut buffer: [u8; 11] = [0; 11];
69 -
    let result: *[u8] = fmt::formatI32(2147483647, &mut buffer[..]);
70 -
    try testing::expect(result.len == 10);
71 -
    try testing::expectBytesEq(result, "2147483647");
69 +
    let start = fmt::formatI32(2147483647, &mut buffer[..]);
70 +
    try testing::expect(buffer.len - start == 10);
71 +
    try testing::expectBytesEq(&buffer[start..], "2147483647");
72 72
}
73 73
74 74
@test fn testFormatI32Min() throws (testing::TestError) {
75 75
    let mut buffer: [u8; 11] = [0; 11];
76 -
    let result: *[u8] = fmt::formatI32(-2147483648, &mut buffer[..]);
77 -
    try testing::expect(result.len == 11);
78 -
    try testing::expectBytesEq(result, "-2147483648");
76 +
    let start = fmt::formatI32(-2147483648, &mut buffer[..]);
77 +
    try testing::expect(buffer.len - start == 11);
78 +
    try testing::expectBytesEq(&buffer[start..], "-2147483648");
79 79
}
80 80
81 81
@test fn testFormatU64Zero() throws (testing::TestError) {
82 82
    let mut buffer: [u8; 20] = [0; 20];
83 -
    let result: *[u8] = fmt::formatU64(0, &mut buffer[..]);
84 -
    try testing::expect(result.len == 1);
85 -
    try testing::expectBytesEq(result, "0");
83 +
    let start = fmt::formatU64(0, &mut buffer[..]);
84 +
    try testing::expect(buffer.len - start == 1);
85 +
    try testing::expectBytesEq(&buffer[start..], "0");
86 86
}
87 87
88 88
@test fn testFormatU64Max() throws (testing::TestError) {
89 89
    let mut buffer: [u8; 20] = [0; 20];
90 -
    let result: *[u8] = fmt::formatU64(18446744073709551615, &mut buffer[..]);
91 -
    try testing::expect(result.len == 20);
92 -
    try testing::expectBytesEq(result, "18446744073709551615");
90 +
    let start = fmt::formatU64(18446744073709551615, &mut buffer[..]);
91 +
    try testing::expect(buffer.len - start == 20);
92 +
    try testing::expectBytesEq(&buffer[start..], "18446744073709551615");
93 93
}
94 94
95 95
@test fn testFormatI64Negative() throws (testing::TestError) {
96 96
    let mut buffer: [u8; 20] = [0; 20];
97 -
    let result: *[u8] = fmt::formatI64(-9876543210, &mut buffer[..]);
98 -
    try testing::expect(result.len == 11);
99 -
    try testing::expectBytesEq(result, "-9876543210");
97 +
    let start = fmt::formatI64(-9876543210, &mut buffer[..]);
98 +
    try testing::expect(buffer.len - start == 11);
99 +
    try testing::expectBytesEq(&buffer[start..], "-9876543210");
100 100
}
101 101
102 102
@test fn testFormatI64Min() throws (testing::TestError) {
103 103
    let mut buffer: [u8; 20] = [0; 20];
104 -
    let result: *[u8] = fmt::formatI64(-9223372036854775808, &mut buffer[..]);
105 -
    try testing::expect(result.len == 20);
106 -
    try testing::expectBytesEq(result, "-9223372036854775808");
104 +
    let start = fmt::formatI64(-9223372036854775808, &mut buffer[..]);
105 +
    try testing::expect(buffer.len - start == 20);
106 +
    try testing::expectBytesEq(&buffer[start..], "-9223372036854775808");
107 107
}
108 108
109 109
@test fn testParseIntLiteralText() throws (testing::TestError) {
110 110
    let dec = try fmt::parseInt("123") catch {
111 111
        throw testing::TestError::Failed;
252 252
        throw testing::TestError::Failed;
253 253
    }
254 254
    let data = unix::readFile(path, &mut buffer[..]) else {
255 255
        throw testing::TestError::Failed;
256 256
    };
257 -
    try testing::expectBytesEq(data, "hello world");
257 +
    try testing::expectBytesEq(&buffer[..data], "hello world");
258 258
}
259 259
260 260
@test fn testStripPrefixMatch() throws (testing::TestError) {
261 261
    let input: *[u8] = "hello world";
262 262
    let prefix: *[u8] = "hello";
338 338
    try testing::expect(mem::cmp("abcd", "abc") > 0);
339 339
}
340 340
341 341
// vec /////////////////////////////////////////////////////////////////////////
342 342
343 -
@test fn testVecInitialState() throws (testing::TestError) {
343 +
@test unsafe fn testVecInitialState() throws (testing::TestError) {
344 344
    let mut arena: [u8; 16] align(4) = undefined;
345 345
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
346 346
347 347
    try testing::expect(vec::len(&v) == 0);
348 348
    try testing::expect(vec::capacity(&v) == 4);
349 349
}
350 350
351 -
@test fn testVecPushAndGet() throws (testing::TestError) {
351 +
@test unsafe fn testVecPushAndGet() throws (testing::TestError) {
352 352
    let mut arena: [u8; 16] align(4) = undefined;
353 353
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
354 354
355 355
    let val1: i32 = 42;
356 356
    try testing::expect(vec::push(&mut v, &val1));
357 357
    try testing::expect(vec::len(&v) == 1);
358 358
359 359
    let ptr = vec::get(&v, 0) else {
360 360
        throw testing::TestError::Failed;
361 361
    };
362 -
    let i32ptr: *i32 = ptr as *i32;
362 +
    let i32ptr: *unsafe i32 = ptr as *unsafe i32;
363 363
    let value: i32 = *i32ptr;
364 364
    try testing::expect(value == 42);
365 365
}
366 366
367 -
@test fn testVecPushPop() throws (testing::TestError) {
367 +
@test unsafe fn testVecPushPop() throws (testing::TestError) {
368 368
    let mut arena: [u8; 16] align(4) = undefined;
369 369
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
370 370
371 371
    let val1: i32 = 42;
372 372
    let val2: i32 = 100;
396 396
    try testing::expect(vec::len(&v) == 0);
397 397
398 398
    try testing::expectNot(vec::pop(&mut v, &mut popped));
399 399
}
400 400
401 -
@test fn testVecGetSet() throws (testing::TestError) {
401 +
@test unsafe fn testVecGetSet() throws (testing::TestError) {
402 402
    let mut arena: [u8; 32] align(4) = undefined;
403 403
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
404 404
405 405
    let val1: i32 = 10;
406 406
    let val2: i32 = 20;
409 409
    vec::push(&mut v, &val1);
410 410
    vec::push(&mut v, &val2);
411 411
    vec::push(&mut v, &val3);
412 412
413 413
    if let ptr = vec::get(&v, 0) {
414 -
        let value: i32 = *(ptr as *i32);
414 +
        let value: i32 = *(ptr as *unsafe i32);
415 415
        try testing::expect(value == 10);
416 416
    } else {
417 417
        throw testing::TestError::Failed;
418 418
    }
419 419
420 420
    if let ptr = vec::get(&v, 1) {
421 -
        let value: i32 = *(ptr as *i32);
421 +
        let value: i32 = *(ptr as *unsafe i32);
422 422
        try testing::expect(value == 20);
423 423
    } else {
424 424
        throw testing::TestError::Failed;
425 425
    }
426 426
427 427
    if let ptr = vec::get(&v, 2) {
428 -
        let value: i32 = *(ptr as *i32);
428 +
        let value: i32 = *(ptr as *unsafe i32);
429 429
        try testing::expect(value == 30);
430 430
    } else {
431 431
        throw testing::TestError::Failed;
432 432
    }
433 433
    try testing::expect(vec::get(&v, 3) == nil);
434 434
435 435
    let new: i32 = 999;
436 436
    try testing::expect(vec::put(&mut v, 1, &new));
437 437
438 438
    if let ptr = vec::get(&v, 1) {
439 -
        let value: i32 = *(ptr as *i32);
439 +
        let value: i32 = *(ptr as *unsafe i32);
440 440
        try testing::expect(value == 999);
441 441
    } else {
442 442
        throw testing::TestError::Failed;
443 443
    }
444 444
    try testing::expectNot(vec::put(&mut v, 5, &new));
445 445
}
446 446
447 -
@test fn testVecCapacity() throws (testing::TestError) {
447 +
@test unsafe fn testVecCapacity() throws (testing::TestError) {
448 448
    let mut arena: [u8; 16] align(4) = undefined;
449 449
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
450 450
451 451
    let val: i32 = 7;
452 452
    try testing::expect(vec::push(&mut v, &val));
457 457
458 458
    try testing::expectNot(vec::push(&mut v, &val));
459 459
    try testing::expect(vec::len(&v) == 4);
460 460
}
461 461
462 -
@test fn testVecReset() throws (testing::TestError) {
462 +
@test unsafe fn testVecReset() throws (testing::TestError) {
463 463
    let mut arena: [u8; 16] align(4) = undefined;
464 464
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
465 465
466 466
    let val: i32 = 123;
467 467
    vec::push(&mut v, &val);
473 473
474 474
    try testing::expect(vec::push(&mut v, &val));
475 475
    try testing::expect(vec::len(&v) == 1);
476 476
}
477 477
478 -
@test fn testVecStruct() throws (testing::TestError) {
478 +
@test unsafe fn testVecStruct() throws (testing::TestError) {
479 479
    let mut arena: [u8; 64] align(4) = undefined;
480 480
    let mut v = vec::new(&mut arena[..], @sizeOf(Point), @alignOf(Point));
481 481
482 482
    let p1: Point = Point { x: 1, y: 2 };
483 483
    let p2: Point = Point { x: 3, y: 4 };
484 484
485 485
    try testing::expect(vec::push(&mut v, &p1));
486 486
    try testing::expect(vec::push(&mut v, &p2));
487 487
488 488
    if let ptr = vec::get(&v, 0) {
489 -
        let p: *Point = ptr as *Point;
489 +
        let p: *unsafe Point = ptr as *unsafe Point;
490 490
        try testing::expect(p.x == 1);
491 491
        try testing::expect(p.y == 2);
492 492
    } else {
493 493
        throw testing::TestError::Failed;
494 494
    }
lib/std/vec.rad +22 -30
1 -
//! Raw vector: type-unsafe dynamic array backed by static storage.
1 +
//! Raw vector backed by caller-managed storage.
2 2
//!
3 -
//! Users provide their own arena (static array) and the vector manages
3 +
//! Users provide their own storage and the vector manages
4 4
//! element count within that arena. The arena should be aligned according
5 5
//! to the element type's requirements.
6 6
7 7
/// Raw vector metadata structure.
8 8
///
9 9
/// Does not own storage, points to user-provided arena.
10 10
export record RawVec: Copy {
11 11
    /// Pointer to user-provided byte arena.
12 -
    data: *mut [u8],
12 +
    data: *unsafe mut [u8],
13 13
    /// Current number of elements stored.
14 14
    len: u32,
15 15
    /// Size of each element in bytes (stride between elements).
16 16
    stride: u32,
17 17
    /// Alignment in bytes required by element type (>= 1).
18 18
    alignment: u32,
19 19
}
20 20
21 21
/// Create a new raw vector with external arena.
22 22
///
23 -
/// * `arena` is a pointer to static array backing storage.
23 +
/// * `arena` must outlive the vector and all returned element pointers.
24 24
/// * `stride` is the size of each element.
25 25
/// * `alignment` is the required alignment for elements.
26 -
export fn new(arena: *mut [u8], stride: u32, alignment: u32) -> RawVec {
26 +
export unsafe fn new(arena: &mut [u8], stride: u32, alignment: u32) -> RawVec {
27 27
    assert stride > 0;
28 28
    assert alignment > 0;
29 29
    assert (arena.ptr as u32) % alignment == 0;
30 30
    assert (arena.len % stride) == 0;
31 31
32 -
    return RawVec { data: arena, len: 0, stride, alignment };
32 +
    return RawVec { data: arena as *unsafe mut [u8], len: 0, stride, alignment };
33 33
}
34 34
35 35
/// Get the current number of elements in the vector.
36 -
export fn len(vec: *RawVec) -> u32 {
36 +
export fn len(vec: &RawVec) -> u32 {
37 37
    return vec.len;
38 38
}
39 39
40 40
/// Get the maximum capacity of the vector.
41 -
export fn capacity(vec: *RawVec) -> u32 {
41 +
export unsafe fn capacity(vec: &RawVec) -> u32 {
42 42
    return vec.data.len / vec.stride;
43 43
}
44 44
45 45
/// Reset the vector to empty (does not clear memory).
46 -
export fn reset(vec: *mut RawVec) {
46 +
export fn reset(vec: &mut RawVec) {
47 47
    set vec.len = 0;
48 48
}
49 49
50 50
/// Get a pointer to the element at the given index.
51 51
///
52 52
/// Returns nil if index is out of bounds.
53 -
export fn get(vec: *RawVec, index: u32) -> ?*opaque {
53 +
export unsafe fn get(vec: &RawVec, index: u32) -> ?*unsafe opaque {
54 54
    if index >= vec.len {
55 55
        return nil;
56 56
    }
57 57
    let offset: u32 = index * vec.stride;
58 -
    let ptr: *u8 = &vec.data[offset];
58 +
    let ptr: *unsafe u8 = &vec.data[offset];
59 59
60 -
    return ptr as *opaque;
60 +
    return ptr as *unsafe opaque;
61 61
}
62 62
63 63
/// Push an element onto the end of the vector.
64 64
///
65 65
/// Returns false if the vector is at capacity.
66 -
export fn push(vec: *mut RawVec, elem: *opaque) -> bool {
66 +
export unsafe fn push(vec: &mut RawVec, elem: &opaque) -> bool {
67 67
    if vec.len >= capacity(vec) {
68 68
        return false;
69 69
    }
70 70
    let off: u32 = vec.len * vec.stride;
71 -
    let dst: *mut u8 = &mut vec.data[off];
72 -
    let src: *u8 = elem as *u8;
73 -
74 -
    copyBytes(dst, src, vec.stride);
71 +
    copyBytes(&mut vec.data[off..off + vec.stride], @sliceOf(elem as &u8, vec.stride));
75 72
    set vec.len += 1;
76 73
77 74
    return true;
78 75
}
79 76
80 77
/// Pop an element from the end of the vector.
81 78
///
82 79
/// Copies the element into the provided output pointer.
83 80
/// Returns false if the vector is empty.
84 -
export fn pop(vec: *mut RawVec, out: *mut opaque) -> bool {
81 +
export fn pop(vec: &mut RawVec, out: &mut opaque) -> bool {
85 82
    if vec.len == 0 {
86 83
        return false;
87 84
    }
88 85
    set vec.len -= 1;
89 86
90 87
    let off: u32 = vec.len * vec.stride;
91 -
    let src: *u8 = &vec.data[off];
92 -
    let dst: *mut u8 = out as *mut u8;
93 -
94 -
    copyBytes(dst, src, vec.stride);
88 +
    copyBytes(@sliceOf(out as &mut u8, vec.stride), &vec.data[off..off + vec.stride]);
95 89
96 90
    return true;
97 91
}
98 92
99 93
/// Set the element at the given index.
100 94
///
101 95
/// Returns false if index is out of bounds.
102 -
export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool {
96 +
export fn put(vec: &mut RawVec, index: u32, elem: &opaque) -> bool {
103 97
    if index >= vec.len {
104 98
        return false;
105 99
    }
106 100
    let off: u32 = index * vec.stride;
107 -
    let dst: *mut u8 = &mut vec.data[off];
108 -
    let src: *u8 = elem as *u8;
109 -
110 -
    copyBytes(dst, src, vec.stride);
101 +
    copyBytes(&mut vec.data[off..off + vec.stride], @sliceOf(elem as &u8, vec.stride));
111 102
112 103
    return true;
113 104
}
114 105
115 106
/// Copy bytes from source to destination.
116 -
fn copyBytes(dst: *mut u8, src: *u8, count: u32) {
117 -
    for i in 0..count {
118 -
        set *(dst + i) = *(src + i);
107 +
fn copyBytes(dst: &mut [u8], src: &[u8]) {
108 +
    assert dst.len == src.len;
109 +
    for i in 0..src.len {
110 +
        set dst[i] = src[i];
119 111
    }
120 112
}
seed/radiance.rv64 +0 -0

Binary file changed.

seed/radiance.rv64.git +1 -1
1 -
1e2fdea952350036a4172f567442e608555094b9e18fa69f1fb615453d5352b6
1 +
55e5d92edb60a10e44c67ba6bb5c2cc19f89ce22fd9c3c1c38c23fb2df718814
seed/update +1 -1
39 39
# ---------------------------------------------------------------------------
40 40
# Command line flags for Radiance compiler
41 41
# ---------------------------------------------------------------------------
42 42
43 43
STD_MODS="$(sed 's/^/-mod /' std.lib | tr '\n' ' ')"
44 -
OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad -entry radiance"
44 +
OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad -mod compiler/radiance/codegen.rad -entry radiance"
45 45
46 46
# ---------------------------------------------------------------------------
47 47
# Emulator settings
48 48
# ---------------------------------------------------------------------------
49 49
test/runner.rad +29 -16
92 92
    }
93 93
    return &line[..end];
94 94
}
95 95
96 96
/// Get next line from string at offset. Returns the line and updates offset past newline.
97 -
fn nextLine(s: *[u8], offset: *mut u32) -> *[u8] {
97 +
fn nextLine(s: *[u8], offset: &mut u32) -> *[u8] {
98 98
    let start = *offset;
99 99
    let mut i = start;
100 100
101 101
    while i < s.len and s[i] <> '\n' {
102 102
        set i += 1;
143 143
}
144 144
145 145
/// Derive the `.ril` path from a `.rad` source path. Returns nil if the path
146 146
/// does not end in `.rad` or the buffer is too small. The result is
147 147
/// null-terminated for use with syscalls.
148 -
fn deriveRilPath(sourcePath: *[u8], buf: *mut [u8]) -> ?*[u8] {
148 +
fn deriveRilPath(sourcePath: *[u8], buf: &mut [u8]) -> ?u32 {
149 149
    let len = sourcePath.len;
150 150
    if len < SOURCE_EXT.len {
151 151
        return nil;
152 152
    }
153 153
    let extStart = len - SOURCE_EXT.len;
165 165
    try mem::copy(&mut buf[extStart..len], SNAPSHOT_EXT) catch {
166 166
        return nil;
167 167
    };
168 168
    set buf[len] = 0;
169 169
170 -
    return &buf[..len];
170 +
    return len;
171 171
}
172 172
173 173
/// Write a self-contained RV64 image containing text and data sections.
174 -
fn writeImage(code: *[u32], roData: *[u8], rwData: *[u8], path: *[u8]) -> bool {
174 +
unsafe fn writeImage(
175 +
    code: *[u32],
176 +
    roData: *[u8],
177 +
    rwData: *[u8],
178 +
    path: *[u8]
179 +
) -> bool {
175 180
    let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len);
176 -
    let headerWords = &header[..];
177 -
    let headerBytes = @sliceOf(headerWords.ptr as *u8, headerWords.len * rv64::WORD_SIZE as u32);
181 +
    let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32);
178 182
    let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32);
179 183
180 -
    return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]);
184 +
    let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420);
185 +
    if fd < 0 { return false; }
186 +
    let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes)
187 +
        and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData);
188 +
    let closed = unix::close(fd) == 0;
189 +
    return written and closed;
181 190
}
182 191
183 -
fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool {
184 -
    let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else {
192 +
unsafe fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool {
193 +
    let sourceLen = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else {
185 194
        io::printError("error: could not read source: ");
186 195
        io::printError(sourcePath);
187 196
        io::printError("\n");
188 197
        return false;
189 198
    };
190 199
200 +
    let source = &SOURCE_BUF[..sourceLen];
191 201
    let mut arena = alloc::new(&mut AST_ARENA_STORAGE[..]);
192 202
    let program = try asm::assemble(
193 203
        asm::scanner::SourceKind::File { path: sourcePath },
194 204
        source,
195 205
        &mut ASM_TEXT_STORAGE[..],
212 222
    }
213 223
    return true;
214 224
}
215 225
216 226
/// Run a single IL snapshot test case. Returns `true` on success.
217 -
fn runTest(sourcePath: *[u8]) -> bool {
227 +
unsafe fn runTest(sourcePath: *[u8]) -> bool {
218 228
    // Path buffer.
219 229
    let mut rilPathBuf: [u8; MAX_PATH_LEN] = undefined;
220 -
    let mut pkgScope: resolver::Scope = undefined;
230 +
    static pkgScope: resolver::Scope = undefined;
221 231
222 232
    // Derive .ril path from source path.
223 -
    let rilPath = deriveRilPath(sourcePath, &mut rilPathBuf[..]) else {
233 +
    let rilPathLen = deriveRilPath(sourcePath, &mut rilPathBuf[..]) else {
224 234
        io::print("error: invalid source path (must end in .rad): ");
225 235
        io::printLn(sourcePath);
226 236
        return false;
227 237
    };
228 238
229 239
    // Read expected IL.
230 -
    let expected = unix::readFile(rilPath, &mut EXPECTED_BUF[..]) else {
240 +
    let expectedLen = unix::readFile(&rilPathBuf[..rilPathLen], &mut EXPECTED_BUF[..]) else {
231 241
        io::print("error: could not read expected IL: ");
232 -
        io::printLn(rilPath);
242 +
        io::printLn(&rilPathBuf[..rilPathLen]);
233 243
        return false;
234 244
    };
235 245
236 246
    // Read source file.
237 -
    let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else {
247 +
    let sourceLen = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else {
238 248
        io::print("error: could not read source: ");
239 249
        io::printLn(sourcePath);
240 250
        return false;
241 251
    };
242 252
253 +
    let expected = &EXPECTED_BUF[..expectedLen];
254 +
    let source = &SOURCE_BUF[..sourceLen];
255 +
243 256
    // Parse source.
244 257
    let mut astArena = ast::nodeArena(&mut AST_ARENA_STORAGE[..]);
245 258
    let root = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch {
246 259
        io::printLn("error: parsing failed");
247 260
        return false;
287 300
288 301
    return true;
289 302
}
290 303
291 304
/// Run a single test specified as an argument.
292 -
@default fn main(env: *sys::Env) -> i32 {
305 +
@default unsafe fn main(env: *sys::Env) -> i32 {
293 306
    let args = env.args;
294 307
295 308
    if args.len == 4 and mem::eq(args[1], "assemble") {
296 309
        if assembleBinary(args[2], args[3]) {
297 310
            return 0;
test/tests/array.aggregate.stride.rad +1 -1
8 8
fn arrayOfRecords(arr: [Point; 3], idx: u32) -> i32 {
9 9
    return arr[idx].x;
10 10
}
11 11
12 12
/// Take address of record in array.
13 -
fn addressOfRecord(arr: [Point; 3], idx: u32) -> *Point {
13 +
unsafe fn addressOfRecord(arr: [Point; 3], idx: u32) -> *unsafe Point {
14 14
    return &arr[idx];
15 15
}
16 16
17 17
/// Array of arrays (16 bytes per element).
18 18
fn arrayOfArrays(arr: [[i32; 4]; 3], idx: u32) -> i32 {
test/tests/array.slice.full.rad +1 -1
1 1
/// Creates a slice from an array with full range.
2 -
fn sliceFull(a: [i32; 4]) -> *[i32] {
2 +
unsafe fn sliceFull(a: [i32; 4]) -> *unsafe [i32] {
3 3
    return &a[..];
4 4
}
test/tests/array.slice.gen.end.rad +2 -2
1 1
//! returns: 0
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut arr: [i32; 4] = [1, 2, 3, 4];
4 -
    let mut slice: *[i32] = &arr[..2];
4 +
    let mut slice: *unsafe [i32] = &arr[..2];
5 5
6 6
    return 0;
7 7
}
test/tests/array.slice.gen.index.rad +2 -2
1 1
//! returns: 0
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut arr: [i32; 4] = [1, 2, 3, 4];
4 -
    let mut slice: *[i32] = &arr[1..];
4 +
    let mut slice: *unsafe [i32] = &arr[1..];
5 5
6 6
    return (slice[1]) - 3;
7 7
}
test/tests/array.slice.gen.open.rad +2 -2
1 1
//! returns: 0
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut arr: [i32; 4] = [1, 2, 3, 4];
4 -
    let mut slice: *[i32] = &arr[..];
4 +
    let mut slice: *unsafe [i32] = &arr[..];
5 5
6 6
    return 0;
7 7
}
test/tests/array.slice.gen.start.end.rad +2 -2
1 1
//! returns: 0
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut arr: [i32; 4] = [1, 2, 3, 4];
4 -
    let mut slice: *[i32] = &arr[1..3];
4 +
    let mut slice: *unsafe [i32] = &arr[1..3];
5 5
6 6
    return 0;
7 7
}
test/tests/array.slice.gen.start.rad +2 -2
1 1
//! returns: 0
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut arr: [i32; 4] = [1, 2, 3, 4];
4 -
    let mut slice: *[i32] = &arr[2..];
4 +
    let mut slice: *unsafe [i32] = &arr[2..];
5 5
6 6
    return 0;
7 7
}
test/tests/array.slice.openend.rad +1 -1
1 1
/// Creates a slice from an array with open end bound.
2 -
fn sliceOpenEnd(a: [i32; 4], start: u32) -> *[i32] {
2 +
unsafe fn sliceOpenEnd(a: [i32; 4], start: u32) -> *unsafe [i32] {
3 3
    return &a[start..];
4 4
}
test/tests/array.slice.openstart.rad +1 -1
1 1
/// Creates a slice from an array with open start bound.
2 -
fn sliceOpenStart(a: [i32; 4], end: u32) -> *[i32] {
2 +
unsafe fn sliceOpenStart(a: [i32; 4], end: u32) -> *unsafe [i32] {
3 3
    return &a[..end];
4 4
}
test/tests/array.slice.rad +6 -6
1 1
//! returns: 0
2 2
//! Test array slicing with various ranges.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
6 6
7 -
    let slice1: *[i32] = &arr[..];
8 -
    let slice2: *[i32] = &arr[1..4];
9 -
    let slice3: *[i32] = &arr[..3];
10 -
    let slice4: *[i32] = &arr[2..];
7 +
    let slice1: *unsafe [i32] = &arr[..];
8 +
    let slice2: *unsafe [i32] = &arr[1..4];
9 +
    let slice3: *unsafe [i32] = &arr[..3];
10 +
    let slice4: *unsafe [i32] = &arr[2..];
11 11
12 12
    assert slice1.len == 5;
13 13
    assert slice2.len == 3;
14 14
    assert slice3.len == 3;
15 15
    assert slice4.len == 3;
16 16
17 -
    let slice5: *[i32] = slice3;
17 +
    let slice5: *unsafe [i32] = slice3;
18 18
19 19
    assert slice5.len == 3;
20 20
    assert slice5.ptr == slice3.ptr;
21 21
22 22
    let sum1: i32 = slice1[0] + slice1[4];  // 6
test/tests/bool.comparison.slice.rad +45 -45
1 1
//! returns: 0
2 -
fn memEq(a: *[u8], b: *[u8]) -> bool {
2 +
fn memEq(a: &[u8], b: &[u8]) -> bool {
3 3
    if a.len <> b.len {
4 4
        return false;
5 5
    }
6 6
    for i in 0..a.len {
7 7
        if a[i] <> b[i] {
13 13
14 14
fn sliceU8(input: *[u8]) -> *[u8] {
15 15
    return input;
16 16
}
17 17
18 -
fn sliceI32(input: *[i32]) -> *[i32] {
18 +
fn sliceI32(input: *unsafe [i32]) -> *unsafe [i32] {
19 19
    return input;
20 20
}
21 21
22 -
fn sliceEqualU32(a: *[u32], b: *[u32]) -> bool {
22 +
fn sliceEqualU32(a: &[u32], b: &[u32]) -> bool {
23 23
    if a.len <> b.len {
24 24
        return false;
25 25
    }
26 26
    for i in 0..a.len {
27 27
        if a[i] <> b[i] {
29 29
        }
30 30
    }
31 31
    return true;
32 32
}
33 33
34 -
fn sliceEqualI32(a: *[i32], b: *[i32]) -> bool {
34 +
fn sliceEqualI32(a: &[i32], b: &[i32]) -> bool {
35 35
    if a.len <> b.len {
36 36
        return false;
37 37
    }
38 38
    for i in 0..a.len {
39 39
        if a[i] <> b[i] {
41 41
        }
42 42
    }
43 43
    return true;
44 44
}
45 45
46 -
fn sliceEqual1() -> bool {
46 +
unsafe fn sliceEqual1() -> bool {
47 47
    let a1: [u32; 3] = [9, 42, 3];
48 48
49 -
    let s1: *[u32] = &a1[..];
50 -
    let s2: *[u32] = &a1[..];
49 +
    let s1: *unsafe [u32] = &a1[..];
50 +
    let s2: *unsafe [u32] = &a1[..];
51 51
52 -
    return s1 == s2 and sliceEqualU32(s1, s2);
52 +
    return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]);
53 53
}
54 54
55 -
fn sliceEqual2() -> bool {
55 +
unsafe fn sliceEqual2() -> bool {
56 56
    let a1: [u32; 3] = [9, 42, 3];
57 57
58 -
    let s1: *[u32] = &a1[1..];
59 -
    let s2: *[u32] = &a1[1..];
58 +
    let s1: *unsafe [u32] = &a1[1..];
59 +
    let s2: *unsafe [u32] = &a1[1..];
60 60
61 -
    return s1 == s2 and sliceEqualU32(s1, s2);
61 +
    return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]);
62 62
}
63 63
64 -
fn sliceEqual3() -> bool {
64 +
unsafe fn sliceEqual3() -> bool {
65 65
    let a1: [u32; 3] = [9, 42, 3];
66 66
67 -
    let s1: *[u32] = &a1[..1];
68 -
    let s2: *[u32] = &a1[..1];
67 +
    let s1: *unsafe [u32] = &a1[..1];
68 +
    let s2: *unsafe [u32] = &a1[..1];
69 69
70 -
    return s1 == s2 and sliceEqualU32(s1, s2);
70 +
    return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]);
71 71
}
72 72
73 -
fn sliceEqual4() -> bool {
73 +
unsafe fn sliceEqual4() -> bool {
74 74
    let a1: [u32; 3] = [9, 42, 3];
75 75
76 -
    let s1: *[u32] = &a1[2..];
77 -
    let s2: *[u32] = &a1[2..];
76 +
    let s1: *unsafe [u32] = &a1[2..];
77 +
    let s2: *unsafe [u32] = &a1[2..];
78 78
79 -
    return s1 == s2 and sliceEqualU32(s1, s2);
79 +
    return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]);
80 80
}
81 81
82 -
fn sliceNotEqualSameArray1() -> bool {
82 +
unsafe fn sliceNotEqualSameArray1() -> bool {
83 83
    let a1: [u32; 3] = [42, 8, 3];
84 84
85 -
    let s1: *[u32] = &a1[..2];
86 -
    let s2: *[u32] = &a1[1..3];
85 +
    let s1: *unsafe [u32] = &a1[..2];
86 +
    let s2: *unsafe [u32] = &a1[1..3];
87 87
88 -
    return s1 <> s2 and not sliceEqualU32(s1, s2);
88 +
    return s1 <> s2 and not sliceEqualU32(&s1[..], &s2[..]);
89 89
}
90 90
91 -
fn sliceNotEqualSameArray2() -> bool {
91 +
unsafe fn sliceNotEqualSameArray2() -> bool {
92 92
    let a1: [u32; 3] = [42, 8, 3];
93 93
94 -
    let s1: *[u32] = &a1[..2];
95 -
    let s2: *[u32] = &a1[..3];
94 +
    let s1: *unsafe [u32] = &a1[..2];
95 +
    let s2: *unsafe [u32] = &a1[..3];
96 96
97 -
    return s1 <> s2 and not sliceEqualU32(s1, s2);
97 +
    return s1 <> s2 and not sliceEqualU32(&s1[..], &s2[..]);
98 98
}
99 99
100 -
fn sliceEqualDifferentArray() -> bool {
100 +
unsafe fn sliceEqualDifferentArray() -> bool {
101 101
    let a1: [u32; 3] = [42, 8, 3];
102 102
    let a2: [u32; 3] = [42, 8, 3];
103 103
104 -
    let s1: *[u32] = &a1[..];
105 -
    let s2: *[u32] = &a2[..];
104 +
    let s1: *unsafe [u32] = &a1[..];
105 +
    let s2: *unsafe [u32] = &a2[..];
106 106
107 -
    return sliceEqualU32(s1, s2) and s1 <> s2;
107 +
    return sliceEqualU32(&s1[..], &s2[..]) and s1 <> s2;
108 108
}
109 109
110 110
fn sliceEqualString1() -> bool {
111 111
    let s1: *[u8] = "ABC";
112 112
    let s2: *[u8] = "ABC";
113 113
114 -
    return memEq(s1, s2);
114 +
    return memEq(&s1[..], s2);
115 115
}
116 116
117 117
fn sliceEqualString2() -> bool {
118 118
    let s1: *[u8] = "ABC";
119 119
    let s2: *[u8] = "DEF";
120 120
121 -
    return not memEq(s1, s2);
121 +
    return not memEq(&s1[..], s2);
122 122
}
123 123
124 -
fn sliceEqualString3() -> bool {
124 +
unsafe fn sliceEqualString3() -> bool {
125 125
    let a1: [u8; 3] = ['A', 'B', 'C'];
126 -
    let s1: *[u8] = &a1[..];
126 +
    let s1: *unsafe [u8] = &a1[..];
127 127
    let s2: *[u8] = "ABC";
128 128
129 -
    return memEq(s1, s2)
130 -
       and memEq(s1, "ABC")
129 +
    return memEq(&s1[..], s2)
130 +
       and memEq(&s1[..], "ABC")
131 131
       and memEq(&a1[..], "ABC");
132 132
}
133 133
134 134
fn sliceEqualString4() -> bool {
135 135
    let s1: *[u8] = "ABC";
136 136
    let s2: *[u8] = &['A', 'B', 'C'];
137 137
138 -
    return memEq(s1, s2);
138 +
    return memEq(&s1[..], s2);
139 139
}
140 140
141 141
fn sliceEqualString5() -> bool {
142 142
    let s1: *[u8] = "ABC";
143 143
144 144
    // Sub-slicing a slice currently trips a separate compiler bug.
145 145
    // Keep this check focused on slice equality behavior itself.
146 -
    return memEq(s1, "ABC");
146 +
    return memEq(&s1[..], "ABC");
147 147
}
148 148
149 149
fn sliceNotEqualU8() -> bool {
150 150
    let a: [u8; 3] = [1, 2, 3];
151 151
    let b: [u8; 3] = [1, 2, 4];
158 158
       and not memEq(&a[..], &a[1..])
159 159
       and not memEq(&a[..], &b[..])
160 160
       and not memEq(&a[..], &[1, 3, 3]);
161 161
}
162 162
163 -
fn sliceNotEqualU16() -> bool {
163 +
unsafe fn sliceNotEqualU16() -> bool {
164 164
    let a: [u16; 3] = [1, 2, 3];
165 165
    let b: [u16; 3] = [1, 2, 4];
166 -
    let s: *[u16] = &[1, 2, 3];
166 +
    let s: *unsafe [u16] = &[1, 2, 3];
167 167
168 168
    return s <> &[1, 2, 4]
169 169
       and s <> &[1, 0, 3]
170 170
       and &a[..] <> &a[1..]
171 171
       and &a[..] <> &b[..]
172 172
       and &a[..] <> &[1, 3, 3];
173 173
}
174 174
175 -
fn sliceReturnEqual() -> bool {
175 +
unsafe fn sliceReturnEqual() -> bool {
176 176
    return memEq(sliceU8("ABC"), "ABC")
177 -
       and sliceEqualI32(sliceI32(&[1, 2, 3]), &[1, 2, 3]);
177 +
       and sliceEqualI32(&sliceI32(&[1, 2, 3])[..], &[1, 2, 3]);
178 178
}
179 179
180 -
@default fn main() -> i32 {
180 +
@default unsafe fn main() -> i32 {
181 181
    assert sliceEqual1();
182 182
    assert sliceEqual2();
183 183
    assert sliceEqual3();
184 184
    assert sliceEqual4();
185 185
    assert sliceEqualString1();
test/tests/bool.comparison.slice.record.gen.rad +10 -10
4 4
    x: i32,
5 5
    y: i32,
6 6
}
7 7
8 8
record Line: Copy {
9 -
    points: *[Point],
9 +
    points: *unsafe [Point],
10 10
}
11 11
12 -
fn pointsEqual(a: *[Point], b: *[Point]) -> bool {
12 +
fn pointsEqual(a: &[Point], b: &[Point]) -> bool {
13 13
    if a.len <> b.len {
14 14
        return false;
15 15
    }
16 16
    for i in 0..a.len {
17 17
        if a[i].x <> b[i].x or a[i].y <> b[i].y {
19 19
        }
20 20
    }
21 21
    return true;
22 22
}
23 23
24 -
fn lineEqual(a: Line, b: Line) -> bool {
25 -
    return pointsEqual(a.points, b.points);
24 +
unsafe fn lineEqual(a: Line, b: Line) -> bool {
25 +
    return pointsEqual(&a.points[..], &b.points[..]);
26 26
}
27 27
28 -
fn testSliceStructEqual() -> bool {
28 +
unsafe fn testSliceStructEqual() -> bool {
29 29
    let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
30 30
    let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
31 31
32 32
    return pointsEqual(&a[..], &b[..]);
33 33
}
34 34
35 -
fn testSliceStructNotEqualContent() -> bool {
35 +
unsafe fn testSliceStructNotEqualContent() -> bool {
36 36
    let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
37 37
    let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }];
38 38
39 39
    return not pointsEqual(&a[..], &b[..]);
40 40
}
41 41
42 -
fn testSliceStructNotEqualLength() -> bool {
42 +
unsafe fn testSliceStructNotEqualLength() -> bool {
43 43
    let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
44 44
    let b: [Point; 1] = [Point { x: 1, y: 2 }];
45 45
46 46
    return not pointsEqual(&a[..], &b[..]);
47 47
}
48 48
49 -
fn testStructWithSliceFieldEqual() -> bool {
49 +
unsafe fn testStructWithSliceFieldEqual() -> bool {
50 50
    let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
51 51
    let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
52 52
53 53
    let line1: Line = Line { points: &pts1[..] };
54 54
    let line2: Line = Line { points: &pts2[..] };
55 55
56 56
    return lineEqual(line1, line2);
57 57
}
58 58
59 -
fn testStructWithSliceFieldNotEqual() -> bool {
59 +
unsafe fn testStructWithSliceFieldNotEqual() -> bool {
60 60
    let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
61 61
    let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }];
62 62
63 63
    let line1: Line = Line { points: &pts1[..] };
64 64
    let line2: Line = Line { points: &pts2[..] };
65 65
66 66
    return not lineEqual(line1, line2);
67 67
}
68 68
69 -
@default fn main() -> i32 {
69 +
@default unsafe fn main() -> i32 {
70 70
    assert testSliceStructEqual();
71 71
    assert testSliceStructNotEqualContent();
72 72
    assert testSliceStructNotEqualLength();
73 73
    assert testStructWithSliceFieldEqual();
74 74
    assert testStructWithSliceFieldNotEqual();
test/tests/bool.comparison.slice.union.gen.rad +12 -12
9 9
    width: u32,
10 10
    height: u32,
11 11
}
12 12
13 13
union Shape: Copy {
14 -
    points(*[Point]),
15 -
    sizes(*[Size]),
14 +
    points(*unsafe [Point]),
15 +
    sizes(*unsafe [Size]),
16 16
}
17 17
18 -
fn pointsEqual(a: *[Point], b: *[Point]) -> bool {
18 +
fn pointsEqual(a: &[Point], b: &[Point]) -> bool {
19 19
    if a.len <> b.len {
20 20
        return false;
21 21
    }
22 22
    for i in 0..a.len {
23 23
        if a[i].x <> b[i].x or a[i].y <> b[i].y {
25 25
        }
26 26
    }
27 27
    return true;
28 28
}
29 29
30 -
fn sizesEqual(a: *[Size], b: *[Size]) -> bool {
30 +
fn sizesEqual(a: &[Size], b: &[Size]) -> bool {
31 31
    if a.len <> b.len {
32 32
        return false;
33 33
    }
34 34
    for i in 0..a.len {
35 35
        if a[i].width <> b[i].width or a[i].height <> b[i].height {
37 37
        }
38 38
    }
39 39
    return true;
40 40
}
41 41
42 -
fn shapeEqual(a: Shape, b: Shape) -> bool {
42 +
unsafe fn shapeEqual(a: Shape, b: Shape) -> bool {
43 43
    if let case Shape::points(pointsA) = a {
44 44
        if let case Shape::points(pointsB) = b {
45 -
            return pointsEqual(pointsA, pointsB);
45 +
            return pointsEqual(&pointsA[..], &pointsB[..]);
46 46
        }
47 47
        return false;
48 48
    }
49 49
    if let case Shape::sizes(sizesA) = a {
50 50
        if let case Shape::sizes(sizesB) = b {
51 -
            return sizesEqual(sizesA, sizesB);
51 +
            return sizesEqual(&sizesA[..], &sizesB[..]);
52 52
        }
53 53
        return false;
54 54
    }
55 55
    return false;
56 56
}
57 57
58 -
fn testEnumSliceEqual() -> bool {
58 +
unsafe fn testEnumSliceEqual() -> bool {
59 59
    let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
60 60
    let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
61 61
62 62
    let s1: Shape = Shape::points(&pts1[..]);
63 63
    let s2: Shape = Shape::points(&pts2[..]);
64 64
65 65
    return shapeEqual(s1, s2);
66 66
}
67 67
68 -
fn testEnumSliceNotEqual() -> bool {
68 +
unsafe fn testEnumSliceNotEqual() -> bool {
69 69
    let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
70 70
    let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }];
71 71
72 72
    let s1: Shape = Shape::points(&pts1[..]);
73 73
    let s2: Shape = Shape::points(&pts2[..]);
74 74
75 75
    return not shapeEqual(s1, s2);
76 76
}
77 77
78 -
fn testEnumSizesSliceEqual() -> bool {
78 +
unsafe fn testEnumSizesSliceEqual() -> bool {
79 79
    let sizes1: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }];
80 80
    let sizes2: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }];
81 81
82 82
    let s1: Shape = Shape::sizes(&sizes1[..]);
83 83
    let s2: Shape = Shape::sizes(&sizes2[..]);
84 84
85 85
    return shapeEqual(s1, s2);
86 86
}
87 87
88 -
fn testEnumDifferentVariants() -> bool {
88 +
unsafe fn testEnumDifferentVariants() -> bool {
89 89
    let pts: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
90 90
    let sizes: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }];
91 91
92 92
    let s1: Shape = Shape::points(&pts[..]);
93 93
    let s2: Shape = Shape::sizes(&sizes[..]);
94 94
95 95
    return not shapeEqual(s1, s2);
96 96
}
97 97
98 -
@default fn main() -> i32 {
98 +
@default unsafe fn main() -> i32 {
99 99
    assert testEnumSliceEqual();
100 100
    assert testEnumSliceNotEqual();
101 101
    assert testEnumSizesSliceEqual();
102 102
    assert testEnumDifferentVariants();
103 103
    return 0;
test/tests/bool.short.circuit.rad +1 -1
1 1
//! returns: 0
2 2
//! Test short-circuiting behavior of 'and' and 'or' operators.
3 -
fn modify(counter: *mut i32, ret: bool) -> bool {
3 +
fn modify(counter: &mut i32, ret: bool) -> bool {
4 4
    set *counter += 1;
5 5
    return ret;
6 6
}
7 7
8 8
@default fn main() -> i32 {
test/tests/builtin.sliceof.invalid.cap.rad +3 -3
1 1
//! returns: 133
2 2
//! Test @sliceOf runtime validation for len > cap.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let mut arr: [i32; 4] = [10, 20, 30, 40];
6 -
    let ptr: *i32 = &arr[0];
7 -
    let slice: *[i32] = @sliceOf(ptr, 4, 3);
6 +
    let ptr: *unsafe i32 = &arr[0];
7 +
    let slice: *unsafe [i32] = @sliceOf(ptr, 4, 3);
8 8
9 9
    return slice.len as i32;
10 10
}
test/tests/builtin.sliceof.mut.rad +3 -3
1 1
//! returns: 0
2 2
// Test the @sliceOf builtin with mutable slice.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let mut arr: [i32; 4] = [10, 20, 30, 40];
6 6
7 7
    // Get a mutable pointer to the first element
8 -
    let ptr: *mut i32 = &mut arr[0];
8 +
    let ptr: *unsafe mut i32 = &mut arr[0];
9 9
10 10
    // Create a mutable slice from the pointer and length
11 -
    let slice: *mut [i32] = @sliceOf(ptr, 4);
11 +
    let slice: *unsafe mut [i32] = @sliceOf(ptr, 4);
12 12
13 13
    // Double each element via the slice
14 14
    set slice[0] *= 2;
15 15
    set slice[1] *= 2;
16 16
    set slice[2] *= 2;
test/tests/builtin.sliceof.rad +3 -3
1 1
//! returns: 100
2 2
// Test @sliceOf builtin: create a slice from a pointer and length.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let mut arr: [i32; 4] = [10, 20, 30, 40];
6 6
7 7
    // Get a pointer to the first element
8 -
    let ptr: *i32 = &arr[0];
8 +
    let ptr: *unsafe i32 = &arr[0];
9 9
10 10
    // Create a slice from the pointer and length
11 -
    let slice: *[i32] = @sliceOf(ptr, 4);
11 +
    let slice: *unsafe [i32] = @sliceOf(ptr, 4);
12 12
13 13
    // Access elements via the slice and sum them
14 14
    let mut sum: i32 = 0;
15 15
    set sum += slice[0];
16 16
    set sum += slice[1];
test/tests/call.aggregate.arg.snapshot.rad +1 -1
5 5
record Pair: Copy {
6 6
    x: i32,
7 7
    y: i32,
8 8
}
9 9
10 -
fn mutate(pair: *mut Pair) -> i32 {
10 +
fn mutate(pair: &mut Pair) -> i32 {
11 11
    set pair.x = 99;
12 12
    return 0;
13 13
}
14 14
15 15
fn first(pair: Pair, ignored: i32) -> i32 {
test/tests/call.clobber.rad +3 -3
1 1
//! returns: 0
2 2
//! Test that values live across function calls are not clobbered.
3 -
fn modify(counter: *mut i32, ret: bool) -> bool {
3 +
unsafe fn modify(counter: *unsafe mut i32, ret: bool) -> bool {
4 4
    set *counter += 1;
5 5
    return ret;
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let mut x: i32 = 0;
10 10
    let r: bool = modify(&mut x, false);
11 11
    assert x == 1;
12 12
    assert not r;
13 13
14 14
    // Pointer live across call.
15 15
    let mut y: i32 = 42;
16 -
    let p: *mut i32 = &mut y;
16 +
    let p: *unsafe mut i32 = &mut y;
17 17
    let r2: bool = modify(p, true);
18 18
    assert *p == 43;
19 19
    assert r2;
20 20
    return 0;
21 21
}
test/tests/compound.assign.index.once.rad +1 -1
1 1
//! returns: 0
2 2
//! Compound assignment evaluates a side-effecting lvalue exactly once.
3 3
4 -
fn nextIndex(calls: *mut u32) -> u32 {
4 +
fn nextIndex(calls: &mut u32) -> u32 {
5 5
    let index = *calls;
6 6
    set *calls += 1;
7 7
    return index;
8 8
}
9 9
test/tests/compound.assign.rad +2 -2
1 1
//! returns: 0
2 2
/// Test compound assignment operators (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=).
3 -
@default fn main() -> i32 {
3 +
@default unsafe fn main() -> i32 {
4 4
    let mut a: i32 = 10;
5 5
6 6
    // Test +=
7 7
    set a += 5;
8 8
    assert a == 15;
50 50
    set arr[1] += 5;
51 51
    assert arr[1] == 25;
52 52
53 53
    // Test compound assignment with pointer dereference.
54 54
    let mut val: i32 = 100;
55 -
    let p: *mut i32 = &mut val;
55 +
    let p: *unsafe mut i32 = &mut val;
56 56
    set *p += 50;
57 57
    assert val == 150;
58 58
59 59
    // Test chained compound assignments.
60 60
    let mut x: i32 = 1;
test/tests/cond.match.guard.regalloc.rad +3 -3
18 18
    span1: u32,
19 19
    span2: u32,
20 20
    kind: Kind,
21 21
}
22 22
23 -
fn getPath(node: *Node, buf: *mut [*[u8]]) -> *[*[u8]] {
24 -
    let mut out: *[*[u8]] = &[];
23 +
unsafe fn getPath(node: &Node, buf: &mut [*[u8]]) -> *unsafe [*[u8]] {
24 +
    let mut out: *unsafe [*[u8]] = &[];
25 25
26 26
    match node.kind {
27 27
        case Kind::Name(name) if name.len > 0 => {
28 28
            set buf[0] = name;
29 29
            set out = &buf[..1];
34 34
        else => {}
35 35
    }
36 36
    return out;
37 37
}
38 38
39 -
@default fn main() -> i32 {
39 +
@default unsafe fn main() -> i32 {
40 40
    let n = Node {
41 41
        id: 0, span1: 0, span2: 5,
42 42
        kind: Kind::Name("Hello"),
43 43
    };
44 44
    let mut buffer: [*[u8]; 4] = undefined;
test/tests/edge.cases.2.rad +1 -1
3 3
4 4
export record Scanner: Copy {
5 5
    source: *[u8],
6 6
}
7 7
8 -
fn peek(s: *Scanner) -> ?u8 {
8 +
fn peek(s: &Scanner) -> ?u8 {
9 9
    if 0 + 0 >= s.source.len {
10 10
        return nil;
11 11
    }
12 12
    return 'a';
13 13
}
test/tests/edge.cases.3.rad +3 -3
4 4
export record Scanner: Copy {
5 5
    source: *[u8],
6 6
    cursor: u32,
7 7
}
8 8
9 -
fn isEof(s: *Scanner) -> bool {
9 +
fn isEof(s: &Scanner) -> bool {
10 10
    return s.cursor >= s.source.len;
11 11
}
12 12
13 -
export fn current(s: *Scanner) -> ?u8 {
13 +
export fn current(s: &Scanner) -> ?u8 {
14 14
    if isEof(s) {
15 15
        return nil;
16 16
    }
17 17
    return s.source[s.cursor];
18 18
}
19 19
20 -
fn peek(s: *Scanner) -> ?u8 {
20 +
fn peek(s: &Scanner) -> ?u8 {
21 21
    if s.cursor + 1 >= s.source.len {
22 22
        return nil;
23 23
    }
24 24
    return s.source[s.cursor + 1];
25 25
}
test/tests/edge.cases.4.rad +4 -4
18 18
19 19
record Node: Copy {
20 20
    value: NodeValue,
21 21
}
22 22
23 -
fn node(nodes: *mut Node, count: *mut u32, value: NodeValue) -> *Node {
23 +
unsafe fn node(nodes: *unsafe mut Node, count: *unsafe mut u32, value: NodeValue) -> *unsafe Node {
24 24
    let index = *count;
25 25
    let slot = nodes + index;
26 26
    set *slot = Node { value };
27 27
    set *count = index + 1;
28 28
    return slot;
29 29
}
30 30
31 -
fn nodeTypeInt(nodes: *mut Node, count: *mut u32, width: u8, sign: Signedness) -> *Node {
31 +
unsafe fn nodeTypeInt(nodes: *unsafe mut Node, count: *unsafe mut u32, width: u8, sign: Signedness) -> *unsafe Node {
32 32
    return node(nodes, count, NodeValue::TypeSig(
33 33
        TypeSig::Integer { width, sign }
34 34
    ));
35 35
}
36 36
37 -
@default fn main() -> i32 {
37 +
@default unsafe fn main() -> i32 {
38 38
    let direct = TypeSig::Integer {
39 39
        width: 4,
40 40
        sign: Signedness::Signed,
41 41
    };
42 42
    let case TypeSig::Integer { width: w1, .. } = direct
43 43
        else return 50;
44 44
45 45
    assert w1 == 4;
46 46
    let mut nodes: [Node; 2] = undefined;
47 -
    let nodesPtr: *mut Node = &mut nodes[0];
47 +
    let nodesPtr: *unsafe mut Node = &mut nodes[0];
48 48
    let mut count: u32 = 0;
49 49
    let typeNode = nodeTypeInt(nodesPtr, &mut count, 4, Signedness::Signed);
50 50
    let case NodeValue::TypeSig(sig) = typeNode.value
51 51
        else return 40;
52 52
test/tests/edge.cases.rad +1 -1
2 2
3 3
export record Scanner: Copy {
4 4
    source: *[u8],
5 5
}
6 6
7 -
fn peek(s: *Scanner)  -> i32 {
7 +
fn peek(s: &Scanner)  -> i32 {
8 8
    assert 0 + 0 <= s.source.len;
9 9
    return 0;
10 10
}
11 11
12 12
@default fn main() -> i32 {
test/tests/error.slice.bounds.rad +2 -2
1 1
//! returns: 133
2 2
//! Test slice bounds checking with runtime EBREAK.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let arr: [i32; 3] = [1, 2, 3];
6 -
    let slice: *[i32] = &arr[..];
6 +
    let slice: *unsafe [i32] = &arr[..];
7 7
    let value: i32 = slice[3];
8 8
9 9
    return value;
10 10
}
test/tests/error.try.rad +3 -3
70 70
        throw TestError::Bust;
71 71
    }
72 72
    return 77;
73 73
}
74 74
75 -
fn catchReturn(flag: *mut u32, fail: bool) -> bool {
75 +
fn catchReturn(flag: &mut u32, fail: bool) -> bool {
76 76
    let value: u32 = try maybeReturn(fail) catch {
77 77
        return true;
78 78
    };
79 79
    set *flag = value;
80 80
    return false;
96 96
        set idx += 1;
97 97
    }
98 98
    return total;
99 99
}
100 100
101 -
fn structSuccess(state: *mut ResultSink) -> u32 throws (TestError) {
101 +
fn structSuccess(state: &mut ResultSink) -> u32 throws (TestError) {
102 102
    let value: u32 = try returnsOk();
103 103
    set state.last = value;
104 104
    set state.count += 1;
105 105
    return value;
106 106
}
107 107
108 -
fn structFailure(state: *mut ResultSink) -> u32 throws (TestError) {
108 +
fn structFailure(state: &mut ResultSink) -> u32 throws (TestError) {
109 109
    try returnsErr(1);
110 110
    set state.last = 999;
111 111
    return 999;
112 112
}
113 113
test/tests/field.aggregate.rad +2 -2
4 4
record Inner: Copy { x: i32, y: i32 }
5 5
union MaybeInt: Copy { None, Some(i32) }
6 6
7 7
record HasRecord: Copy { inner: Inner, z: i32 }
8 8
record HasUnion: Copy { maybe: MaybeInt, z: i32 }
9 -
record HasSlice: Copy { data: *[i32], z: i32 }
9 +
record HasSlice: Copy { data: *unsafe [i32], z: i32 }
10 10
11 11
/// Access a record field and read from it.
12 12
fn accessRecordField() -> i32 {
13 13
    let r = HasRecord { inner: Inner { x: 10, y: 20 }, z: 30 };
14 14
    return r.inner.y;
19 19
    let r = HasUnion { maybe: MaybeInt::Some(42), z: 99 };
20 20
    return r.maybe;
21 21
}
22 22
23 23
/// Access a slice field and get its length.
24 -
fn accessSliceField() -> u32 {
24 +
unsafe fn accessSliceField() -> u32 {
25 25
    let arr: [i32; 3] = [1, 2, 3];
26 26
    let r = HasSlice { data: &arr[..], z: 77 };
27 27
    return r.data.len;
28 28
}
test/tests/fn.callback.nested.rad +9 -5
15 15
        return n + 1;
16 16
    }
17 17
    return current;
18 18
}
19 19
20 -
fn maxRegCallback(reg: Reg, ctx: *mut opaque) {
21 -
    let max = ctx as *mut u32;
20 +
/// Update the maximum register count through a borrowed counter.
21 +
fn updateMax(reg: Reg, max: &mut u32) {
22 22
    set *max = maxRegNum(reg.n, *max);
23 23
}
24 24
25 -
fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) {
25 +
fn maxRegCallback(reg: Reg, ctx: &mut opaque) {
26 +
    updateMax(reg, ctx as &mut u32);
27 +
}
28 +
29 +
fn withReg(val: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) {
26 30
    if let case Val::Reg(r) = val {
27 31
        callback(r, ctx);
28 32
    }
29 33
}
30 34
31 -
fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) {
35 +
fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) {
32 36
    withReg(a, callback, ctx);
33 37
    withReg(b, callback, ctx);
34 38
    withReg(c, callback, ctx);
35 39
}
36 40
40 44
    forEachVal(
41 45
        Val::Reg(Reg { n: 0 }),
42 46
        Val::Reg(Reg { n: 5 }),
43 47
        Val::Reg(Reg { n: 2 }),
44 48
        maxRegCallback,
45 -
        &mut maxReg as *mut opaque
49 +
        &mut maxReg as &mut opaque
46 50
    );
47 51
48 52
    // maxReg should be 6 (max register 5 + 1)
49 53
    if maxReg == 6 {
50 54
        return 0;
test/tests/fn.unsafe.callbacks.rad added +30 -0
1 +
//! returns: 0
2 +
3 +
mod operations;
4 +
5 +
record Callback: Copy {
6 +
    /// Function called with a raw pointer to a live integer.
7 +
    invoke: unsafe fn(*unsafe u32) -> u32,
8 +
}
9 +
10 +
/// Call a safe function from a module that also exports unsafe functions.
11 +
fn readBorrow(value: &u32) -> u32 {
12 +
    return operations::readBorrow(value);
13 +
}
14 +
15 +
/// Return a constant without reading the pointer.
16 +
fn constantValue(value: *unsafe u32) -> u32 {
17 +
    return 7;
18 +
}
19 +
20 +
@default unsafe fn main() -> i32 {
21 +
    let value: u32 = 42;
22 +
    assert readBorrow(&value) == 42;
23 +
24 +
    let raw = Callback { invoke: operations::readRaw };
25 +
    assert raw.invoke(&value) == 42;
26 +
27 +
    let checked = Callback { invoke: constantValue };
28 +
    assert checked.invoke(&value) == 7;
29 +
    return 0;
30 +
}
test/tests/fn.unsafe.callbacks/operations.rad added +9 -0
1 +
/// Read an integer through a checked borrow.
2 +
export fn readBorrow(value: &u32) -> u32 {
3 +
    return *value;
4 +
}
5 +
6 +
/// Read an integer through a raw pointer that must remain valid for the call.
7 +
export unsafe fn readRaw(value: *unsafe u32) -> u32 {
8 +
    return *value;
9 +
}
test/tests/for.else.continue.rad +1 -1
4 4
record Field: Copy {
5 5
    name: ?*[u8],
6 6
    value: i32,
7 7
}
8 8
9 -
fn findField(fields: *[Field], target: *[u8]) -> ?i32 {
9 +
fn findField(fields: &[Field], target: &[u8]) -> ?i32 {
10 10
    for i in 0..fields.len {
11 11
        let name = fields[i].name
12 12
            else continue;
13 13
        if name.len == target.len {
14 14
            let mut eq = true;
test/tests/index.u8.rad +2 -2
1 1
//! returns: 0
2 2
3 -
@default fn main() -> i32 {
3 +
@default unsafe fn main() -> i32 {
4 4
    let arr: [i32; 4] = [10, 20, 42, 30];
5 5
6 6
    // u8 index.
7 7
    let idx8: u8 = 2;
8 8
    if arr[idx8] <> 42 {
21 21
    // Unsuffixed integer literal index.
22 22
    if arr[1] <> 20 {
23 23
        return 4;
24 24
    }
25 25
    // u8 index into slice.
26 -
    let s = &arr[..];
26 +
    let s: *unsafe [i32] = &arr[..];
27 27
    let si: u8 = 1;
28 28
    if s[si] <> 20 {
29 29
        return 5;
30 30
    }
31 31
    return 0;
test/tests/match.mutref.push.rad +4 -4
1 1
//! returns: 0
2 2
//! Test pushing items through a mutable reference obtained via match.
3 3
4 4
record U32List: Copy {
5 -
    data: *mut [u32],
5 +
    data: *unsafe mut [u32],
6 6
    len: u32,
7 7
}
8 8
9 9
union Sealed: Copy {
10 10
    No { items: U32List },
11 11
    Yes,
12 12
}
13 13
14 -
fn pushItem(list: *mut U32List, value: u32) {
14 +
unsafe fn pushItem(list: &mut U32List, value: u32) {
15 15
    set list.data[list.len] = value;
16 16
    set list.len += 1;
17 17
}
18 18
19 -
fn addToUnsealedBlock(state: *mut Sealed, value: u32) -> bool {
19 +
unsafe fn addToUnsealedBlock(state: &mut Sealed, value: u32) -> bool {
20 20
    match state {
21 21
        case Sealed::No { items } => {
22 22
            pushItem(items, value);
23 23
            return true;
24 24
        },
26 26
            return false;
27 27
        },
28 28
    }
29 29
}
30 30
31 -
@default fn main() -> i32 {
31 +
@default unsafe fn main() -> i32 {
32 32
    let mut buf: [u32; 8] = undefined;
33 33
    set buf[0] = 0;
34 34
    let mut state = Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } };
35 35
36 36
    assert addToUnsealedBlock(&mut state, 42);
test/tests/match.mutref.union.rad +1 -1
10 10
record Data: Copy {
11 11
    state: State,
12 12
    value: u32,
13 13
}
14 14
15 -
fn process(d: *mut Data) -> u32 {
15 +
fn process(d: &mut Data) -> u32 {
16 16
    match &mut d.state {
17 17
        case State::A { count } => {
18 18
            let c = *count;
19 19
            set *count = c + 1;
20 20
            return c;
test/tests/match.nested.deref.rad +12 -12
5 5
    A(i32),
6 6
    B,
7 7
}
8 8
9 9
union Outer: Copy {
10 -
    Some(*Inner),
10 +
    Some(*unsafe Inner),
11 11
    None,
12 12
}
13 13
14 14
/// Match nested union variant through pointer dereference in match/case.
15 -
fn matchDeref(o: Outer) -> i32 {
15 +
unsafe fn matchDeref(o: Outer) -> i32 {
16 16
    match o {
17 17
        case Outer::Some(Inner::A(x)) => {
18 18
            return x;
19 19
        }
20 20
        case Outer::Some(Inner::B) => {
25 25
        }
26 26
    }
27 27
}
28 28
29 29
/// If-let-case with auto-deref nested pattern.
30 -
fn ifLetDeref(o: Outer) -> i32 {
30 +
unsafe fn ifLetDeref(o: Outer) -> i32 {
31 31
    if let case Outer::Some(Inner::A(x)) = o {
32 32
        return x;
33 33
    }
34 34
    return 0;
35 35
}
36 36
37 37
/// Let-else with auto-deref nested pattern.
38 -
fn letElseDeref(o: Outer) -> i32 {
38 +
unsafe fn letElseDeref(o: Outer) -> i32 {
39 39
    let case Outer::Some(Inner::A(x)) = o
40 40
        else { return -1; };
41 41
    return x;
42 42
}
43 43
44 44
/// Record with pointer field and nested pattern through deref.
45 45
record Container: Copy {
46 -
    inner: *Inner,
46 +
    inner: *unsafe Inner,
47 47
    tag: i32,
48 48
}
49 49
50 50
union Boxed: Copy {
51 51
    Some { c: Container },
52 52
    None,
53 53
}
54 54
55 55
/// Nested record with auto-deref on a pointer field.
56 -
fn matchRecordDeref(b: Boxed) -> i32 {
56 +
unsafe fn matchRecordDeref(b: Boxed) -> i32 {
57 57
    match b {
58 58
        case Boxed::Some { c: Container { inner: Inner::A(val), tag } } => {
59 59
            return val + tag;
60 60
        }
61 61
        else => {
63 63
        }
64 64
    }
65 65
}
66 66
67 67
/// Auto-deref through pointer in if-let-case with record.
68 -
fn ifLetRecordDeref(b: Boxed) -> i32 {
68 +
unsafe fn ifLetRecordDeref(b: Boxed) -> i32 {
69 69
    if let case Boxed::Some { c: Container { inner: Inner::A(val), tag } } = b {
70 70
        return val + tag;
71 71
    }
72 72
    return 0;
73 73
}
74 74
75 75
/// Void variant through pointer deref.
76 -
fn matchDerefVoid(o: Outer) -> i32 {
76 +
unsafe fn matchDerefVoid(o: Outer) -> i32 {
77 77
    if let case Outer::Some(Inner::B) = o {
78 78
        return 1;
79 79
    }
80 80
    return 0;
81 81
}
82 82
83 83
/// Auto-deref with placeholder in nested pattern.
84 -
fn matchDerefPlaceholder(o: Outer) -> i32 {
84 +
unsafe fn matchDerefPlaceholder(o: Outer) -> i32 {
85 85
    if let case Outer::Some(Inner::A(_)) = o {
86 86
        return 1;
87 87
    }
88 88
    return 0;
89 89
}
93 93
    x: i32,
94 94
    y: i32,
95 95
}
96 96
97 97
union Holder: Copy {
98 -
    Ptr { p: *Point, z: i32 },
98 +
    Ptr { p: *unsafe Point, z: i32 },
99 99
    Empty,
100 100
}
101 101
102 -
fn derefRecordField(h: Holder) -> i32 {
102 +
unsafe fn derefRecordField(h: Holder) -> i32 {
103 103
    if let case Holder::Ptr { p: Point { x, y }, z } = h {
104 104
        return x + y + z;
105 105
    }
106 106
    return 0;
107 107
}
108 108
109 -
@default fn main() -> i32 {
109 +
@default unsafe fn main() -> i32 {
110 110
    let innerA = Inner::A(42);
111 111
    let innerB = Inner::B;
112 112
113 113
    // matchDeref
114 114
    assert matchDeref(Outer::Some(&innerA)) == 42;
test/tests/method.basic.rad +4 -4
4 4
record Point: Copy {
5 5
    x: i32,
6 6
    y: i32,
7 7
}
8 8
9 -
fn (p: *Point) sum() -> i32 {
9 +
fn (p: &Point) sum() -> i32 {
10 10
    return p.x + p.y;
11 11
}
12 12
13 -
fn (p: *mut Point) translate(dx: i32, dy: i32) {
13 +
fn (p: &mut Point) translate(dx: i32, dy: i32) {
14 14
    set p.x = p.x + dx;
15 15
    set p.y = p.y + dy;
16 16
}
17 17
18 -
@default fn main() -> i32 {
18 +
@default unsafe fn main() -> i32 {
19 19
    let mut pt = Point { x: 3, y: 4 };
20 20
21 21
    // Call immutable method.
22 22
    assert pt.sum() == 7;
23 23
25 25
    pt.translate(10, 20);
26 26
    assert pt.x == 13;
27 27
    assert pt.y == 24;
28 28
29 29
    // Call via pointer.
30 -
    let ptr = &pt;
30 +
    let ptr: *unsafe Point = &pt;
31 31
    assert ptr.sum() == 37;
32 32
33 33
    return 0;
34 34
}
test/tests/method.multiple.rad +5 -5
4 4
record Vec2: Copy {
5 5
    x: i32,
6 6
    y: i32,
7 7
}
8 8
9 -
fn (v: *Vec2) magnitudeSq() -> i32 {
9 +
fn (v: &Vec2) magnitudeSq() -> i32 {
10 10
    return v.x * v.x + v.y * v.y;
11 11
}
12 12
13 -
fn (v: *Vec2) dot(other: *Vec2) -> i32 {
13 +
unsafe fn (v: &Vec2) dot(other: *unsafe Vec2) -> i32 {
14 14
    return v.x * other.x + v.y * other.y;
15 15
}
16 16
17 -
fn (v: *mut Vec2) add(other: *Vec2) {
17 +
unsafe fn (v: &mut Vec2) add(other: *unsafe Vec2) {
18 18
    set v.x = v.x + other.x;
19 19
    set v.y = v.y + other.y;
20 20
}
21 21
22 -
fn (v: *mut Vec2) scale(factor: i32) {
22 +
fn (v: &mut Vec2) scale(factor: i32) {
23 23
    set v.x = v.x * factor;
24 24
    set v.y = v.y * factor;
25 25
}
26 26
27 -
@default fn main() -> i32 {
27 +
@default unsafe fn main() -> i32 {
28 28
    let mut a = Vec2 { x: 3, y: 4 };
29 29
    let b = Vec2 { x: 1, y: 2 };
30 30
31 31
    // Immutable method.
32 32
    assert a.magnitudeSq() == 25;
test/tests/method.ptr.rad +5 -5
3 3
4 4
record Counter: Copy {
5 5
    value: i32,
6 6
}
7 7
8 -
fn (c: *Counter) get() -> i32 {
8 +
fn (c: &Counter) get() -> i32 {
9 9
    return c.value;
10 10
}
11 11
12 -
fn (c: *mut Counter) inc() {
12 +
fn (c: &mut Counter) inc() {
13 13
    set c.value = c.value + 1;
14 14
}
15 15
16 -
@default fn main() -> i32 {
16 +
@default unsafe fn main() -> i32 {
17 17
    let mut c = Counter { value: 0 };
18 18
19 19
    // Direct call on value.
20 20
    assert c.get() == 0;
21 21
22 22
    // Mutable method on value.
23 23
    c.inc();
24 24
    assert c.get() == 1;
25 25
26 26
    // Call via immutable pointer.
27 -
    let p = &c;
27 +
    let p: *unsafe Counter = &c;
28 28
    assert p.get() == 1;
29 29
30 30
    // Call via mutable pointer.
31 -
    let mp = &mut c;
31 +
    let mp: *unsafe mut Counter = &mut c;
32 32
    mp.inc();
33 33
    assert mp.get() == 2;
34 34
35 35
    // Original value also updated.
36 36
    assert c.get() == 2;
test/tests/method.with.trait.rad +5 -5
5 5
    x: i32,
6 6
    y: i32,
7 7
}
8 8
9 9
// Standalone method.
10 -
fn (w: *Widget) area() -> i32 {
10 +
unsafe fn (w: *unsafe Widget) area() -> i32 {
11 11
    return w.x * w.y;
12 12
}
13 13
14 14
// Trait with its own method.
15 15
trait Printable {
16 -
    fn (*Printable) code() -> i32;
16 +
    unsafe fn (*unsafe Printable) code() -> i32;
17 17
}
18 18
19 19
instance Printable for Widget {
20 -
    fn (w: *Widget) code() -> i32 {
20 +
    unsafe fn (w: *unsafe Widget) code() -> i32 {
21 21
        return w.x + w.y;
22 22
    }
23 23
}
24 24
25 -
@default fn main() -> i32 {
25 +
@default unsafe fn main() -> i32 {
26 26
    let w = Widget { x: 3, y: 5 };
27 27
28 28
    // Standalone method call.
29 29
    assert w.area() == 15;
30 30
31 31
    // Trait method call via trait object.
32 -
    let p: *opaque Printable = &w;
32 +
    let p: *unsafe opaque Printable = &w;
33 33
    assert p.code() == 8;
34 34
35 35
    return 0;
36 36
}
test/tests/mutref.call.result.rad +3 -3
3 3
4 4
record Box: Copy {
5 5
    x: i32,
6 6
}
7 7
8 -
fn idBox(b: *mut Box) -> *mut Box {
8 +
fn idBox(b: *unsafe mut Box) -> *unsafe mut Box {
9 9
    return b;
10 10
}
11 11
12 -
@default fn main() -> i32 {
12 +
@default unsafe fn main() -> i32 {
13 13
    let mut b = Box { x: 1 };
14 14
15 -
    let px: *mut i32 = &mut idBox(&mut b).x;
15 +
    let px: *unsafe mut i32 = &mut idBox(&mut b).x;
16 16
    set *px = 9;
17 17
18 18
    assert b.x == 9;
19 19
    return 0;
20 20
}
test/tests/mutref.loop.bug.rad +1 -1
8 8
//!
9 9
//! The critical case is when the loop executes zero iterations: the merge
10 10
//! block tries to `load` through the initial integer value (not a valid
11 11
//! pointer), crashing the program.
12 12
13 -
fn store(ptr: *mut u32, val: u32) {
13 +
fn store(ptr: &mut u32, val: u32) {
14 14
    set *ptr = val;
15 15
}
16 16
17 17
/// Zero-iteration loop with &mut inside the body.
18 18
/// Without the fix, `val` starts as integer 42 in SSA, but the post-loop
test/tests/mutref.loop.rad +4 -4
1 -
fn callback(val: u32, ctx: *mut opaque) {
2 -
    let max = ctx as *mut u32;
1 +
unsafe fn callback(val: u32, ctx: *unsafe mut opaque) {
2 +
    let max = ctx as *unsafe mut u32;
3 3
    set *max = val;
4 4
}
5 5
6 -
fn test() -> i32 {
6 +
unsafe fn test() -> i32 {
7 7
    let mut maxReg: u32 = 0;
8 8
    let mut i: u32 = 0;
9 9
    while i < 3 {
10 -
        callback(i, &mut maxReg as *mut opaque);
10 +
        callback(i, &mut maxReg as *unsafe mut opaque);
11 11
        set i += 1;
12 12
    }
13 13
    return maxReg as i32;
14 14
}
test/tests/mutref.scalar.rad +1 -1
1 -
fn modify(counter: *mut i32, ret: bool) -> bool {
1 +
fn modify(counter: &mut i32, ret: bool) -> bool {
2 2
    set *counter += 1;
3 3
    return ret;
4 4
}
5 5
6 6
fn test() -> i32 {
test/tests/opt.nil.check.rad +8 -8
2 2
//! Test nil check for optional pointers and slices.
3 3
//! Ensure that nil checks compare the full pointer width, not just the low byte.
4 4
5 5
/// Return a pointer whose low byte is zero but is non-null.
6 6
/// This tests that nil checks use W64, not W8.
7 -
fn makeAlignedPtr() -> *u8 {
7 +
unsafe fn makeAlignedPtr() -> *unsafe u8 {
8 8
    let arr: [u8; 512] = undefined;
9 9
    // Find an address within arr whose low byte is 0x00.
10 10
    let base: u64 = &arr[0] as u64;
11 11
    let offset: u64 = 256 - (base % 256);
12 -
    return &arr[offset as u32] as *u8;
12 +
    return &arr[offset as u32] as *unsafe u8;
13 13
}
14 14
15 15
/// Test: optional pointer nil check must use full 64-bit comparison.
16 -
fn testOptionalPtrNilCheck() -> i32 {
16 +
unsafe fn testOptionalPtrNilCheck() -> i32 {
17 17
    let p = makeAlignedPtr();
18 -
    let opt: ?*u8 = p;
18 +
    let opt: ?*unsafe u8 = p;
19 19
20 20
    // The pointer is not nil, but its low byte is 0x00.
21 21
    // A W8 comparison would wrongly say it's nil.
22 22
    assert opt <> nil;
23 23
    if let v = opt {
26 26
    }
27 27
    return 2;
28 28
}
29 29
30 30
/// Test: optional slice nil check must use full 64-bit comparison.
31 -
fn testOptionalSliceNilCheck() -> i32 {
31 +
unsafe fn testOptionalSliceNilCheck() -> i32 {
32 32
    let arr: [u8; 512] = undefined;
33 33
    let base: u64 = &arr[0] as u64;
34 34
    let offset: u64 = 256 - (base % 256);
35 35
    // Create a slice starting at an address whose low byte is 0.
36 -
    let s: *[u8] = &arr[offset as u32 ..];
36 +
    let s: *unsafe [u8] = &arr[offset as u32 ..];
37 37
38 -
    let opt: ?*[u8] = s;
38 +
    let opt: ?*unsafe [u8] = s;
39 39
    assert opt <> nil;
40 40
    if let v = opt {
41 41
        return 0;
42 42
    }
43 43
    return 2;
44 44
}
45 45
46 -
@default fn main() -> i32 {
46 +
@default unsafe fn main() -> i32 {
47 47
    let r1 = testOptionalPtrNilCheck();
48 48
    if r1 <> 0 {
49 49
        return r1;
50 50
    }
51 51
    let r2 = testOptionalSliceNilCheck();
test/tests/opt.slice.npo.rad +31 -31
3 3
//! Optional slices should have the same size as slices (16 bytes),
4 4
//! using a null data pointer to represent `nil`.
5 5
6 6
fn checkSizes() -> u8 {
7 7
    // ?*[T] should be the same size as *[T] (16 bytes, not 24).
8 -
    assert @sizeOf(?*[u8]) == 16;
9 -
    assert @alignOf(?*[u8]) == 8;
10 -
    assert @sizeOf(?*[u16]) == 16;
11 -
    assert @sizeOf(?*mut [u8]) == 16;
8 +
    assert @sizeOf(?*unsafe [u8]) == 16;
9 +
    assert @alignOf(?*unsafe [u8]) == 8;
10 +
    assert @sizeOf(?*unsafe [u16]) == 16;
11 +
    assert @sizeOf(?*unsafe mut [u8]) == 16;
12 12
    return 0;
13 13
}
14 14
15 15
fn checkNil() -> u8 {
16 -
    let x: ?*[u8] = nil;
16 +
    let x: ?*unsafe [u8] = nil;
17 17
    assert x == nil;
18 18
    return 0;
19 19
}
20 20
21 -
fn checkWrap() -> u8 {
21 +
unsafe fn checkWrap() -> u8 {
22 22
    let arr: [u8; 3] = [1, 2, 3];
23 -
    let s = &arr[..];
24 -
    let opt: ?*[u8] = s;
23 +
    let s: *unsafe [u8] = &arr[..];
24 +
    let opt: ?*unsafe [u8] = s;
25 25
26 26
    assert opt <> nil;
27 27
    return 0;
28 28
}
29 29
30 -
fn checkIfLet() -> u8 {
30 +
unsafe fn checkIfLet() -> u8 {
31 31
    let arr: [u8; 3] = [10, 20, 30];
32 -
    let s = &arr[..];
33 -
    let opt: ?*[u8] = s;
32 +
    let s: *unsafe [u8] = &arr[..];
33 +
    let opt: ?*unsafe [u8] = s;
34 34
35 35
    if let val = opt {
36 36
        assert val.len == 3;
37 37
        assert val[0] == 10;
38 38
        assert val[1] == 20;
39 39
    } else {
40 40
        return 34;
41 41
    }
42 42
43 -
    let none: ?*[u8] = nil;
43 +
    let none: ?*unsafe [u8] = nil;
44 44
    if let _ = none {
45 45
        return 35;
46 46
    }
47 47
    return 0;
48 48
}
49 49
50 -
fn checkLetElse() -> u8 {
50 +
unsafe fn checkLetElse() -> u8 {
51 51
    let arr: [u8; 3] = [10, 20, 30];
52 -
    let s = &arr[..];
53 -
    let opt: ?*[u8] = s;
52 +
    let s: *unsafe [u8] = &arr[..];
53 +
    let opt: ?*unsafe [u8] = s;
54 54
55 55
    let val = opt else {
56 56
        return 40;
57 57
    };
58 58
    assert val.len == 3;
59 59
    return 0;
60 60
}
61 61
62 -
fn returnNil() -> ?*[u8] {
62 +
fn returnNil() -> ?*unsafe [u8] {
63 63
    return nil;
64 64
}
65 65
66 -
fn returnSome() -> ?*[u8] {
67 -
    let arr: [u8; 2] = [42, 99];
68 -
    return &arr[..];
66 +
unsafe fn returnSome() -> ?*unsafe [u8] {
67 +
    static arr: [u8; 2] = [42, 99];
68 +
    return &arr[..] as *unsafe [u8];
69 69
}
70 70
71 -
fn checkReturn() -> u8 {
71 +
unsafe fn checkReturn() -> u8 {
72 72
    let a = returnNil();
73 73
    assert a == nil;
74 74
    let b = returnSome();
75 75
    assert b <> nil;
76 76
    if let val = b {
79 79
        return 53;
80 80
    }
81 81
    return 0;
82 82
}
83 83
84 -
fn checkMatch() -> u8 {
84 +
unsafe fn checkMatch() -> u8 {
85 85
    let arr: [u8; 2] = [5, 6];
86 -
    let s = &arr[..];
87 -
    let opt: ?*[u8] = s;
86 +
    let s: *unsafe [u8] = &arr[..];
87 +
    let opt: ?*unsafe [u8] = s;
88 88
89 89
    match opt {
90 90
        case nil => {
91 91
            return 60;
92 92
        }
93 93
        else => {}
94 94
    }
95 95
96 -
    let none: ?*[u8] = nil;
96 +
    let none: ?*unsafe [u8] = nil;
97 97
    match none {
98 98
        case nil => {}
99 99
        else => {
100 100
            return 61;
101 101
        }
102 102
    }
103 103
    return 0;
104 104
}
105 105
106 -
fn checkEq() -> u8 {
107 -
    let a: ?*[u8] = nil;
108 -
    let b: ?*[u8] = nil;
106 +
unsafe fn checkEq() -> u8 {
107 +
    let a: ?*unsafe [u8] = nil;
108 +
    let b: ?*unsafe [u8] = nil;
109 109
110 110
    // nil == nil
111 111
    assert a == b;
112 112
113 113
    let arr: [u8; 2] = [1, 2];
114 -
    let s = &arr[..];
115 -
    let c: ?*[u8] = s;
114 +
    let s: *unsafe [u8] = &arr[..];
115 +
    let c: ?*unsafe [u8] = s;
116 116
117 117
    // some <> nil
118 118
    assert c <> a;
119 119
120 120
    // some == some (same pointer)
121 -
    let d: ?*[u8] = s;
121 +
    let d: ?*unsafe [u8] = s;
122 122
    assert c == d;
123 123
124 124
    return 0;
125 125
}
126 126
127 -
@default fn main() -> u8 {
127 +
@default unsafe fn main() -> u8 {
128 128
    let r1 = checkSizes();
129 129
    if r1 <> 0 {
130 130
        return r1;
131 131
    }
132 132
    let r2 = checkNil();
test/tests/opt.slice.npo.ril +6 -3
1 +
data mut $returnSome$nominal$arr align 1 {
2 +
    w8 42;
3 +
    w8 99;
4 +
}
5 +
1 6
fn w8 $checkSizes() {
2 7
  @entry0
3 8
    br.eq w32 16 16 @assert.ok2 @assert.fail1;
4 9
  @assert.fail1
5 10
    unreachable;
161 166
    ret %0;
162 167
}
163 168
164 169
fn w64 $returnSome(w64 %0) {
165 170
  @entry0
166 -
    reserve %1 2 1;
167 -
    store w8 42 %1 0;
168 -
    store w8 99 %1 1;
171 +
    copy %1 $returnSome$nominal$arr;
169 172
    reserve %2 16 8;
170 173
    store w64 %1 %2 0;
171 174
    store w32 2 %2 8;
172 175
    store w32 2 %2 12;
173 176
    blit %0 %2 16;
test/tests/pointer.copy.edge.case.rad +4 -4
17 17
record Parser: Copy {
18 18
    nodes: [Node; 1],
19 19
    count: u32,
20 20
}
21 21
22 -
fn makeNode(p: *mut Parser, kind: NodeKind) -> *mut Node {
22 +
unsafe fn makeNode(p: *unsafe mut Parser, kind: NodeKind) -> *unsafe mut Node {
23 23
    let idx: u32 = p.count;
24 24
    set p.nodes[idx] = Node {
25 25
        span: Span { length: 0 },
26 26
        kind,
27 27
    };
28 28
    set p.count = idx + 1;
29 29
    return &mut p.nodes[idx];
30 30
}
31 31
32 -
fn setLen(n: *mut Node, len: u32) {
32 +
unsafe fn setLen(n: *unsafe mut Node, len: u32) {
33 33
    set n.span.length = len;
34 34
}
35 35
36 -
@default fn main() -> i32 {
36 +
@default unsafe fn main() -> i32 {
37 37
    let mut parser: Parser = Parser {
38 38
        nodes: [Node {
39 39
            span: Span { length: 0 },
40 40
            kind: NodeKind::Placeholder,
41 41
        }; 1],
46 46
    set parser.nodes[0] = Node {
47 47
        span: Span { length: 0 },
48 48
        kind: NodeKind::Placeholder,
49 49
    };
50 50
51 -
    let node: *mut Node = makeNode(&mut parser, NodeKind::Bool(true));
51 +
    let node: *unsafe mut Node = makeNode(&mut parser, NodeKind::Bool(true));
52 52
    setLen(node, 4);
53 53
54 54
    match parser.nodes[0].kind {
55 55
        case NodeKind::Bool(value) => {
56 56
            assert value;
test/tests/pointer.slice.index.rad +2 -2
1 1
//! returns: 0
2 2
3 3
record Holder: Copy {
4 -
    ptr: *[i32],
4 +
    ptr: *unsafe [i32],
5 5
    zero: u32,
6 6
}
7 7
8 8
// Test that pointer-to-slice indexing correctly dereferences the slice header.
9 -
@default fn main() -> i32 {
9 +
@default unsafe fn main() -> i32 {
10 10
    let arr: [i32; 2] = [5, 7];
11 11
    let h = Holder { ptr: &arr[..], zero: 0 };
12 12
13 13
    return (h.ptr[1]) - 7;
14 14
}
test/tests/pointer.stack.borrow.rad added +38 -0
1 +
//! returns: 0
2 +
3 +
/// Permanent values used by stored pointers.
4 +
static VALUES: [u32; 3] = [3, 5, 7];
5 +
6 +
/// Add a value to a stack location during a call.
7 +
fn increment(value: &mut u32, amount: u32) {
8 +
    set *value += amount;
9 +
}
10 +
11 +
/// Sum borrowed values during a call.
12 +
fn sum(values: &[u32]) -> u32 {
13 +
    let mut result: u32 = 0;
14 +
    for value in values {
15 +
        set result += value;
16 +
    }
17 +
    return result;
18 +
}
19 +
20 +
/// Return a slice backed by permanent storage.
21 +
fn values() -> *[u32] {
22 +
    return &VALUES[1..];
23 +
}
24 +
25 +
/// Exercise stack borrows and stored pointers to permanent data.
26 +
@default fn main() -> i32 {
27 +
    let mut value: u32 = 4;
28 +
    increment(&mut value, 6);
29 +
    if value <> 10 { return 1; }
30 +
31 +
    let local: [u32; 3] = [value, 2, 3];
32 +
    if sum(&local[..]) <> 15 { return 2; }
33 +
    if sum(values()) <> 12 { return 3; }
34 +
35 +
    let pointer: *u32 = &VALUES[0];
36 +
    if *pointer <> 3 { return 4; }
37 +
    return 0;
38 +
}
test/tests/pointer.stack.unsafe.rad added +18 -0
1 +
//! returns: 0
2 +
3 +
/// Exercise raw pointers while their stack storage is alive.
4 +
@default unsafe fn main() -> i32 {
5 +
    let mut value: u32 = 4;
6 +
    let pointer: *unsafe mut u32 = &mut value;
7 +
    set *pointer = 9;
8 +
    if value <> 9 { return 1; }
9 +
10 +
    let mut values: [u32; 2] = [value, 2];
11 +
    let slice: *unsafe mut [u32] = &mut values[..];
12 +
    set slice[1] = 7;
13 +
    if values[1] <> 7 { return 2; }
14 +
15 +
    let cast = &value as *unsafe u32;
16 +
    if *cast <> 9 { return 3; }
17 +
    return 0;
18 +
}
test/tests/prog.bignum.rad +9 -9
6 6
7 7
/// Number of limbs per big number (128 bits = 4 x 32-bit words).
8 8
constant LIMBS: u32 = 4;
9 9
10 10
/// Set a big number to a u32 value.
11 -
fn bnFromU32(dst: *mut [u32], val: u32) {
11 +
fn bnFromU32(dst: &mut [u32], val: u32) {
12 12
    set dst[0] = val;
13 13
    let mut i: u32 = 1;
14 14
    while i < LIMBS {
15 15
        set dst[i] = 0;
16 16
        set i += 1;
17 17
    }
18 18
}
19 19
20 20
/// Set a big number to zero.
21 -
fn bnZero(dst: *mut [u32]) {
21 +
fn bnZero(dst: &mut [u32]) {
22 22
    let mut i: u32 = 0;
23 23
    while i < LIMBS {
24 24
        set dst[i] = 0;
25 25
        set i += 1;
26 26
    }
27 27
}
28 28
29 29
/// Copy src to dst.
30 -
fn bnCopy(dst: *mut [u32], src: *[u32]) {
30 +
fn bnCopy(dst: &mut [u32], src: &[u32]) {
31 31
    let mut i: u32 = 0;
32 32
    while i < LIMBS {
33 33
        set dst[i] = src[i];
34 34
        set i += 1;
35 35
    }
36 36
}
37 37
38 38
/// Compare two big numbers. Returns 0 if equal, 1 if a > b, -1 if a < b.
39 -
fn bnCmp(a: *[u32], b: *[u32]) -> i32 {
39 +
fn bnCmp(a: &[u32], b: &[u32]) -> i32 {
40 40
    let mut i: i32 = LIMBS as i32 - 1;
41 41
    while i >= 0 {
42 42
        if a[i as u32] > b[i as u32] {
43 43
            return 1;
44 44
        }
52 52
    }
53 53
    return 0;
54 54
}
55 55
56 56
/// Add two big numbers: dst = a + b. Returns carry (0 or 1).
57 -
fn bnAdd(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 {
57 +
fn bnAdd(dst: &mut [u32], a: &[u32], b: &[u32]) -> u32 {
58 58
    let mut carry: u32 = 0;
59 59
    let mut i: u32 = 0;
60 60
    while i < LIMBS {
61 61
        let sumLo: u32 = a[i] + b[i];
62 62
        let mut carry1: u32 = 0;
74 74
    }
75 75
    return carry;
76 76
}
77 77
78 78
/// Subtract two big numbers: dst = a - b. Returns borrow (0 or 1).
79 -
fn bnSub(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 {
79 +
fn bnSub(dst: &mut [u32], a: &[u32], b: &[u32]) -> u32 {
80 80
    let mut borrow: u32 = 0;
81 81
    let mut i: u32 = 0;
82 82
    while i < LIMBS {
83 83
        let diff: u32 = a[i] - b[i];
84 84
        let mut borrow1: u32 = 0;
96 96
    }
97 97
    return borrow;
98 98
}
99 99
100 100
/// Multiply two LIMBS-word numbers, producing a 2*LIMBS-word result in wide.
101 -
fn bnMul(wide: *mut [u32], a: *[u32], b: *[u32]) {
101 +
fn bnMul(wide: &mut [u32], a: &[u32], b: &[u32]) {
102 102
    let mut i: u32 = 0;
103 103
    while i < LIMBS * 2 {
104 104
        set wide[i] = 0;
105 105
        set i += 1;
106 106
    }
152 152
        set i += 1;
153 153
    }
154 154
}
155 155
156 156
/// Left shift a big number by 1 bit.
157 -
fn bnShl1(dst: *mut [u32], src: *[u32]) {
157 +
fn bnShl1(dst: &mut [u32], src: &[u32]) {
158 158
    let mut carry: u32 = 0;
159 159
    let mut i: u32 = 0;
160 160
    while i < LIMBS {
161 161
        let newCarry: u32 = src[i] >> 31;
162 162
        set dst[i] = (src[i] << 1) | carry;
164 164
        set i += 1;
165 165
    }
166 166
}
167 167
168 168
/// Right shift a big number by 1 bit.
169 -
fn bnShr1(dst: *mut [u32], src: *[u32]) {
169 +
fn bnShr1(dst: &mut [u32], src: &[u32]) {
170 170
    let mut carry: u32 = 0;
171 171
    let mut i: u32 = LIMBS;
172 172
    while i > 0 {
173 173
        set i -= 1;
174 174
        let newCarry: u32 = src[i] & 1;
test/tests/prog.binsearch.rad +4 -4
1 1
//! returns: 0
2 2
//! Binary search.
3 3
//! Search a pre-sorted array for present and absent values.
4 4
5 5
/// Binary search for `target` in `data`. Returns the index if found, or nil.
6 -
fn binarySearch(data: *[i32], target: i32) -> ?u32 {
6 +
fn binarySearch(data: &[i32], target: i32) -> ?u32 {
7 7
    let mut lo: i32 = 0;
8 8
    let mut hi: i32 = data.len as i32 - 1;
9 9
10 10
    while lo <= hi {
11 11
        let mid: i32 = lo + (hi - lo) / 2;
21 21
    }
22 22
    return nil;
23 23
}
24 24
25 25
/// Test searching for elements that exist.
26 -
fn testPresent(data: *[i32]) -> i32 {
26 +
fn testPresent(data: &[i32]) -> i32 {
27 27
    // First element.
28 28
    let idx0 = binarySearch(data, 3) else {
29 29
        return 1;
30 30
    };
31 31
    assert idx0 == 0;
56 56
    assert idx16 == 16;
57 57
    return 0;
58 58
}
59 59
60 60
/// Test searching for elements that do not exist.
61 -
fn testAbsent(data: *[i32]) -> i32 {
61 +
fn testAbsent(data: &[i32]) -> i32 {
62 62
    // Below range.
63 63
    assert binarySearch(data, 1) == nil;
64 64
    // Above range.
65 65
    assert binarySearch(data, 200) == nil;
66 66
    // Between existing elements.
71 71
    assert binarySearch(data, 99) == nil;
72 72
    return 0;
73 73
}
74 74
75 75
/// Test searching for every element in the array.
76 -
fn testAllPresent(data: *[i32]) -> i32 {
76 +
fn testAllPresent(data: &[i32]) -> i32 {
77 77
    for value, index in data {
78 78
        let found = binarySearch(data, value) else {
79 79
            return index as i32 + 1;
80 80
        };
81 81
        if found <> index {
test/tests/prog.bubblesort.rad +4 -4
1 1
//! returns: 0
2 2
//! Bubble sort.
3 3
//! Sort an array of integers and verify the result.
4 4
5 5
/// Bubble sort the array in ascending order.
6 -
fn bubbleSort(data: *mut [i32]) {
6 +
fn bubbleSort(data: &mut [i32]) {
7 7
    let mut n: u32 = data.len;
8 8
    while n > 1 {
9 9
        let mut swapped: bool = false;
10 10
        let mut i: u32 = 0;
11 11
        while i < n - 1 {
24 24
        set n -= 1;
25 25
    }
26 26
}
27 27
28 28
/// Verify the array is sorted in ascending order.
29 -
fn isSorted(data: *[i32]) -> bool {
29 +
fn isSorted(data: &[i32]) -> bool {
30 30
    let mut prev: ?i32 = nil;
31 31
    for val in data {
32 32
        if let p = prev {
33 33
            if p > val {
34 34
                return false;
38 38
    }
39 39
    return true;
40 40
}
41 41
42 42
/// Compute the sum of all elements.
43 -
fn sum(data: *[i32]) -> i32 {
43 +
fn sum(data: &[i32]) -> i32 {
44 44
    let mut total: i32 = 0;
45 45
    for val in data {
46 46
        set total += val;
47 47
    }
48 48
    return total;
49 49
}
50 50
51 51
/// Verify specific positions in the sorted output.
52 -
fn verifyPositions(data: *[i32]) -> i32 {
52 +
fn verifyPositions(data: &[i32]) -> i32 {
53 53
    // Sorted: 3 5 7 12 17 19 28 31 42 50 55 66 71 80 88 93
54 54
    assert data[0] == 3;
55 55
    assert data[1] == 5;
56 56
    assert data[2] == 7;
57 57
    assert data[7] == 31;
test/tests/prog.cordic.rad +8 -8
27 27
    sin: i32,
28 28
}
29 29
30 30
/// CORDIC rotation mode: compute cos(angle) and sin(angle).
31 31
/// Input angle in Q16.16 radians, must be in [-pi/2, pi/2].
32 -
fn cordicRotate(angle: i32, atanTable: *[i32]) -> CosSin {
32 +
fn cordicRotate(angle: i32, atanTable: &[i32]) -> CosSin {
33 33
    let mut x: i32 = CORDIC_GAIN;
34 34
    let mut y: i32 = 0;
35 35
    let mut z: i32 = angle;
36 36
37 37
    let mut i: u32 = 0;
53 53
54 54
    return CosSin { cos: x, sin: y };
55 55
}
56 56
57 57
/// Compute cos and sin for any angle by reducing to [-pi/2, pi/2].
58 -
fn cosSin(angle: i32, atanTable: *[i32]) -> CosSin {
58 +
fn cosSin(angle: i32, atanTable: &[i32]) -> CosSin {
59 59
    let mut a: i32 = angle;
60 60
61 61
    // Reduce to [-pi, pi].
62 62
    while a > PI {
63 63
        set a -= 2 * PI;
120 120
    }
121 121
    return result as i32;
122 122
}
123 123
124 124
/// Test cos(0) = 1, sin(0) = 0.
125 -
fn testZero(atanTable: *[i32]) -> i32 {
125 +
fn testZero(atanTable: &[i32]) -> i32 {
126 126
    let r: CosSin = cosSin(0, atanTable);
127 127
    // cos(0) should be close to 65536 (1.0 in Q16.16).
128 128
    let cosErr: i32 = abs(r.cos - SCALE);
129 129
    let sinErr: i32 = abs(r.sin);
130 130
133 133
    assert sinErr <= 655;
134 134
    return 0;
135 135
}
136 136
137 137
/// Test cos(pi/2) = 0, sin(pi/2) = 1.
138 -
fn testHalfPi(atanTable: *[i32]) -> i32 {
138 +
fn testHalfPi(atanTable: &[i32]) -> i32 {
139 139
    let r: CosSin = cosSin(HALF_PI, atanTable);
140 140
    let cosErr: i32 = abs(r.cos);
141 141
    let sinErr: i32 = abs(r.sin - SCALE);
142 142
143 143
    assert cosErr <= 655;
144 144
    assert sinErr <= 655;
145 145
    return 0;
146 146
}
147 147
148 148
/// Test cos(pi) = -1, sin(pi) = 0.
149 -
fn testPi(atanTable: *[i32]) -> i32 {
149 +
fn testPi(atanTable: &[i32]) -> i32 {
150 150
    let r: CosSin = cosSin(PI, atanTable);
151 151
    let cosErr: i32 = abs(r.cos + SCALE);
152 152
    let sinErr: i32 = abs(r.sin);
153 153
154 154
    assert cosErr <= 655;
155 155
    assert sinErr <= 655;
156 156
    return 0;
157 157
}
158 158
159 159
/// Test Pythagorean identity: sin^2 + cos^2 = 1 for several angles.
160 -
fn testPythagorean(atanTable: *[i32]) -> i32 {
160 +
fn testPythagorean(atanTable: &[i32]) -> i32 {
161 161
    // Test at 16 evenly spaced angles from 0 to 2*pi.
162 162
    let step: i32 = PI / 8;
163 163
    let mut i: i32 = 0 - PI;
164 164
165 165
    while i <= PI {
176 176
    }
177 177
    return 0;
178 178
}
179 179
180 180
/// Test symmetry: sin(-x) = -sin(x), cos(-x) = cos(x).
181 -
fn testSymmetry(atanTable: *[i32]) -> i32 {
181 +
fn testSymmetry(atanTable: &[i32]) -> i32 {
182 182
    let angles: [i32; 5] = [16384, 32768, 51472, 65536, 81920];
183 183
184 184
    let mut i: u32 = 0;
185 185
    while i < 5 {
186 186
        let a: i32 = angles[i];
196 196
    }
197 197
    return 0;
198 198
}
199 199
200 200
/// Test specific known value: cos(pi/3) = 0.5, sin(pi/3) = 0.866.
201 -
fn testPiThird(atanTable: *[i32]) -> i32 {
201 +
fn testPiThird(atanTable: &[i32]) -> i32 {
202 202
    // pi/3 in Q16.16 = 68629.
203 203
    let piThird: i32 = 68629;
204 204
    let r: CosSin = cosSin(piThird, atanTable);
205 205
206 206
    // cos(pi/3) = 0.5 = 32768 in Q16.16.
test/tests/prog.crc32.rad +5 -5
2 2
//! CRC-32.
3 3
//! Compute CRC-32 of a byte buffer using a 256-entry lookup table.
4 4
//! The table is built at startup. Verify against known checksums.
5 5
6 6
/// Build the CRC-32 lookup table using the standard polynomial 0xEDB88320.
7 -
fn buildTable(table: *mut [u32]) {
7 +
fn buildTable(table: &mut [u32]) {
8 8
    let mut i: u32 = 0;
9 9
    while i < 256 {
10 10
        let mut crc: u32 = i;
11 11
        let mut j: u32 = 0;
12 12
        while j < 8 {
21 21
        set i += 1;
22 22
    }
23 23
}
24 24
25 25
/// Compute CRC-32 of a byte slice.
26 -
fn crc32(table: *[u32], data: *[u8]) -> u32 {
26 +
fn crc32(table: &[u32], data: &[u8]) -> u32 {
27 27
    let mut crc: u32 = 0xFFFFFFFF;
28 28
    let mut i: u32 = 0;
29 29
    while i < data.len {
30 30
        let byte: u8 = data[i];
31 31
        let index: u32 = (crc ^ byte as u32) & 0xFF;
34 34
    }
35 35
    return crc ^ 0xFFFFFFFF;
36 36
}
37 37
38 38
/// Test the lookup table has been built correctly.
39 -
fn testTable(table: *[u32]) -> i32 {
39 +
fn testTable(table: &[u32]) -> i32 {
40 40
    // TABLE[0] should be 0 (zero input, all shifts produce zero).
41 41
    assert table[0] == 0;
42 42
    // Known value: TABLE[1] = 0x77073096
43 43
    assert table[1] == 0x77073096;
44 44
    // TABLE[255] is a known value: 0x2D02EF8D
45 45
    assert table[255] == 0x2D02EF8D;
46 46
    return 0;
47 47
}
48 48
49 49
/// Test CRC-32 of known strings.
50 -
fn testKnownCRC(table: *[u32]) -> i32 {
50 +
fn testKnownCRC(table: &[u32]) -> i32 {
51 51
    // CRC-32 of empty data should be 0x00000000.
52 52
    assert crc32(table, &[]) == 0x00000000;
53 53
54 54
    // CRC-32 of "123456789" = 0xCBF43926 (the standard check value).
55 55
    let check: [u8; 9] = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39];
60 60
    assert crc32(table, &single[..]) == 0xD3D99E8B;
61 61
    return 0;
62 62
}
63 63
64 64
/// Test CRC-32 of incrementally built data.
65 -
fn testIncremental(table: *[u32]) -> i32 {
65 +
fn testIncremental(table: &[u32]) -> i32 {
66 66
    // Build a 32-byte buffer with values 0..31.
67 67
    let mut buf: [u8; 32] = [0; 32];
68 68
    let mut i: u32 = 0;
69 69
    while i < 32 {
70 70
        set buf[i] = i as u8;
test/tests/prog.dijkstra.rad +23 -23
10 10
    dist: u32,
11 11
    node: u32,
12 12
}
13 13
14 14
record Graph: Copy {
15 -
    adj: *mut [[u32; 16]],
16 -
    dist: *mut [u32],
17 -
    prev: *mut [i32],
18 -
    visited: *mut [bool],
15 +
    adj: *unsafe mut [[u32; 16]],
16 +
    dist: *unsafe mut [u32],
17 +
    prev: *unsafe mut [i32],
18 +
    visited: *unsafe mut [bool],
19 19
    numNodes: u32,
20 -
    heap: *mut [HeapEntry],
20 +
    heap: *unsafe mut [HeapEntry],
21 21
    heapSize: u32,
22 22
}
23 23
24 -
fn heapSwap(g: *mut Graph, i: u32, j: u32) {
24 +
unsafe fn heapSwap(g: &mut Graph, i: u32, j: u32) {
25 25
    let tmp: HeapEntry = g.heap[i];
26 26
    set g.heap[i] = g.heap[j];
27 27
    set g.heap[j] = tmp;
28 28
}
29 29
30 -
fn siftUp(g: *mut Graph, pos: u32) {
30 +
unsafe fn siftUp(g: &mut Graph, pos: u32) {
31 31
    let mut i: u32 = pos;
32 32
    while i > 0 {
33 33
        let parent: u32 = (i - 1) / 2;
34 34
        if g.heap[i].dist < g.heap[parent].dist {
35 35
            heapSwap(g, i, parent);
38 38
            return;
39 39
        }
40 40
    }
41 41
}
42 42
43 -
fn siftDown(g: *mut Graph, pos: u32) {
43 +
unsafe fn siftDown(g: &mut Graph, pos: u32) {
44 44
    let mut i: u32 = pos;
45 45
    while true {
46 46
        let left: u32 = 2 * i + 1;
47 47
        let right: u32 = 2 * i + 2;
48 48
        let mut smallest: u32 = i;
59 59
        heapSwap(g, i, smallest);
60 60
        set i = smallest;
61 61
    }
62 62
}
63 63
64 -
fn heapPush(g: *mut Graph, dist: u32, node: u32) {
64 +
unsafe fn heapPush(g: &mut Graph, dist: u32, node: u32) {
65 65
    set g.heap[g.heapSize] = HeapEntry { dist, node };
66 66
    set g.heapSize += 1;
67 67
    siftUp(g, g.heapSize - 1);
68 68
}
69 69
70 70
/// Pop from the heap. Returns nil if the heap is empty.
71 -
fn heapPop(g: *mut Graph) -> ?HeapEntry {
71 +
unsafe fn heapPop(g: &mut Graph) -> ?HeapEntry {
72 72
    if g.heapSize == 0 {
73 73
        return nil;
74 74
    }
75 75
    let result: HeapEntry = g.heap[0];
76 76
    set g.heapSize -= 1;
79 79
        siftDown(g, 0);
80 80
    }
81 81
    return result;
82 82
}
83 83
84 -
fn addEdge(g: *mut Graph, from: u32, to: u32, weight: u32) {
84 +
unsafe fn addEdge(g: &mut Graph, from: u32, to: u32, weight: u32) {
85 85
    set g.adj[from][to] = weight;
86 86
}
87 87
88 -
fn addBidiEdge(g: *mut Graph, from: u32, to: u32, weight: u32) {
88 +
unsafe fn addBidiEdge(g: &mut Graph, from: u32, to: u32, weight: u32) {
89 89
    set g.adj[from][to] = weight;
90 90
    set g.adj[to][from] = weight;
91 91
}
92 92
93 -
fn resetGraph(g: *mut Graph, n: u32) {
93 +
unsafe fn resetGraph(g: &mut Graph, n: u32) {
94 94
    set g.numNodes = n;
95 95
    let mut i: u32 = 0;
96 96
    while i < MAX_NODES {
97 97
        let mut j: u32 = 0;
98 98
        while j < MAX_NODES {
106 106
    }
107 107
    set g.heapSize = 0;
108 108
}
109 109
110 110
/// Get the predecessor as an optional; -1 means no predecessor.
111 -
fn getPrev(g: *Graph, node: u32) -> ?u32 {
111 +
unsafe fn getPrev(g: &Graph, node: u32) -> ?u32 {
112 112
    let p = g.prev[node];
113 113
    if p < 0 {
114 114
        return nil;
115 115
    }
116 116
    return p as u32;
117 117
}
118 118
119 -
fn dijkstra(g: *mut Graph, source: u32) {
119 +
unsafe fn dijkstra(g: &mut Graph, source: u32) {
120 120
    set g.dist[source] = 0;
121 121
    heapPush(g, 0, source);
122 122
123 123
    while let entry = heapPop(g) {
124 124
        let u: u32 = entry.node;
142 142
        }
143 143
    }
144 144
}
145 145
146 146
/// Reconstruct the shortest path from source to target.
147 -
fn reconstructPath(g: *Graph, target: u32, path: *mut [u32]) -> u32 {
147 +
unsafe fn reconstructPath(g: &Graph, target: u32, path: &mut [u32]) -> u32 {
148 148
    let mut len: u32 = 0;
149 149
150 150
    set path[len] = target;
151 151
    set len += 1;
152 152
168 168
        set b -= 1;
169 169
    }
170 170
    return len;
171 171
}
172 172
173 -
fn testLinear(g: *mut Graph) -> i32 {
173 +
unsafe fn testLinear(g: &mut Graph) -> i32 {
174 174
    resetGraph(g, 5);
175 175
    addEdge(g, 0, 1, 10);
176 176
    addEdge(g, 1, 2, 10);
177 177
    addEdge(g, 2, 3, 10);
178 178
    addEdge(g, 3, 4, 10);
193 193
    assert path[4] == 4;
194 194
195 195
    return 0;
196 196
}
197 197
198 -
fn testShortcut(g: *mut Graph) -> i32 {
198 +
unsafe fn testShortcut(g: &mut Graph) -> i32 {
199 199
    resetGraph(g, 4);
200 200
    addEdge(g, 0, 1, 10);
201 201
    addEdge(g, 1, 2, 10);
202 202
    addEdge(g, 0, 3, 5);
203 203
    addEdge(g, 3, 2, 3);
212 212
    assert prev2 == 3;
213 213
214 214
    return 0;
215 215
}
216 216
217 -
fn testBidirectional(g: *mut Graph) -> i32 {
217 +
unsafe fn testBidirectional(g: &mut Graph) -> i32 {
218 218
    resetGraph(g, 5);
219 219
    addBidiEdge(g, 0, 1, 1);
220 220
    addBidiEdge(g, 1, 2, 2);
221 221
    addBidiEdge(g, 2, 3, 3);
222 222
    addBidiEdge(g, 3, 0, 4);
234 234
    }
235 235
236 236
    return 0;
237 237
}
238 238
239 -
fn testComplete(g: *mut Graph) -> i32 {
239 +
unsafe fn testComplete(g: &mut Graph) -> i32 {
240 240
    resetGraph(g, 8);
241 241
242 242
    let mut i: u32 = 0;
243 243
    while i < 8 {
244 244
        let mut j: u32 = 0;
267 267
    }
268 268
269 269
    return 0;
270 270
}
271 271
272 -
fn testDisconnected(g: *mut Graph) -> i32 {
272 +
unsafe fn testDisconnected(g: &mut Graph) -> i32 {
273 273
    resetGraph(g, 6);
274 274
    addEdge(g, 0, 1, 5);
275 275
    addEdge(g, 1, 2, 3);
276 276
    addEdge(g, 3, 4, 2);
277 277
    addEdge(g, 4, 5, 1);
290 290
    assert getPrev(g, 3) == nil;
291 291
292 292
    return 0;
293 293
}
294 294
295 -
fn testDiamond(g: *mut Graph) -> i32 {
295 +
unsafe fn testDiamond(g: &mut Graph) -> i32 {
296 296
    resetGraph(g, 5);
297 297
    addEdge(g, 0, 1, 5);
298 298
    addEdge(g, 0, 2, 5);
299 299
    addEdge(g, 1, 3, 5);
300 300
    addEdge(g, 2, 3, 5);
311 311
    assert prev3 == 1 or prev3 == 2;
312 312
313 313
    return 0;
314 314
}
315 315
316 -
@default fn main() -> i32 {
316 +
@default unsafe fn main() -> i32 {
317 317
    let mut adj: [[u32; 16]; 16] = [[0xFFFFFFFF; 16]; 16];
318 318
    let mut dist: [u32; 16] = [0xFFFFFFFF; 16];
319 319
    let mut prev: [i32; 16] = [-1; 16];
320 320
    let mut visited: [bool; 16] = [false; 16];
321 321
    let mut heap: [HeapEntry; 256] = [HeapEntry { dist: 0, node: 0 }; 256];
test/tests/prog.eval.rad +17 -17
31 31
union EvalError: Copy {
32 32
    DivByZero
33 33
}
34 34
35 35
/// Allocate a new node, returning its index.
36 -
fn newNode(pool: *mut Pool, expr: Expr) -> u32 {
36 +
fn newNode(pool: &mut Pool, expr: Expr) -> u32 {
37 37
    let idx = pool.count;
38 38
    set pool.nodes[idx] = expr;
39 39
    set pool.count += 1;
40 40
    return idx;
41 41
}
42 42
43 43
/// Convenience constructors.
44 -
fn num(pool: *mut Pool, n: i32) -> u32 {
44 +
fn num(pool: &mut Pool, n: i32) -> u32 {
45 45
    return newNode(pool, Expr::Num(n));
46 46
}
47 47
48 -
fn add(pool: *mut Pool, left: u32, right: u32) -> u32 {
48 +
fn add(pool: &mut Pool, left: u32, right: u32) -> u32 {
49 49
    return newNode(pool, Expr::Add(BinOp { left, right }));
50 50
}
51 51
52 -
fn sub(pool: *mut Pool, left: u32, right: u32) -> u32 {
52 +
fn sub(pool: &mut Pool, left: u32, right: u32) -> u32 {
53 53
    return newNode(pool, Expr::Sub(BinOp { left, right }));
54 54
}
55 55
56 -
fn mul(pool: *mut Pool, left: u32, right: u32) -> u32 {
56 +
fn mul(pool: &mut Pool, left: u32, right: u32) -> u32 {
57 57
    return newNode(pool, Expr::Mul(BinOp { left, right }));
58 58
}
59 59
60 -
fn div(pool: *mut Pool, left: u32, right: u32) -> u32 {
60 +
fn div(pool: &mut Pool, left: u32, right: u32) -> u32 {
61 61
    return newNode(pool, Expr::Div(BinOp { left, right }));
62 62
}
63 63
64 -
fn neg(pool: *mut Pool, child: u32) -> u32 {
64 +
fn neg(pool: &mut Pool, child: u32) -> u32 {
65 65
    return newNode(pool, Expr::Neg(child));
66 66
}
67 67
68 68
/// Recursively evaluate the expression tree rooted at nodes[idx].
69 -
fn eval(nodes: *[Expr], idx: u32) -> i32 throws (EvalError) {
69 +
fn eval(nodes: &[Expr], idx: u32) -> i32 throws (EvalError) {
70 70
    let node = nodes[idx];
71 71
    match node {
72 72
        case Expr::Num(n) => {
73 73
            return n;
74 74
        }
94 94
        }
95 95
    }
96 96
}
97 97
98 98
/// Count the total number of nodes in the tree rooted at idx.
99 -
fn countNodes(nodes: *[Expr], idx: u32) -> u32 {
99 +
fn countNodes(nodes: &[Expr], idx: u32) -> u32 {
100 100
    let node = nodes[idx];
101 101
    match node {
102 102
        case Expr::Num(_) => {
103 103
            return 1;
104 104
        }
119 119
        }
120 120
    }
121 121
}
122 122
123 123
/// Reset the node pool.
124 -
fn reset(pool: *mut Pool) {
124 +
fn reset(pool: &mut Pool) {
125 125
    set pool.count = 0;
126 126
}
127 127
128 128
/// Test 1: Simple addition: 3 + 4 = 7
129 -
fn testSimpleAdd(pool: *mut Pool) -> i32 {
129 +
fn testSimpleAdd(pool: &mut Pool) -> i32 {
130 130
    reset(pool);
131 131
    let root = add(pool, num(pool, 3), num(pool, 4));
132 132
    assert try! eval(&pool.nodes[..], root) == 7;
133 133
    return 0;
134 134
}
135 135
136 136
/// Test 2: Nested expression: (2 + 3) * (4 - 1) = 5 * 3 = 15
137 -
fn testNested(pool: *mut Pool) -> i32 {
137 +
fn testNested(pool: &mut Pool) -> i32 {
138 138
    reset(pool);
139 139
    let left = add(pool, num(pool, 2), num(pool, 3));
140 140
    let right = sub(pool, num(pool, 4), num(pool, 1));
141 141
    let root = mul(pool, left, right);
142 142
    assert try! eval(&pool.nodes[..], root) == 15;
143 143
    assert countNodes(&pool.nodes[..], root) == 7;
144 144
    return 0;
145 145
}
146 146
147 147
/// Test 3: Complex expression: ((10 + 5) * 2 - 6) / 4 = (30 - 6) / 4 = 24 / 4 = 6
148 -
fn testComplex(pool: *mut Pool) -> i32 {
148 +
fn testComplex(pool: &mut Pool) -> i32 {
149 149
    reset(pool);
150 150
    let a = add(pool, num(pool, 10), num(pool, 5));
151 151
    let b = mul(pool, a, num(pool, 2));
152 152
    let c = sub(pool, b, num(pool, 6));
153 153
    let root = div(pool, c, num(pool, 4));
154 154
    assert try! eval(&pool.nodes[..], root) == 6;
155 155
    return 0;
156 156
}
157 157
158 158
/// Test 4: Negation: -(3 + 4) = -7
159 -
fn testNeg(pool: *mut Pool) -> i32 {
159 +
fn testNeg(pool: &mut Pool) -> i32 {
160 160
    reset(pool);
161 161
    let root = neg(pool, add(pool, num(pool, 3), num(pool, 4)));
162 162
    assert try! eval(&pool.nodes[..], root) == -7;
163 163
    return 0;
164 164
}
165 165
166 166
/// Test 5: Deep tree: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36
167 -
fn testDeep(pool: *mut Pool) -> i32 {
167 +
fn testDeep(pool: &mut Pool) -> i32 {
168 168
    reset(pool);
169 169
    let mut root = num(pool, 1);
170 170
    let values: [i32; 7] = [2, 3, 4, 5, 6, 7, 8];
171 171
    for v in values {
172 172
        set root = add(pool, root, num(pool, v));
175 175
    assert countNodes(&pool.nodes[..], root) == 15;
176 176
    return 0;
177 177
}
178 178
179 179
/// Test 6: Mixed operations: (100 - 3 * (2 + 8)) / 7 = (100 - 30) / 7 = 70 / 7 = 10
180 -
fn testMixed(pool: *mut Pool) -> i32 {
180 +
fn testMixed(pool: &mut Pool) -> i32 {
181 181
    reset(pool);
182 182
    let inner = add(pool, num(pool, 2), num(pool, 8));
183 183
    let product = mul(pool, num(pool, 3), inner);
184 184
    let diff = sub(pool, num(pool, 100), product);
185 185
    let root = div(pool, diff, num(pool, 7));
186 186
    assert try! eval(&pool.nodes[..], root) == 10;
187 187
    return 0;
188 188
}
189 189
190 190
/// Test 7: Single number.
191 -
fn testSingleNum(pool: *mut Pool) -> i32 {
191 +
fn testSingleNum(pool: &mut Pool) -> i32 {
192 192
    reset(pool);
193 193
    let root = num(pool, 42);
194 194
    assert try! eval(&pool.nodes[..], root) == 42;
195 195
    assert countNodes(&pool.nodes[..], root) == 1;
196 196
    return 0;
test/tests/prog.hanoi.rad +7 -7
19 19
    moves: [Move; 63],
20 20
    count: u32,
21 21
}
22 22
23 23
/// Record a move.
24 -
fn recordMove(ml: *mut MoveLog, disk: u32, from: u32, to: u32) {
24 +
fn recordMove(ml: &mut MoveLog, disk: u32, from: u32, to: u32) {
25 25
    if ml.count < MAX_MOVES {
26 26
        set ml.moves[ml.count] = Move { disk, from, to };
27 27
        set ml.count += 1;
28 28
    }
29 29
}
30 30
31 31
/// Solve Tower of Hanoi recursively.
32 32
/// Move `n` disks from peg `from` to peg `to` using `aux` as auxiliary.
33 -
fn hanoi(ml: *mut MoveLog, n: u32, from: u32, to: u32, aux: u32) {
33 +
fn hanoi(ml: &mut MoveLog, n: u32, from: u32, to: u32, aux: u32) {
34 34
    if n == 0 {
35 35
        return;
36 36
    }
37 37
    hanoi(ml, n - 1, from, aux, to);
38 38
    recordMove(ml, n, from, to);
39 39
    hanoi(ml, n - 1, aux, to, from);
40 40
}
41 41
42 42
/// Verify total move count.
43 -
fn testMoveCount(ml: *MoveLog) -> i32 {
43 +
fn testMoveCount(ml: &MoveLog) -> i32 {
44 44
    // For N disks, there are 2^N - 1 moves.
45 45
    assert ml.count == MAX_MOVES;
46 46
    return 0;
47 47
}
48 48
49 49
/// Verify specific moves.
50 -
fn testSpecificMoves(ml: *MoveLog) -> i32 {
50 +
fn testSpecificMoves(ml: &MoveLog) -> i32 {
51 51
    // First move: smallest disk (1) from peg 0 to peg 2.
52 52
    assert ml.moves[0].disk == 1;
53 53
    assert ml.moves[0].from == 0;
54 54
    assert ml.moves[0].to == 2;
55 55
70 70
record Pegs: Copy {
71 71
    stacks: [[u32; 6]; 3],
72 72
    top: [u32; 3],
73 73
}
74 74
75 -
fn pegPush(pegs: *mut Pegs, peg: u32, disk: u32) -> bool {
75 +
fn pegPush(pegs: &mut Pegs, peg: u32, disk: u32) -> bool {
76 76
    let t = pegs.top[peg];
77 77
    if t > 0 {
78 78
        // Check that the top disk is larger than the one being placed.
79 79
        if pegs.stacks[peg][t - 1] < disk {
80 80
            return false;
83 83
    set pegs.stacks[peg][t] = disk;
84 84
    set pegs.top[peg] = t + 1;
85 85
    return true;
86 86
}
87 87
88 -
fn pegPop(pegs: *mut Pegs, peg: u32) -> u32 {
88 +
fn pegPop(pegs: &mut Pegs, peg: u32) -> u32 {
89 89
    let t = pegs.top[peg];
90 90
    if t == 0 {
91 91
        // Should not happen in a valid solution.
92 92
        return 0;
93 93
    }
97 97
98 98
/// Simulate the peg state to verify correctness.
99 99
/// Replay all moves and check:
100 100
/// 1. No larger disk is placed on a smaller disk.
101 101
/// 2. All disks end up on peg 1.
102 -
fn testSimulate(ml: *MoveLog) -> i32 {
102 +
fn testSimulate(ml: &MoveLog) -> i32 {
103 103
    let mut pegs = Pegs {
104 104
        stacks: [[0; 6]; 3],
105 105
        top: [0, 0, 0],
106 106
    };
107 107
test/tests/prog.huffman.rad +26 -26
23 23
}
24 24
25 25
constant NIL: u32 = 0xFFFFFFFF;
26 26
27 27
record HuffState: Copy {
28 -
    nodes: *mut [HNode],
28 +
    nodes: *unsafe mut [HNode],
29 29
    nodeCount: u32,
30 -
    heap: *mut [u32],
30 +
    heap: *unsafe mut [u32],
31 31
    heapSize: u32,
32 -
    codeBits: *mut [u32],
33 -
    codeLen: *mut [u32],
34 -
    bitstream: *mut [u8],
32 +
    codeBits: *unsafe mut [u32],
33 +
    codeLen: *unsafe mut [u32],
34 +
    bitstream: *unsafe mut [u8],
35 35
    bitCount: u32,
36 36
}
37 37
38 -
fn newLeaf(s: *mut HuffState, freq: u32, symbol: u32) -> u32 {
38 +
unsafe fn newLeaf(s: *unsafe mut HuffState, freq: u32, symbol: u32) -> u32 {
39 39
    let idx: u32 = s.nodeCount;
40 40
    set s.nodes[idx] = HNode { freq, kind: HNodeKind::Leaf(symbol), left: NIL, right: NIL };
41 41
    set s.nodeCount += 1;
42 42
    return idx;
43 43
}
44 44
45 -
fn newInterior(s: *mut HuffState, freq: u32, left: u32, right: u32) -> u32 {
45 +
unsafe fn newInterior(s: *unsafe mut HuffState, freq: u32, left: u32, right: u32) -> u32 {
46 46
    let idx: u32 = s.nodeCount;
47 47
    set s.nodes[idx] = HNode { freq, kind: HNodeKind::Interior, left, right };
48 48
    set s.nodeCount += 1;
49 49
    return idx;
50 50
}
51 51
52 52
/// Get the symbol from a node, or nil if it's an interior node.
53 -
fn nodeSymbol(node: *HNode) -> ?u32 {
53 +
unsafe fn nodeSymbol(node: *unsafe HNode) -> ?u32 {
54 54
    match node.kind {
55 55
        case HNodeKind::Leaf(sym) => {
56 56
            return sym;
57 57
        }
58 58
        case HNodeKind::Interior => {
59 59
            return nil;
60 60
        }
61 61
    }
62 62
}
63 63
64 -
fn heapSwap(s: *mut HuffState, i: u32, j: u32) {
64 +
unsafe fn heapSwap(s: *unsafe mut HuffState, i: u32, j: u32) {
65 65
    let tmp: u32 = s.heap[i];
66 66
    set s.heap[i] = s.heap[j];
67 67
    set s.heap[j] = tmp;
68 68
}
69 69
70 -
fn heapFreq(s: *HuffState, i: u32) -> u32 {
70 +
unsafe fn heapFreq(s: *unsafe HuffState, i: u32) -> u32 {
71 71
    return s.nodes[s.heap[i]].freq;
72 72
}
73 73
74 -
fn siftUp(s: *mut HuffState, pos: u32) {
74 +
unsafe fn siftUp(s: *unsafe mut HuffState, pos: u32) {
75 75
    let mut i: u32 = pos;
76 76
    while i > 0 {
77 77
        let parent: u32 = (i - 1) / 2;
78 78
        if heapFreq(s, i) < heapFreq(s, parent) {
79 79
            heapSwap(s, i, parent);
82 82
            return;
83 83
        }
84 84
    }
85 85
}
86 86
87 -
fn siftDown(s: *mut HuffState, pos: u32) {
87 +
unsafe fn siftDown(s: *unsafe mut HuffState, pos: u32) {
88 88
    let mut i: u32 = pos;
89 89
    while true {
90 90
        let left: u32 = 2 * i + 1;
91 91
        let right: u32 = 2 * i + 2;
92 92
        let mut smallest: u32 = i;
103 103
        heapSwap(s, i, smallest);
104 104
        set i = smallest;
105 105
    }
106 106
}
107 107
108 -
fn heapPush(s: *mut HuffState, nodeIdx: u32) {
108 +
unsafe fn heapPush(s: *unsafe mut HuffState, nodeIdx: u32) {
109 109
    set s.heap[s.heapSize] = nodeIdx;
110 110
    set s.heapSize += 1;
111 111
    siftUp(s, s.heapSize - 1);
112 112
}
113 113
114 -
fn heapPop(s: *mut HuffState) -> u32 {
114 +
unsafe fn heapPop(s: *unsafe mut HuffState) -> u32 {
115 115
    let result: u32 = s.heap[0];
116 116
    set s.heapSize -= 1;
117 117
    set s.heap[0] = s.heap[s.heapSize];
118 118
    if s.heapSize > 0 {
119 119
        siftDown(s, 0);
120 120
    }
121 121
    return result;
122 122
}
123 123
124 -
fn buildTree(s: *mut HuffState, freqs: *[u32]) -> u32 {
124 +
unsafe fn buildTree(s: *unsafe mut HuffState, freqs: *unsafe [u32]) -> u32 {
125 125
    set s.nodeCount = 0;
126 126
    set s.heapSize = 0;
127 127
128 128
    for freq, sym in freqs {
129 129
        if freq > 0 {
141 141
    }
142 142
143 143
    return heapPop(s);
144 144
}
145 145
146 -
fn generateCodes(s: *mut HuffState, nodeIdx: u32, code: u32, depth: u32) {
146 +
unsafe fn generateCodes(s: *unsafe mut HuffState, nodeIdx: u32, code: u32, depth: u32) {
147 147
    match s.nodes[nodeIdx].kind {
148 148
        case HNodeKind::Leaf(sym) => {
149 149
            set s.codeBits[sym] = code;
150 150
            set s.codeLen[sym] = depth;
151 151
        }
158 158
            }
159 159
        }
160 160
    }
161 161
}
162 162
163 -
fn writeBit(s: *mut HuffState, bit: u32) {
163 +
unsafe fn writeBit(s: *unsafe mut HuffState, bit: u32) {
164 164
    let byteIdx: u32 = s.bitCount / 8;
165 165
    let bitIdx: u32 = 7 - (s.bitCount % 8);
166 166
    if bit == 1 {
167 167
        set s.bitstream[byteIdx] |= (1 as u8 << bitIdx as u8);
168 168
    }
169 169
    set s.bitCount += 1;
170 170
}
171 171
172 -
fn readBit(s: *HuffState, pos: u32) -> u32 {
172 +
unsafe fn readBit(s: *unsafe HuffState, pos: u32) -> u32 {
173 173
    let byteIdx: u32 = pos / 8;
174 174
    let bitIdx: u32 = 7 - (pos % 8);
175 175
    return (s.bitstream[byteIdx] >> bitIdx as u8) as u32 & 1;
176 176
}
177 177
178 -
fn encode(s: *mut HuffState, msg: *[u32]) {
178 +
unsafe fn encode(s: *unsafe mut HuffState, msg: *unsafe [u32]) {
179 179
    set s.bitCount = 0;
180 180
    let mut i: u32 = 0;
181 181
    while i < MAX_BITS {
182 182
        set s.bitstream[i] = 0;
183 183
        set i += 1;
193 193
            set b += 1;
194 194
        }
195 195
    }
196 196
}
197 197
198 -
fn decode(s: *HuffState, root: u32, numSymbols: u32, out: *mut [u32]) -> u32 {
198 +
unsafe fn decode(s: *unsafe HuffState, root: u32, numSymbols: u32, out: *unsafe mut [u32]) -> u32 {
199 199
    let mut bitPos: u32 = 0;
200 200
    let mut decoded: u32 = 0;
201 201
202 202
    while decoded < numSymbols {
203 203
        let mut cur: u32 = root;
218 218
        set decoded += 1;
219 219
    }
220 220
    return decoded;
221 221
}
222 222
223 -
fn resetCodes(s: *mut HuffState) {
223 +
unsafe fn resetCodes(s: *unsafe mut HuffState) {
224 224
    let mut i: u32 = 0;
225 225
    while i < MAX_SYMBOLS {
226 226
        set s.codeBits[i] = 0;
227 227
        set s.codeLen[i] = 0;
228 228
        set i += 1;
229 229
    }
230 230
}
231 231
232 -
fn testBasic(s: *mut HuffState) -> i32 {
232 +
unsafe fn testBasic(s: *unsafe mut HuffState) -> i32 {
233 233
    let freqs: [u32; 5] = [5, 9, 12, 13, 16];
234 234
    let root: u32 = buildTree(s, &freqs[..]);
235 235
236 236
    assert s.nodes[root].freq == 55;
237 237
259 259
    }
260 260
261 261
    return 0;
262 262
}
263 263
264 -
fn testRoundTrip(s: *mut HuffState) -> i32 {
264 +
unsafe fn testRoundTrip(s: *unsafe mut HuffState) -> i32 {
265 265
    let freqs: [u32; 5] = [5, 9, 12, 13, 16];
266 266
    let root: u32 = buildTree(s, &freqs[..]);
267 267
268 268
    resetCodes(s);
269 269
    generateCodes(s, root, 0, 0);
284 284
    }
285 285
286 286
    return 0;
287 287
}
288 288
289 -
fn testSkewed(s: *mut HuffState) -> i32 {
289 +
unsafe fn testSkewed(s: *unsafe mut HuffState) -> i32 {
290 290
    let freqs: [u32; 6] = [100, 1, 1, 1, 1, 1];
291 291
    let root: u32 = buildTree(s, &freqs[..]);
292 292
293 293
    resetCodes(s);
294 294
    generateCodes(s, root, 0, 0);
318 318
    }
319 319
320 320
    return 0;
321 321
}
322 322
323 -
fn testUniform(s: *mut HuffState) -> i32 {
323 +
unsafe fn testUniform(s: *unsafe mut HuffState) -> i32 {
324 324
    let freqs: [u32; 8] = [10, 10, 10, 10, 10, 10, 10, 10];
325 325
    let root: u32 = buildTree(s, &freqs[..]);
326 326
327 327
    assert s.nodes[root].freq == 80;
328 328
349 349
    }
350 350
351 351
    return 0;
352 352
}
353 353
354 -
@default fn main() -> i32 {
354 +
@default unsafe fn main() -> i32 {
355 355
    let mut nodes: [HNode; 63] = [HNode { freq: 0, kind: HNodeKind::Interior, left: 0xFFFFFFFF, right: 0xFFFFFFFF }; 63];
356 356
    let mut heap: [u32; 63] = [0; 63];
357 357
    let mut codeBits: [u32; 32] = [0; 32];
358 358
    let mut codeLen: [u32; 32] = [0; 32];
359 359
    let mut bitstream: [u8; 512] = [0; 512];
test/tests/prog.hybridsort.rad +7 -7
2 2
//! Insertion sort and selection sort.
3 3
//! Sort two copies of the same data with each algorithm, then verify
4 4
//! both produce identical sorted output.
5 5
6 6
/// Copy `src` into `dst`.
7 -
fn copy(dst: *mut [i32], src: *[i32]) {
7 +
fn copy(dst: &mut [i32], src: &[i32]) {
8 8
    for val, i in src {
9 9
        set dst[i] = val;
10 10
    }
11 11
}
12 12
13 13
/// Insertion sort on the given array.
14 -
fn insertionSort(data: *mut [i32]) {
14 +
fn insertionSort(data: &mut [i32]) {
15 15
    let mut i: u32 = 1;
16 16
    while i < data.len {
17 17
        let key = data[i];
18 18
        let mut j: i32 = i as i32 - 1;
19 19
        while j >= 0 and data[j as u32] > key {
24 24
        set i += 1;
25 25
    }
26 26
}
27 27
28 28
/// Selection sort on the given array.
29 -
fn selectionSort(data: *mut [i32]) {
29 +
fn selectionSort(data: &mut [i32]) {
30 30
    let mut i: u32 = 0;
31 31
    while i < data.len - 1 {
32 32
        let mut minIdx: u32 = i;
33 33
        let mut j: u32 = i + 1;
34 34
        while j < data.len {
45 45
        set i += 1;
46 46
    }
47 47
}
48 48
49 49
/// Check that an array is sorted in ascending order.
50 -
fn isSorted(data: *[i32]) -> bool {
50 +
fn isSorted(data: &[i32]) -> bool {
51 51
    let mut prev: ?i32 = nil;
52 52
    for val in data {
53 53
        if let p = prev {
54 54
            if p > val {
55 55
                return false;
59 59
    }
60 60
    return true;
61 61
}
62 62
63 63
/// Compute sum of all elements.
64 -
fn sum(data: *[i32]) -> i32 {
64 +
fn sum(data: &[i32]) -> i32 {
65 65
    let mut total: i32 = 0;
66 66
    for val in data {
67 67
        set total += val;
68 68
    }
69 69
    return total;
70 70
}
71 71
72 72
/// Compare two arrays element by element.
73 -
fn arraysEqual(a: *[i32], b: *[i32]) -> i32 {
73 +
fn arraysEqual(a: &[i32], b: &[i32]) -> i32 {
74 74
    for val, i in a {
75 75
        if val <> b[i] {
76 76
            return i as i32 + 1;
77 77
        }
78 78
    }
79 79
    return 0;
80 80
}
81 81
82 82
/// Verify specific positions in the sorted output.
83 -
fn verifyPositions(data: *[i32]) -> i32 {
83 +
fn verifyPositions(data: &[i32]) -> i32 {
84 84
    // Sorted: 2 3 6 8 10 14 19 25 27 33 39 41 45 48 53 56 62 67 72 74 81 88 91 97
85 85
    let expected: [i32; 6] = [2, 3, 6, 41, 91, 97];
86 86
    let indices: [u32; 6] = [0, 1, 2, 11, 22, 23];
87 87
88 88
    for exp, i in expected {
test/tests/prog.linkedlist.rad +14 -14
15 15
    free: u32,
16 16
    head: ?u32,
17 17
}
18 18
19 19
/// Allocate a node from the pool. Returns its index.
20 -
fn alloc(list: *mut List, value: i32) -> u32 {
20 +
fn alloc(list: &mut List, value: i32) -> u32 {
21 21
    let idx = list.free;
22 22
    set list.free += 1;
23 23
    set list.pool[idx] = Node { value, next: nil };
24 24
    return idx;
25 25
}
26 26
27 27
/// Push a value onto the front of the list.
28 -
fn push(list: *mut List, value: i32) {
28 +
fn push(list: &mut List, value: i32) {
29 29
    let idx = alloc(list, value);
30 30
    set list.pool[idx].next = list.head;
31 31
    set list.head = idx;
32 32
}
33 33
34 34
/// Pop a value from the front of the list. Returns nil if empty.
35 -
fn pop(list: *mut List) -> ?i32 {
35 +
fn pop(list: &mut List) -> ?i32 {
36 36
    let idx = list.head else {
37 37
        return nil;
38 38
    };
39 39
    let value = list.pool[idx].value;
40 40
    set list.head = list.pool[idx].next;
41 41
    return value;
42 42
}
43 43
44 44
/// Get the length of the list.
45 -
fn length(list: *List) -> u32 {
45 +
fn length(list: &List) -> u32 {
46 46
    let mut count: u32 = 0;
47 47
    let mut cur = list.head;
48 48
    while let idx = cur {
49 49
        set count += 1;
50 50
        set cur = list.pool[idx].next;
51 51
    }
52 52
    return count;
53 53
}
54 54
55 55
/// Find a value in the list. Returns the node index if found, or nil.
56 -
fn find(list: *List, value: i32) -> ?u32 {
56 +
fn find(list: &List, value: i32) -> ?u32 {
57 57
    let mut cur = list.head;
58 58
    while let idx = cur {
59 59
        if list.pool[idx].value == value {
60 60
            return idx;
61 61
        }
63 63
    }
64 64
    return nil;
65 65
}
66 66
67 67
/// Get the value at a given index (0-based from head).
68 -
fn valueAt(list: *List, index: u32) -> ?i32 {
68 +
fn valueAt(list: &List, index: u32) -> ?i32 {
69 69
    let mut cur = list.head;
70 70
    let mut i: u32 = 0;
71 71
    while let idx = cur {
72 72
        if i == index {
73 73
            return list.pool[idx].value;
77 77
    }
78 78
    return nil;
79 79
}
80 80
81 81
/// Reverse the linked list in-place.
82 -
fn reverse(list: *mut List) {
82 +
fn reverse(list: &mut List) {
83 83
    let mut prev: ?u32 = nil;
84 84
    let mut cur = list.head;
85 85
    while let idx = cur {
86 86
        let next = list.pool[idx].next;
87 87
        set list.pool[idx].next = prev;
90 90
    }
91 91
    set list.head = prev;
92 92
}
93 93
94 94
/// Compute the sum of all values in the list.
95 -
fn sum(list: *List) -> i32 {
95 +
fn sum(list: &List) -> i32 {
96 96
    let mut total: i32 = 0;
97 97
    let mut cur = list.head;
98 98
    while let idx = cur {
99 99
        set total += list.pool[idx].value;
100 100
        set cur = list.pool[idx].next;
101 101
    }
102 102
    return total;
103 103
}
104 104
105 105
/// Reset the list to empty.
106 -
fn reset(list: *mut List) {
106 +
fn reset(list: &mut List) {
107 107
    set list.head = nil;
108 108
    set list.free = 0;
109 109
}
110 110
111 111
/// Test basic push and length.
112 -
fn testPushLength(list: *mut List) -> i32 {
112 +
fn testPushLength(list: &mut List) -> i32 {
113 113
    push(list, 10);
114 114
    push(list, 20);
115 115
    push(list, 30);
116 116
117 117
    assert length(list) == 3;
130 130
    assert v2 == 10;
131 131
    return 0;
132 132
}
133 133
134 134
/// Test pop.
135 -
fn testPop(list: *mut List) -> i32 {
135 +
fn testPop(list: &mut List) -> i32 {
136 136
    let v = pop(list) else {
137 137
        return 1;
138 138
    };
139 139
    assert v == 30;
140 140
    assert length(list) == 2;
144 144
    assert v0 == 20;
145 145
    return 0;
146 146
}
147 147
148 148
/// Test find.
149 -
fn testFind(list: *mut List) -> i32 {
149 +
fn testFind(list: &mut List) -> i32 {
150 150
    // 20 should be found.
151 151
    assert find(list, 20) <> nil;
152 152
    // 10 should be found.
153 153
    assert find(list, 10) <> nil;
154 154
    // 30 was popped, should not be found.
157 157
    assert find(list, 99) == nil;
158 158
    return 0;
159 159
}
160 160
161 161
/// Test reverse.
162 -
fn testReverse(list: *mut List) -> i32 {
162 +
fn testReverse(list: &mut List) -> i32 {
163 163
    // Current list: 20 -> 10
164 164
    // Add more elements.
165 165
    push(list, 40);
166 166
    push(list, 50);
167 167
    // List: 50 -> 40 -> 20 -> 10
193 193
    assert sum(list) == sumBefore;
194 194
    return 0;
195 195
}
196 196
197 197
/// Test building a larger list.
198 -
fn testLargerList(list: *mut List) -> i32 {
198 +
fn testLargerList(list: &mut List) -> i32 {
199 199
    reset(list);
200 200
201 201
    // Push 32 elements.
202 202
    let mut i: u32 = 0;
203 203
    while i < 32 {
test/tests/prog.lzw.rad +23 -23
12 12
    prefix: u32,
13 13
    suffix: u8,
14 14
}
15 15
16 16
record LzwState: Copy {
17 -
    encDict: *mut [DictEntry],
17 +
    encDict: *unsafe mut [DictEntry],
18 18
    encDictSize: u32,
19 -
    decDict: *mut [DictEntry],
19 +
    decDict: *unsafe mut [DictEntry],
20 20
    decDictSize: u32,
21 -
    encoded: *mut [u32],
21 +
    encoded: *unsafe mut [u32],
22 22
    encLen: u32,
23 -
    decoded: *mut [u8],
23 +
    decoded: *unsafe mut [u8],
24 24
    decLen: u32,
25 -
    temp: *mut [u8],
25 +
    temp: *unsafe mut [u8],
26 26
}
27 27
28 -
fn initEncDict(s: *mut LzwState) {
28 +
unsafe fn initEncDict(s: *unsafe mut LzwState) {
29 29
    set s.encDictSize = INIT_DICT;
30 30
    let mut i: u32 = 0;
31 31
    while i < 256 {
32 32
        set s.encDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 };
33 33
        set i += 1;
34 34
    }
35 35
}
36 36
37 -
fn initDecDict(s: *mut LzwState) {
37 +
unsafe fn initDecDict(s: *unsafe mut LzwState) {
38 38
    set s.decDictSize = INIT_DICT;
39 39
    let mut i: u32 = 0;
40 40
    while i < 256 {
41 41
        set s.decDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 };
42 42
        set i += 1;
43 43
    }
44 44
}
45 45
46 -
fn dictLookup(s: *LzwState, prefix: u32, suffix: u8) -> u32 {
46 +
unsafe fn dictLookup(s: *unsafe LzwState, prefix: u32, suffix: u8) -> u32 {
47 47
    let mut i: u32 = 0;
48 48
    while i < s.encDictSize {
49 49
        if s.encDict[i].prefix == prefix and s.encDict[i].suffix == suffix {
50 50
            return i;
51 51
        }
52 52
        set i += 1;
53 53
    }
54 54
    return 0xFFFFFFFF;
55 55
}
56 56
57 -
fn dictAdd(s: *mut LzwState, prefix: u32, suffix: u8) {
57 +
unsafe fn dictAdd(s: *unsafe mut LzwState, prefix: u32, suffix: u8) {
58 58
    if s.encDictSize < MAX_DICT {
59 59
        set s.encDict[s.encDictSize] = DictEntry { prefix, suffix };
60 60
        set s.encDictSize += 1;
61 61
    }
62 62
}
63 63
64 -
fn emitCode(s: *mut LzwState, code: u32) {
64 +
unsafe fn emitCode(s: *unsafe mut LzwState, code: u32) {
65 65
    set s.encoded[s.encLen] = code;
66 66
    set s.encLen += 1;
67 67
}
68 68
69 -
fn encode(s: *mut LzwState, data: *[u8]) {
69 +
unsafe fn encode(s: *unsafe mut LzwState, data: &[u8]) {
70 70
    initEncDict(s);
71 71
    set s.encLen = 0;
72 72
73 73
    if data.len == 0 {
74 74
        emitCode(s, EOI_CODE);
92 92
    }
93 93
    emitCode(s, w);
94 94
    emitCode(s, EOI_CODE);
95 95
}
96 96
97 -
fn decodeString(s: *mut LzwState, code: u32) -> u32 {
97 +
unsafe fn decodeString(s: *unsafe mut LzwState, code: u32) -> u32 {
98 98
    let mut len: u32 = 0;
99 99
    let mut c: u32 = code;
100 100
    while c <> 0xFFFF and c < s.decDictSize {
101 101
        set s.temp[len] = s.decDict[c].suffix;
102 102
        set len += 1;
112 112
        set b -= 1;
113 113
    }
114 114
    return len;
115 115
}
116 116
117 -
fn firstByte(s: *LzwState, code: u32) -> u8 {
117 +
unsafe fn firstByte(s: *unsafe LzwState, code: u32) -> u8 {
118 118
    let mut c: u32 = code;
119 119
    while s.decDict[c].prefix <> 0xFFFF and s.decDict[c].prefix < s.decDictSize {
120 120
        set c = s.decDict[c].prefix;
121 121
    }
122 122
    return s.decDict[c].suffix;
123 123
}
124 124
125 -
fn decDictAdd(s: *mut LzwState, prefix: u32, suffix: u8) {
125 +
unsafe fn decDictAdd(s: *unsafe mut LzwState, prefix: u32, suffix: u8) {
126 126
    if s.decDictSize < MAX_DICT {
127 127
        set s.decDict[s.decDictSize] = DictEntry { prefix, suffix };
128 128
        set s.decDictSize += 1;
129 129
    }
130 130
}
131 131
132 -
fn outputByte(s: *mut LzwState, b: u8) {
132 +
unsafe fn outputByte(s: *unsafe mut LzwState, b: u8) {
133 133
    set s.decoded[s.decLen] = b;
134 134
    set s.decLen += 1;
135 135
}
136 136
137 -
fn decode(s: *mut LzwState) {
137 +
unsafe fn decode(s: *unsafe mut LzwState) {
138 138
    initDecDict(s);
139 139
    set s.decLen = 0;
140 140
141 141
    if s.encLen == 0 {
142 142
        return;
182 182
        }
183 183
        set prevCode = code;
184 184
    }
185 185
}
186 186
187 -
fn testSimple(s: *mut LzwState) -> i32 {
187 +
unsafe fn testSimple(s: *unsafe mut LzwState) -> i32 {
188 188
    let data: *[u8] = "ABABABABAB";
189 189
    encode(s, data);
190 190
191 191
    assert s.encoded[s.encLen - 1] == EOI_CODE;
192 192
198 198
        set i += 1;
199 199
    }
200 200
    return 0;
201 201
}
202 202
203 -
fn testDistinct(s: *mut LzwState) -> i32 {
203 +
unsafe fn testDistinct(s: *unsafe mut LzwState) -> i32 {
204 204
    let data: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
205 205
    encode(s, &data[..]);
206 206
207 207
    decode(s);
208 208
    assert s.decLen == 16;
212 212
        set i += 1;
213 213
    }
214 214
    return 0;
215 215
}
216 216
217 -
fn testRepetitive(s: *mut LzwState) -> i32 {
217 +
unsafe fn testRepetitive(s: *unsafe mut LzwState) -> i32 {
218 218
    let mut data: [u8; 64] = [65; 64];
219 219
    encode(s, &data[..]);
220 220
221 221
    assert s.encLen < 32;
222 222
228 228
        set i += 1;
229 229
    }
230 230
    return 0;
231 231
}
232 232
233 -
fn testMixed(s: *mut LzwState) -> i32 {
233 +
unsafe fn testMixed(s: *unsafe mut LzwState) -> i32 {
234 234
    let data: *[u8] = "TOBEORNOTTOBEORTOBEORNOT";
235 235
    encode(s, data);
236 236
237 237
    decode(s);
238 238
    assert s.decLen == 24;
245 245
    assert s.encLen - 1 < 24;
246 246
247 247
    return 0;
248 248
}
249 249
250 -
fn testEmpty(s: *mut LzwState) -> i32 {
250 +
unsafe fn testEmpty(s: *unsafe mut LzwState) -> i32 {
251 251
    encode(s, &[]);
252 252
    assert s.encLen == 1;
253 253
    assert s.encoded[0] == EOI_CODE;
254 254
255 255
    decode(s);
256 256
    assert s.decLen == 0;
257 257
    return 0;
258 258
}
259 259
260 -
fn testSingle(s: *mut LzwState) -> i32 {
260 +
unsafe fn testSingle(s: *unsafe mut LzwState) -> i32 {
261 261
    let data: *[u8] = "*";
262 262
    encode(s, data);
263 263
264 264
    decode(s);
265 265
    assert s.decLen == 1;
266 266
    assert s.decoded[0] == 42;
267 267
    return 0;
268 268
}
269 269
270 -
@default fn main() -> i32 {
270 +
@default unsafe fn main() -> i32 {
271 271
    let mut encDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512];
272 272
    let mut decDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512];
273 273
    let mut encoded: [u32; 512] = [0; 512];
274 274
    let mut decoded: [u8; 256] = [0; 256];
275 275
    let mut temp: [u8; 256] = [0; 256];
test/tests/prog.matmul.rad +5 -5
9 9
record Mat4: Copy {
10 10
    rows: [[i32; 4]; 4],
11 11
}
12 12
13 13
/// Perform matrix multiplication: c = a * b.
14 -
fn matmul(c: *mut Mat4, a: *Mat4, b: *Mat4) {
14 +
fn matmul(c: &mut Mat4, a: &Mat4, b: &Mat4) {
15 15
    let mut i: u32 = 0;
16 16
    while i < N {
17 17
        let mut j: u32 = 0;
18 18
        while j < N {
19 19
            let mut sum: i32 = 0;
28 28
        set i += 1;
29 29
    }
30 30
}
31 31
32 32
/// Verify c matches expected.
33 -
fn verifyResult(c: *Mat4, expected: *Mat4) -> i32 {
33 +
fn verifyResult(c: &Mat4, expected: &Mat4) -> i32 {
34 34
    let mut i: u32 = 0;
35 35
    while i < N {
36 36
        let mut j: u32 = 0;
37 37
        while j < N {
38 38
            if c.rows[i][j] <> expected.rows[i][j] {
44 44
    }
45 45
    return 0;
46 46
}
47 47
48 48
/// Compute the trace (sum of diagonal) of a matrix.
49 -
fn trace(m: *Mat4) -> i32 {
49 +
fn trace(m: &Mat4) -> i32 {
50 50
    let mut sum: i32 = 0;
51 51
    let mut i: u32 = 0;
52 52
    while i < N {
53 53
        set sum += m.rows[i][i];
54 54
        set i += 1;
55 55
    }
56 56
    return sum;
57 57
}
58 58
59 59
/// Zero out a matrix.
60 -
fn zero(m: *mut Mat4) {
60 +
fn zero(m: &mut Mat4) {
61 61
    let mut i: u32 = 0;
62 62
    while i < N {
63 63
        let mut j: u32 = 0;
64 64
        while j < N {
65 65
            set m.rows[i][j] = 0;
68 68
        set i += 1;
69 69
    }
70 70
}
71 71
72 72
/// Multiply matrices multiple times to test repeated computation.
73 -
fn testRepeatedMultiply(c: *mut Mat4, a: *Mat4, b: *Mat4) -> i32 {
73 +
fn testRepeatedMultiply(c: &mut Mat4, a: &Mat4, b: &Mat4) -> i32 {
74 74
    // First pass already done, trace = 13+43+43+85 = 184.
75 75
    assert trace(c) == 184;
76 76
77 77
    // Zero out c and re-multiply to ensure idempotent.
78 78
    zero(c);
test/tests/prog.mersenne.rad +11 -11
6 6
constant MATRIX_A: u32 = 0x9908B0DF;
7 7
constant UPPER_MASK: u32 = 0x80000000;
8 8
constant LOWER_MASK: u32 = 0x7FFFFFFF;
9 9
10 10
record MtState: Copy {
11 -
    mt: *mut [u32],
11 +
    mt: *unsafe mut [u32],
12 12
    mti: u32,
13 13
}
14 14
15 -
fn mtInit(s: *mut MtState, seed: u32) {
15 +
unsafe fn mtInit(s: &mut MtState, seed: u32) {
16 16
    set s.mt[0] = seed;
17 17
    let mut i: u32 = 1;
18 18
    while i < N {
19 19
        let prev: u32 = s.mt[i - 1];
20 20
        let xored: u32 = prev ^ (prev >> 30);
34 34
        set i += 1;
35 35
    }
36 36
    set s.mti = N;
37 37
}
38 38
39 -
fn generateNumbers(s: *mut MtState) {
39 +
unsafe fn generateNumbers(s: &mut MtState) {
40 40
    let mut i: u32 = 0;
41 41
42 42
    while i < N - M {
43 43
        let y: u32 = (s.mt[i] & UPPER_MASK) | (s.mt[i + 1] & LOWER_MASK);
44 44
        let mut mag: u32 = 0;
67 67
    set s.mt[N - 1] = s.mt[M - 1] ^ (y >> 1) ^ mag;
68 68
69 69
    set s.mti = 0;
70 70
}
71 71
72 -
fn mtNext(s: *mut MtState) -> u32 {
72 +
unsafe fn mtNext(s: &mut MtState) -> u32 {
73 73
    if s.mti >= N {
74 74
        generateNumbers(s);
75 75
    }
76 76
77 77
    let mut y: u32 = s.mt[s.mti];
83 83
    set y ^= (y >> 18);
84 84
85 85
    return y;
86 86
}
87 87
88 -
fn testKnownSequence(s: *mut MtState) -> i32 {
88 +
unsafe fn testKnownSequence(s: &mut MtState) -> i32 {
89 89
    mtInit(s, 1);
90 90
91 91
    let v0: u32 = mtNext(s);
92 92
    assert v0 == 1791095845;
93 93
104 104
    assert v4 == 491263;
105 105
106 106
    return 0;
107 107
}
108 108
109 -
fn testDeterminism(s: *mut MtState) -> i32 {
109 +
unsafe fn testDeterminism(s: &mut MtState) -> i32 {
110 110
    mtInit(s, 42);
111 111
112 112
    let mut first: [u32; 10] = [0; 10];
113 113
    let mut i: u32 = 0;
114 114
    while i < 10 {
126 126
    }
127 127
128 128
    return 0;
129 129
}
130 130
131 -
fn testDifferentSeeds(s: *mut MtState) -> i32 {
131 +
unsafe fn testDifferentSeeds(s: &mut MtState) -> i32 {
132 132
    mtInit(s, 1);
133 133
    let a: u32 = mtNext(s);
134 134
135 135
    mtInit(s, 2);
136 136
    let b: u32 = mtNext(s);
143 143
    assert a <> c;
144 144
145 145
    return 0;
146 146
}
147 147
148 -
fn testChiSquared(s: *mut MtState) -> i32 {
148 +
unsafe fn testChiSquared(s: &mut MtState) -> i32 {
149 149
    mtInit(s, 12345);
150 150
151 151
    constant NUM_BINS: u32 = 16;
152 152
    constant NUM_SAMPLES: u32 = 1600;
153 153
    constant EXPECTED: u32 = 100;
187 187
    }
188 188
189 189
    return 0;
190 190
}
191 191
192 -
fn testRegeneration(s: *mut MtState) -> i32 {
192 +
unsafe fn testRegeneration(s: &mut MtState) -> i32 {
193 193
    mtInit(s, 7);
194 194
195 195
    let mut last: u32 = 0;
196 196
    let mut i: u32 = 0;
197 197
    while i < 700 {
218 218
    assert more <> 0;
219 219
220 220
    return 0;
221 221
}
222 222
223 -
fn testBitCoverage(s: *mut MtState) -> i32 {
223 +
unsafe fn testBitCoverage(s: &mut MtState) -> i32 {
224 224
    mtInit(s, 999);
225 225
226 226
    let mut orAll: u32 = 0;
227 227
    let mut andAll: u32 = 0xFFFFFFFF;
228 228
239 239
    assert andAll == 0;
240 240
241 241
    return 0;
242 242
}
243 243
244 -
@default fn main() -> i32 {
244 +
@default unsafe fn main() -> i32 {
245 245
    let mut mt: [u32; 624] = [0; 624];
246 246
    let mut s: MtState = MtState {
247 247
        mt: &mut mt[..],
248 248
        mti: 625,
249 249
    };
test/tests/prog.nqueens.rad +8 -8
4 4
//! for boards of size 1 through 8 and verify against known solution counts.
5 5
6 6
constant MAX_N: u32 = 8;
7 7
8 8
record Board: Copy {
9 -
    queens: *mut [i32],
9 +
    queens: *unsafe mut [i32],
10 10
    solutionCount: u32,
11 11
    boardSize: u32,
12 12
}
13 13
14 -
fn isSafe(b: *Board, row: u32, col: u32) -> bool {
14 +
unsafe fn isSafe(b: &Board, row: u32, col: u32) -> bool {
15 15
    let mut i: u32 = 0;
16 16
    while i < row {
17 17
        let qcol: i32 = b.queens[i];
18 18
        if qcol == col as i32 {
19 19
            return false;
26 26
        set i += 1;
27 27
    }
28 28
    return true;
29 29
}
30 30
31 -
fn solve(b: *mut Board, row: u32) {
31 +
unsafe fn solve(b: &mut Board, row: u32) {
32 32
    if row == b.boardSize {
33 33
        set b.solutionCount += 1;
34 34
        return;
35 35
    }
36 36
    let mut col: u32 = 0;
42 42
        }
43 43
        set col += 1;
44 44
    }
45 45
}
46 46
47 -
fn resetBoard(b: *mut Board) {
47 +
unsafe fn resetBoard(b: &mut Board) {
48 48
    let mut i: u32 = 0;
49 49
    while i < MAX_N {
50 50
        set b.queens[i] = -1;
51 51
        set i += 1;
52 52
    }
53 53
    set b.solutionCount = 0;
54 54
}
55 55
56 -
fn solveNQueens(b: *mut Board, n: u32) -> u32 {
56 +
unsafe fn solveNQueens(b: &mut Board, n: u32) -> u32 {
57 57
    resetBoard(b);
58 58
    set b.boardSize = n;
59 59
    solve(b, 0);
60 60
    return b.solutionCount;
61 61
}
62 62
63 -
fn testAllSizes(b: *mut Board) -> i32 {
63 +
unsafe fn testAllSizes(b: &mut Board) -> i32 {
64 64
    let expected: [u32; 9] = [0, 1, 0, 0, 2, 10, 4, 40, 92];
65 65
    let mut n: u32 = 1;
66 66
    while n <= 8 {
67 67
        let count: u32 = solveNQueens(b, n);
68 68
        if count <> expected[n] {
101 101
    }
102 102
103 103
    return 0;
104 104
}
105 105
106 -
fn testDeterminism(b: *mut Board) -> i32 {
106 +
unsafe fn testDeterminism(b: &mut Board) -> i32 {
107 107
    let count1: u32 = solveNQueens(b, 8);
108 108
    let count2: u32 = solveNQueens(b, 8);
109 109
    let count3: u32 = solveNQueens(b, 8);
110 110
111 111
    assert count1 == 92;
129 129
    assert expected[8] > expected[7];
130 130
131 131
    return 0;
132 132
}
133 133
134 -
@default fn main() -> i32 {
134 +
@default unsafe fn main() -> i32 {
135 135
    let mut queens: [i32; 8] = [-1; 8];
136 136
    let mut b: Board = Board {
137 137
        queens: &mut queens[..],
138 138
        solutionCount: 0,
139 139
        boardSize: 0,
test/tests/prog.rbtree.rad +18 -18
15 15
    right: u32,
16 16
    parent: u32,
17 17
}
18 18
19 19
record RBTree: Copy {
20 -
    pool: *mut [RBNode],
20 +
    pool: *unsafe mut [RBNode],
21 21
    poolNext: u32,
22 22
    root: u32,
23 -
    inorder: *mut [i32],
23 +
    inorder: *unsafe mut [i32],
24 24
    inorderCount: u32,
25 25
}
26 26
27 -
fn allocNode(t: *mut RBTree, key: i32) -> u32 {
27 +
unsafe fn allocNode(t: *unsafe mut RBTree, key: i32) -> u32 {
28 28
    let idx: u32 = t.poolNext;
29 29
    set t.poolNext += 1;
30 30
    set t.pool[idx] = RBNode { key, color: RED, left: NIL, right: NIL, parent: NIL };
31 31
    return idx;
32 32
}
33 33
34 -
fn rotateLeft(t: *mut RBTree, x: u32) {
34 +
unsafe fn rotateLeft(t: *unsafe mut RBTree, x: u32) {
35 35
    let y: u32 = t.pool[x].right;
36 36
    set t.pool[x].right = t.pool[y].left;
37 37
    if t.pool[y].left <> NIL {
38 38
        set t.pool[t.pool[y].left].parent = x;
39 39
    }
47 47
    }
48 48
    set t.pool[y].left = x;
49 49
    set t.pool[x].parent = y;
50 50
}
51 51
52 -
fn rotateRight(t: *mut RBTree, x: u32) {
52 +
unsafe fn rotateRight(t: *unsafe mut RBTree, x: u32) {
53 53
    let y: u32 = t.pool[x].left;
54 54
    set t.pool[x].left = t.pool[y].right;
55 55
    if t.pool[y].right <> NIL {
56 56
        set t.pool[t.pool[y].right].parent = x;
57 57
    }
65 65
    }
66 66
    set t.pool[y].right = x;
67 67
    set t.pool[x].parent = y;
68 68
}
69 69
70 -
fn insertFixup(t: *mut RBTree, zArg: u32) {
70 +
unsafe fn insertFixup(t: *unsafe mut RBTree, zArg: u32) {
71 71
    let mut z: u32 = zArg;
72 72
    while t.pool[t.pool[z].parent].color == RED {
73 73
        if t.pool[z].parent == t.pool[t.pool[t.pool[z].parent].parent].left {
74 74
            let y: u32 = t.pool[t.pool[t.pool[z].parent].parent].right;
75 75
            if t.pool[y].color == RED {
105 105
        }
106 106
    }
107 107
    set t.pool[t.root].color = BLACK;
108 108
}
109 109
110 -
fn insert(t: *mut RBTree, key: i32) {
110 +
unsafe fn insert(t: *unsafe mut RBTree, key: i32) {
111 111
    let z: u32 = allocNode(t, key);
112 112
    let mut y: u32 = NIL;
113 113
    let mut x: u32 = t.root;
114 114
115 115
    while x <> NIL {
131 131
    }
132 132
133 133
    insertFixup(t, z);
134 134
}
135 135
136 -
fn search(t: *RBTree, key: i32) -> bool {
136 +
unsafe fn search(t: *unsafe RBTree, key: i32) -> bool {
137 137
    let mut x: u32 = t.root;
138 138
    while x <> NIL {
139 139
        if key == t.pool[x].key {
140 140
            return true;
141 141
        } else if key < t.pool[x].key {
145 145
        }
146 146
    }
147 147
    return false;
148 148
}
149 149
150 -
fn inorderWalk(t: *mut RBTree, x: u32) {
150 +
unsafe fn inorderWalk(t: *unsafe mut RBTree, x: u32) {
151 151
    if x == NIL {
152 152
        return;
153 153
    }
154 154
    inorderWalk(t, t.pool[x].left);
155 155
    set t.inorder[t.inorderCount] = t.pool[x].key;
156 156
    set t.inorderCount += 1;
157 157
    inorderWalk(t, t.pool[x].right);
158 158
}
159 159
160 -
fn countNodes(t: *RBTree, x: u32) -> u32 {
160 +
unsafe fn countNodes(t: *unsafe RBTree, x: u32) -> u32 {
161 161
    if x == NIL {
162 162
        return 0;
163 163
    }
164 164
    return 1 + countNodes(t, t.pool[x].left) + countNodes(t, t.pool[x].right);
165 165
}
166 166
167 -
fn blackHeight(t: *RBTree, x: u32) -> i32 {
167 +
unsafe fn blackHeight(t: *unsafe RBTree, x: u32) -> i32 {
168 168
    if x == NIL {
169 169
        return 1;
170 170
    }
171 171
    let leftBH: i32 = blackHeight(t, t.pool[x].left);
172 172
    let rightBH: i32 = blackHeight(t, t.pool[x].right);
182 182
        return leftBH + 1;
183 183
    }
184 184
    return leftBH;
185 185
}
186 186
187 -
fn noRedRed(t: *RBTree, x: u32) -> bool {
187 +
unsafe fn noRedRed(t: *unsafe RBTree, x: u32) -> bool {
188 188
    if x == NIL {
189 189
        return true;
190 190
    }
191 191
    if t.pool[x].color == RED {
192 192
        if t.pool[t.pool[x].left].color == RED {
200 200
        return false;
201 201
    }
202 202
    return noRedRed(t, t.pool[x].right);
203 203
}
204 204
205 -
fn resetTree(t: *mut RBTree) {
205 +
unsafe fn resetTree(t: *unsafe mut RBTree) {
206 206
    let mut i: u32 = 0;
207 207
    while i < POOL_SIZE {
208 208
        set t.pool[i] = RBNode { key: 0, color: BLACK, left: NIL, right: NIL, parent: NIL };
209 209
        set i += 1;
210 210
    }
211 211
    set t.poolNext = 1;
212 212
    set t.root = NIL;
213 213
    set t.inorderCount = 0;
214 214
}
215 215
216 -
fn testAscending(t: *mut RBTree) -> i32 {
216 +
unsafe fn testAscending(t: *unsafe mut RBTree) -> i32 {
217 217
    resetTree(t);
218 218
219 219
    let mut i: i32 = 0;
220 220
    while i < 32 {
221 221
        insert(t, i);
240 240
    }
241 241
242 242
    return 0;
243 243
}
244 244
245 -
fn testDescending(t: *mut RBTree) -> i32 {
245 +
unsafe fn testDescending(t: *unsafe mut RBTree) -> i32 {
246 246
    resetTree(t);
247 247
248 248
    let mut i: i32 = 31;
249 249
    while i >= 0 {
250 250
        insert(t, i);
267 267
    }
268 268
269 269
    return 0;
270 270
}
271 271
272 -
fn testRandom(t: *mut RBTree) -> i32 {
272 +
unsafe fn testRandom(t: *unsafe mut RBTree) -> i32 {
273 273
    resetTree(t);
274 274
275 275
    let mut inserted: [bool; 48] = [false; 48];
276 276
    let mut seed: u32 = 42;
277 277
    let mut count: u32 = 0;
310 310
    }
311 311
312 312
    return 0;
313 313
}
314 314
315 -
fn testHeight(t: *mut RBTree) -> i32 {
315 +
unsafe fn testHeight(t: *unsafe mut RBTree) -> i32 {
316 316
    resetTree(t);
317 317
318 318
    let mut i: i32 = 0;
319 319
    while i < 63 {
320 320
        insert(t, i);
326 326
    assert bh <= 7;
327 327
328 328
    return 0;
329 329
}
330 330
331 -
@default fn main() -> i32 {
331 +
@default unsafe fn main() -> i32 {
332 332
    let mut pool: [RBNode; 128] = [RBNode { key: 0, color: 1, left: 0, right: 0, parent: 0 }; 128];
333 333
    let mut inorder: [i32; 128] = [0; 128];
334 334
335 335
    let mut t: RBTree = RBTree {
336 336
        pool: &mut pool[..],
test/tests/prog.regex.rad +28 -28
22 22
    start: u32,
23 23
    endState: u32,
24 24
}
25 25
26 26
record NfaState: Copy {
27 -
    trans: *mut [Trans],
27 +
    trans: *unsafe mut [Trans],
28 28
    transCount: u32,
29 -
    stateFirst: *mut [u32],
30 -
    transNext: *mut [u32],
29 +
    stateFirst: *unsafe mut [u32],
30 +
    transNext: *unsafe mut [u32],
31 31
    stateCount: u32,
32 32
    acceptState: u32,
33 -
    current: *mut [u32],
34 -
    nextSet: *mut [u32],
35 -
    closure: *mut [u32],
36 -
    fragStack: *mut [Frag],
33 +
    current: *unsafe mut [u32],
34 +
    nextSet: *unsafe mut [u32],
35 +
    closure: *unsafe mut [u32],
36 +
    fragStack: *unsafe mut [Frag],
37 37
    fragTop: u32,
38 38
}
39 39
40 -
fn newState(nfa: *mut NfaState) -> u32 {
40 +
unsafe fn newState(nfa: *unsafe mut NfaState) -> u32 {
41 41
    let s: u32 = nfa.stateCount;
42 42
    set nfa.stateFirst[s] = NIL;
43 43
    set nfa.stateCount += 1;
44 44
    return s;
45 45
}
46 46
47 -
fn addTrans(nfa: *mut NfaState, from: u32, kind: u32, ch: u8, to: u32) {
47 +
unsafe fn addTrans(nfa: *unsafe mut NfaState, from: u32, kind: u32, ch: u8, to: u32) {
48 48
    let idx: u32 = nfa.transCount;
49 49
    set nfa.trans[idx] = Trans { kind, ch, to };
50 50
    set nfa.transNext[idx] = nfa.stateFirst[from];
51 51
    set nfa.stateFirst[from] = idx;
52 52
    set nfa.transCount += 1;
53 53
}
54 54
55 -
fn pushFrag(nfa: *mut NfaState, f: Frag) {
55 +
unsafe fn pushFrag(nfa: *unsafe mut NfaState, f: Frag) {
56 56
    set nfa.fragStack[nfa.fragTop] = f;
57 57
    set nfa.fragTop += 1;
58 58
}
59 59
60 -
fn popFrag(nfa: *mut NfaState) -> Frag {
60 +
unsafe fn popFrag(nfa: *unsafe mut NfaState) -> Frag {
61 61
    set nfa.fragTop -= 1;
62 62
    return nfa.fragStack[nfa.fragTop];
63 63
}
64 64
65 -
fn setEmpty(s: *mut [u32]) {
65 +
unsafe fn setEmpty(s: *unsafe mut [u32]) {
66 66
    set s[0] = 0;
67 67
    set s[1] = 0;
68 68
    set s[2] = 0;
69 69
    set s[3] = 0;
70 70
}
71 71
72 -
fn setAdd(s: *mut [u32], bit: u32) {
72 +
unsafe fn setAdd(s: *unsafe mut [u32], bit: u32) {
73 73
    let word: u32 = bit / 32;
74 74
    let pos: u32 = bit % 32;
75 75
    set s[word] |= (1 << pos);
76 76
}
77 77
78 -
fn setHas(s: *[u32], bit: u32) -> bool {
78 +
unsafe fn setHas(s: *unsafe [u32], bit: u32) -> bool {
79 79
    let word: u32 = bit / 32;
80 80
    let pos: u32 = bit % 32;
81 81
    return (s[word] >> pos) & 1 == 1;
82 82
}
83 83
84 -
fn setIsEmpty(s: *[u32]) -> bool {
84 +
unsafe fn setIsEmpty(s: *unsafe [u32]) -> bool {
85 85
    return s[0] == 0 and s[1] == 0 and s[2] == 0 and s[3] == 0;
86 86
}
87 87
88 -
fn setCopy(dst: *mut [u32], src: *[u32]) {
88 +
unsafe fn setCopy(dst: *unsafe mut [u32], src: *unsafe [u32]) {
89 89
    set dst[0] = src[0];
90 90
    set dst[1] = src[1];
91 91
    set dst[2] = src[2];
92 92
    set dst[3] = src[3];
93 93
}
94 94
95 -
fn epsilonClosure(nfa: *mut NfaState, states: *mut [u32]) {
95 +
unsafe fn epsilonClosure(nfa: *unsafe mut NfaState, states: *unsafe mut [u32]) {
96 96
    setCopy(nfa.closure, states);
97 97
98 98
    let mut changed: bool = true;
99 99
    while changed {
100 100
        set changed = false;
117 117
    }
118 118
119 119
    setCopy(states, nfa.closure);
120 120
}
121 121
122 -
fn resetNFA(nfa: *mut NfaState) {
122 +
unsafe fn resetNFA(nfa: *unsafe mut NfaState) {
123 123
    set nfa.stateCount = 0;
124 124
    set nfa.transCount = 0;
125 125
    set nfa.fragTop = 0;
126 126
    let mut i: u32 = 0;
127 127
    while i < MAX_STATES {
133 133
        set nfa.transNext[i] = NIL;
134 134
        set i += 1;
135 135
    }
136 136
}
137 137
138 -
fn compile(nfa: *mut NfaState, pattern: *[u8]) -> u32 {
138 +
unsafe fn compile(nfa: *unsafe mut NfaState, pattern: &[u8]) -> u32 {
139 139
    resetNFA(nfa);
140 140
141 141
    let mut i: u32 = 0;
142 142
    while i < pattern.len {
143 143
        let ch: u8 = pattern[i];
233 233
234 234
    set nfa.acceptState = frags[numFrags - 1].endState;
235 235
    return frags[0].start;
236 236
}
237 237
238 -
fn nfaMatches(nfa: *mut NfaState, start: u32, input: *[u8]) -> bool {
238 +
unsafe fn nfaMatches(nfa: *unsafe mut NfaState, start: u32, input: &[u8]) -> bool {
239 239
    setEmpty(nfa.current);
240 240
    setAdd(nfa.current, start);
241 241
    epsilonClosure(nfa, nfa.current);
242 242
243 243
    let mut i: u32 = 0;
271 271
    }
272 272
273 273
    return setHas(nfa.current, nfa.acceptState);
274 274
}
275 275
276 -
fn testLiteral(nfa: *mut NfaState) -> i32 {
276 +
unsafe fn testLiteral(nfa: *unsafe mut NfaState) -> i32 {
277 277
    let start: u32 = compile(nfa, "abc");
278 278
279 279
    assert nfaMatches(nfa, start, "abc");
280 280
    if nfaMatches(nfa, start, "ab") { return 2; }
281 281
    if nfaMatches(nfa, start, "abcd") { return 3; }
282 282
    if nfaMatches(nfa, start, "abd") { return 4; }
283 283
284 284
    return 0;
285 285
}
286 286
287 -
fn testStar(nfa: *mut NfaState) -> i32 {
287 +
unsafe fn testStar(nfa: *unsafe mut NfaState) -> i32 {
288 288
    let start: u32 = compile(nfa, "a*");
289 289
290 290
    assert nfaMatches(nfa, start, "");
291 291
    assert nfaMatches(nfa, start, "a");
292 292
    assert nfaMatches(nfa, start, "aaa");
293 293
    if nfaMatches(nfa, start, "b") { return 4; }
294 294
295 295
    return 0;
296 296
}
297 297
298 -
fn testPlus(nfa: *mut NfaState) -> i32 {
298 +
unsafe fn testPlus(nfa: *unsafe mut NfaState) -> i32 {
299 299
    let start: u32 = compile(nfa, "a+");
300 300
301 301
    if nfaMatches(nfa, start, "") { return 1; }
302 302
    assert nfaMatches(nfa, start, "a");
303 303
    assert nfaMatches(nfa, start, "aaaaa");
304 304
305 305
    return 0;
306 306
}
307 307
308 -
fn testQuestion(nfa: *mut NfaState) -> i32 {
308 +
unsafe fn testQuestion(nfa: *unsafe mut NfaState) -> i32 {
309 309
    let start: u32 = compile(nfa, "a?");
310 310
311 311
    assert nfaMatches(nfa, start, "");
312 312
    assert nfaMatches(nfa, start, "a");
313 313
    if nfaMatches(nfa, start, "aa") { return 3; }
314 314
315 315
    return 0;
316 316
}
317 317
318 -
fn testDot(nfa: *mut NfaState) -> i32 {
318 +
unsafe fn testDot(nfa: *unsafe mut NfaState) -> i32 {
319 319
    let start: u32 = compile(nfa, "..");
320 320
321 321
    assert nfaMatches(nfa, start, "ab");
322 322
    assert nfaMatches(nfa, start, "zz");
323 323
    if nfaMatches(nfa, start, "a") { return 3; }
324 324
    if nfaMatches(nfa, start, "abc") { return 4; }
325 325
326 326
    return 0;
327 327
}
328 328
329 -
fn testComplex(nfa: *mut NfaState) -> i32 {
329 +
unsafe fn testComplex(nfa: *unsafe mut NfaState) -> i32 {
330 330
    let start: u32 = compile(nfa, "ab*c");
331 331
332 332
    assert nfaMatches(nfa, start, "ac");
333 333
    assert nfaMatches(nfa, start, "abc");
334 334
    assert nfaMatches(nfa, start, "abbc");
337 337
    if nfaMatches(nfa, start, "adc") { return 6; }
338 338
339 339
    return 0;
340 340
}
341 341
342 -
fn testDotStar(nfa: *mut NfaState) -> i32 {
342 +
unsafe fn testDotStar(nfa: *unsafe mut NfaState) -> i32 {
343 343
    let start: u32 = compile(nfa, ".*");
344 344
345 345
    assert nfaMatches(nfa, start, "");
346 346
    assert nfaMatches(nfa, start, "hello");
347 347
    assert nfaMatches(nfa, start, "x");
348 348
349 349
    return 0;
350 350
}
351 351
352 -
@default fn main() -> i32 {
352 +
@default unsafe fn main() -> i32 {
353 353
    let mut trans: [Trans; 256] = [Trans { kind: 0, ch: 0, to: 0 }; 256];
354 354
    let mut stateFirst: [u32; 128] = [0xFFFFFFFF; 128];
355 355
    let mut transNext: [u32; 256] = [0xFFFFFFFF; 256];
356 356
    let mut current: [u32; 4] = [0; 4];
357 357
    let mut nextSet: [u32; 4] = [0; 4];
test/tests/prog.sha256.rad +6 -6
50 50
fn ssig1(x: u32) -> u32 {
51 51
    return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
52 52
}
53 53
54 54
/// Prepare the message schedule from a 16-word (512-bit) block.
55 -
fn prepareSchedule(s: *mut Sha256, block: *[u32]) {
55 +
fn prepareSchedule(s: &mut Sha256, block: &[u32]) {
56 56
    // Copy the first 16 words directly.
57 57
    let mut i: u32 = 0;
58 58
    while i < 16 {
59 59
        set s.w[i] = block[i];
60 60
        set i += 1;
65 65
        set i += 1;
66 66
    }
67 67
}
68 68
69 69
/// Run the 64-round compression function.
70 -
fn compress(s: *mut Sha256, k: *[u32]) {
70 +
fn compress(s: &mut Sha256, k: &[u32]) {
71 71
    let mut a: u32 = s.h[0];
72 72
    let mut b: u32 = s.h[1];
73 73
    let mut c: u32 = s.h[2];
74 74
    let mut d: u32 = s.h[3];
75 75
    let mut e: u32 = s.h[4];
101 101
    set s.h[6] += g;
102 102
    set s.h[7] += hh;
103 103
}
104 104
105 105
/// Reset hash state to initial values.
106 -
fn resetHash(s: *mut Sha256) {
106 +
fn resetHash(s: &mut Sha256) {
107 107
    let mut i: u32 = 0;
108 108
    while i < 8 {
109 109
        set s.h[i] = INIT_H[i];
110 110
        set i += 1;
111 111
    }
112 112
}
113 113
114 114
/// Pad and hash a short message (up to 55 bytes, fits in one 512-bit block).
115 -
fn hashMessage(s: *mut Sha256, k: *[u32], msg: *[u8]) -> i32 {
115 +
fn hashMessage(s: &mut Sha256, k: &[u32], msg: &[u8]) -> i32 {
116 116
    // The message must fit in a single block (max 55 bytes for 1-block padding).
117 117
    if msg.len > 55 {
118 118
        return -1;
119 119
    }
120 120
157 157
    return 0;
158 158
}
159 159
160 160
/// Test SHA-256 of the empty string "".
161 161
/// Expected: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
162 -
fn testEmpty(s: *mut Sha256, k: *[u32]) -> i32 {
162 +
fn testEmpty(s: &mut Sha256, k: &[u32]) -> i32 {
163 163
    assert hashMessage(s, k, &[]) == 0;
164 164
165 165
    assert s.h[0] == 0xE3B0C442;
166 166
    assert s.h[1] == 0x98FC1C14;
167 167
    assert s.h[2] == 0x9AFBF4C8;
173 173
    return 0;
174 174
}
175 175
176 176
/// Test SHA-256 of "abc".
177 177
/// Expected: ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad
178 -
fn testAbc(s: *mut Sha256, k: *[u32]) -> i32 {
178 +
fn testAbc(s: &mut Sha256, k: &[u32]) -> i32 {
179 179
    let msg: [u8; 3] = [0x61, 0x62, 0x63];
180 180
    assert hashMessage(s, k, &msg[..]) == 0;
181 181
182 182
    assert s.h[0] == 0xBA7816BF;
183 183
    assert s.h[1] == 0x8F01CFEA;
test/tests/prog.sieve.rad +8 -8
2 2
//! Sieve of Eratosthenes.
3 3
//! Find all primes up to 256 using a boolean array.
4 4
//! Verify the count of primes matches the known value (54 primes <= 256).
5 5
6 6
/// Mark all multiples of p as composite.
7 -
fn markMultiples(sieve: *mut [bool], p: u32) {
7 +
fn markMultiples(sieve: &mut [bool], p: u32) {
8 8
    let mut i: u32 = p * p;
9 9
    while i < sieve.len {
10 10
        set sieve[i] = true;
11 11
        set i += p;
12 12
    }
13 13
}
14 14
15 15
/// Run the sieve algorithm.
16 -
fn runSieve(sieve: *mut [bool]) {
16 +
fn runSieve(sieve: &mut [bool]) {
17 17
    // 0 and 1 are not prime.
18 18
    set sieve[0] = true;
19 19
    set sieve[1] = true;
20 20
21 21
    let mut p: u32 = 2;
26 26
        set p += 1;
27 27
    }
28 28
}
29 29
30 30
/// Count the number of primes found.
31 -
fn countPrimes(sieve: *[bool]) -> u32 {
31 +
fn countPrimes(sieve: &[bool]) -> u32 {
32 32
    let mut count: u32 = 0;
33 33
    for composite in sieve {
34 34
        if not composite {
35 35
            set count += 1;
36 36
        }
37 37
    }
38 38
    return count;
39 39
}
40 40
41 41
/// Collect primes into a slice, return count.
42 -
fn collectPrimes(sieve: *[bool], primes: *mut [u32]) -> u32 {
42 +
fn collectPrimes(sieve: &[bool], primes: &mut [u32]) -> u32 {
43 43
    let mut count: u32 = 0;
44 44
    for composite, idx in sieve {
45 45
        if not composite {
46 46
            if count < primes.len {
47 47
                set primes[count] = idx;
51 51
    }
52 52
    return count;
53 53
}
54 54
55 55
/// Check that specific known primes are marked correctly.
56 -
fn verifyKnownPrimes(sieve: *[bool]) -> i32 {
56 +
fn verifyKnownPrimes(sieve: &[bool]) -> i32 {
57 57
    // Known small primes.
58 58
    let primes: [u32; 6] = [2, 3, 5, 7, 11, 13];
59 59
    for p in primes {
60 60
        if sieve[p] {
61 61
            return 1;
78 78
    assert sieve[250];
79 79
    return 0;
80 80
}
81 81
82 82
/// Verify that collected primes are in ascending order and all valid.
83 -
fn verifyCollected(sieve: *[bool]) -> i32 {
83 +
unsafe fn verifyCollected(sieve: &[bool]) -> i32 {
84 84
    let mut primesBuf: [u32; 64] = [0; 64];
85 85
    let count = collectPrimes(sieve, &mut primesBuf[..]);
86 86
87 87
    assert count == 54;
88 88
90 90
    assert primesBuf[0] == 2;
91 91
    // Last prime should be 251.
92 92
    assert primesBuf[count - 1] == 251;
93 93
94 94
    // Verify ascending order.
95 -
    let collected = &primesBuf[0..count];
95 +
    let collected: *unsafe [u32] = &primesBuf[0..count];
96 96
    let mut prev: ?u32 = nil;
97 97
    for p in collected {
98 98
        if let prevVal = prev {
99 99
            assert prevVal < p;
100 100
        }
101 101
        set prev = p;
102 102
    }
103 103
    return 0;
104 104
}
105 105
106 -
@default fn main() -> i32 {
106 +
@default unsafe fn main() -> i32 {
107 107
    let mut sieve: [bool; 256] = [false; 256];
108 108
    runSieve(&mut sieve[..]);
109 109
110 110
    let r1 = verifyKnownPrimes(&sieve[..]);
111 111
    if r1 <> 0 {
test/tests/prog.symtab.rad +18 -18
30 30
    symbolCount: u32,
31 31
}
32 32
33 33
/// The symbol table.
34 34
record SymTab: Copy {
35 -
    symbols: *mut [Symbol],
35 +
    symbols: *unsafe mut [Symbol],
36 36
    symbolCount: u32,
37 -
    scopes: *mut [ScopeMarker],
37 +
    scopes: *unsafe mut [ScopeMarker],
38 38
    scopeDepth: u32,
39 -
    buckets: *mut [u32],
39 +
    buckets: *unsafe mut [u32],
40 40
}
41 41
42 42
/// Simple string hash function.
43 -
fn hashName(name: *[u8]) -> u32 {
43 +
fn hashName(name: &[u8]) -> u32 {
44 44
    let mut h: u32 = 5381;
45 45
    for ch in name {
46 46
        set h = ((h << 5) + h) + ch as u32;
47 47
    }
48 48
    return h;
49 49
}
50 50
51 51
/// Initialize the symbol table.
52 -
fn init(tab: *mut SymTab) {
52 +
unsafe fn init(tab: &mut SymTab) {
53 53
    set tab.symbolCount = 0;
54 54
    set tab.scopeDepth = 0;
55 55
56 56
    for i in 0..HASH_SIZE {
57 57
        set tab.buckets[i] = NIL;
58 58
    }
59 59
}
60 60
61 61
/// Push a new scope.
62 -
fn pushScope(tab: *mut SymTab) {
62 +
unsafe fn pushScope(tab: &mut SymTab) {
63 63
    set tab.scopes[tab.scopeDepth] = ScopeMarker { symbolCount: tab.symbolCount };
64 64
    set tab.scopeDepth += 1;
65 65
}
66 66
67 67
/// Pop the current scope, removing all symbols defined in it.
68 -
fn popScope(tab: *mut SymTab) {
68 +
unsafe fn popScope(tab: &mut SymTab) {
69 69
    if tab.scopeDepth == 0 {
70 70
        return;
71 71
    }
72 72
    set tab.scopeDepth -= 1;
73 73
    let marker = tab.scopes[tab.scopeDepth];
91 91
        }
92 92
    }
93 93
}
94 94
95 95
/// Define a symbol in the current scope.
96 -
fn define(tab: *mut SymTab, name: *[u8], value: i32) -> u32 {
96 +
unsafe fn define(tab: &mut SymTab, name: &[u8], value: i32) -> u32 {
97 97
    let h = hashName(name);
98 98
    let bucket = h % HASH_SIZE;
99 99
100 100
    // Check for shadowed symbol with same name.
101 101
    let mut shadowIdx: u32 = NIL;
126 126
    set tab.symbolCount += 1;
127 127
    return idx;
128 128
}
129 129
130 130
/// Look up a symbol by name. Returns the value if found.
131 -
fn lookup(tab: *SymTab, name: *[u8]) -> ?i32 {
131 +
unsafe fn lookup(tab: &SymTab, name: &[u8]) -> ?i32 {
132 132
    let h = hashName(name);
133 133
    let bucket = h % HASH_SIZE;
134 134
    let mut cur = tab.buckets[bucket];
135 135
136 136
    while cur <> NIL {
141 141
    }
142 142
    return nil;
143 143
}
144 144
145 145
/// Update a symbol's value. Returns true if the symbol was found.
146 -
fn update(tab: *mut SymTab, name: *[u8], newValue: i32) -> bool {
146 +
unsafe fn update(tab: &mut SymTab, name: &[u8], newValue: i32) -> bool {
147 147
    let h = hashName(name);
148 148
    let bucket = h % HASH_SIZE;
149 149
    let mut cur = tab.buckets[bucket];
150 150
151 151
    while cur <> NIL {
157 157
    }
158 158
    return false;
159 159
}
160 160
161 161
/// Test basic define and lookup.
162 -
fn testBasic(tab: *mut SymTab) -> i32 {
162 +
unsafe fn testBasic(tab: &mut SymTab) -> i32 {
163 163
    init(tab);
164 164
    pushScope(tab);
165 165
166 166
    define(tab, "x", 10);
167 167
    define(tab, "y", 20);
190 190
    popScope(tab);
191 191
    return 0;
192 192
}
193 193
194 194
/// Test scope shadowing.
195 -
fn testShadowing(tab: *mut SymTab) -> i32 {
195 +
unsafe fn testShadowing(tab: &mut SymTab) -> i32 {
196 196
    init(tab);
197 197
    pushScope(tab);
198 198
    define(tab, "x", 1);
199 199
200 200
    // Verify outer x.
223 223
    popScope(tab);
224 224
    return 0;
225 225
}
226 226
227 227
/// Test deep nesting with shadowing.
228 -
fn testDeepNesting(tab: *mut SymTab) -> i32 {
228 +
unsafe fn testDeepNesting(tab: &mut SymTab) -> i32 {
229 229
    init(tab);
230 230
231 231
    // Define x at each of 8 scope levels.
232 232
    let mut i: u32 = 0;
233 233
    while i < 8 {
257 257
    popScope(tab);
258 258
    return 0;
259 259
}
260 260
261 261
/// Test multiple symbols per scope.
262 -
fn testMultipleSymbols(tab: *mut SymTab) -> i32 {
262 +
unsafe fn testMultipleSymbols(tab: &mut SymTab) -> i32 {
263 263
    init(tab);
264 264
    pushScope(tab);
265 265
266 266
    // Define a bunch of symbols.
267 267
    let names: [*[u8]; 8] = ["a", "bb", "ccc", "dddd", "eeeee", "ff", "ggg", "h"];
287 287
    popScope(tab);
288 288
    return 0;
289 289
}
290 290
291 291
/// Test update functionality.
292 -
fn testUpdate(tab: *mut SymTab) -> i32 {
292 +
unsafe fn testUpdate(tab: &mut SymTab) -> i32 {
293 293
    init(tab);
294 294
    pushScope(tab);
295 295
296 296
    define(tab, "counter", 0);
297 297
318 318
    popScope(tab);
319 319
    return 0;
320 320
}
321 321
322 322
/// Test scope isolation: symbols in popped scopes are gone.
323 -
fn testScopeIsolation(tab: *mut SymTab) -> i32 {
323 +
unsafe fn testScopeIsolation(tab: &mut SymTab) -> i32 {
324 324
    init(tab);
325 325
326 326
    pushScope(tab);
327 327
    define(tab, "outer", 1);
328 328
356 356
    popScope(tab);
357 357
    return 0;
358 358
}
359 359
360 360
/// Test interleaved defines and lookups across scopes using while-let.
361 -
fn testInterleaved(tab: *mut SymTab) -> i32 {
361 +
unsafe fn testInterleaved(tab: &mut SymTab) -> i32 {
362 362
    init(tab);
363 363
    pushScope(tab);
364 364
365 365
    define(tab, "a", 100);
366 366
    define(tab, "b", 200);
400 400
    popScope(tab);
401 401
    popScope(tab);
402 402
    return 0;
403 403
}
404 404
405 -
@default fn main() -> i32 {
405 +
@default unsafe fn main() -> i32 {
406 406
    let mut symbols: [Symbol; 256] = [Symbol { nameHash: 0, value: 0, depth: 0, next: NIL, shadow: NIL }; 256];
407 407
    let mut scopes: [ScopeMarker; 16] = [ScopeMarker { symbolCount: 0 }; 16];
408 408
    let mut buckets: [u32; 64] = [NIL; 64];
409 409
410 410
    let mut tab = SymTab {
test/tests/prog.tokenizer.rad +34 -34
33 33
    pos: u32,
34 34
}
35 35
36 36
/// A list of tokens with a fixed-size backing store.
37 37
record TokenList: Copy {
38 -
    tokens: *mut [Token],
38 +
    tokens: *unsafe mut [Token],
39 39
    count: u32,
40 40
}
41 41
42 42
/// AST node for parsed expressions.
43 43
union Expr: Copy {
53 53
    right: u32,
54 54
}
55 55
56 56
/// Pool of AST nodes.
57 57
record ExprPool: Copy {
58 -
    nodes: *mut [Expr],
58 +
    nodes: *unsafe mut [Expr],
59 59
    count: u32,
60 60
}
61 61
62 62
/// Parser state.
63 63
record Parser: Copy {
64 -
    tokens: *[Token],
64 +
    tokens: *unsafe [Token],
65 65
    tokenCount: u32,
66 66
    pos: u32,
67 -
    pool: *mut ExprPool,
67 +
    pool: *unsafe mut ExprPool,
68 68
}
69 69
70 70
/// Check if a byte is a digit.
71 71
fn isDigit(c: u8) -> bool {
72 72
    return c >= 48 and c <= 57;
76 76
fn isSpace(c: u8) -> bool {
77 77
    return c == 32 or c == 9 or c == 10 or c == 13;
78 78
}
79 79
80 80
/// Peek at the current character, returning nil at end.
81 -
fn peek(lex: *Lexer) -> ?u8 {
81 +
unsafe fn peek(lex: *unsafe Lexer) -> ?u8 {
82 82
    if lex.pos < lex.source.len {
83 83
        return lex.source[lex.pos];
84 84
    }
85 85
    return nil;
86 86
}
87 87
88 88
/// Advance the lexer by one character.
89 -
fn advance(lex: *mut Lexer) {
89 +
unsafe fn advance(lex: *unsafe mut Lexer) {
90 90
    if lex.pos < lex.source.len {
91 91
        set lex.pos += 1;
92 92
    }
93 93
}
94 94
95 95
/// Skip whitespace characters.
96 -
fn skipWhitespace(lex: *mut Lexer) {
96 +
unsafe fn skipWhitespace(lex: *unsafe mut Lexer) {
97 97
    while let ch = peek(lex); isSpace(ch) {
98 98
        advance(lex);
99 99
    }
100 100
}
101 101
102 102
/// Scan a number literal.
103 -
fn scanNumber(lex: *mut Lexer) -> i32 {
103 +
unsafe fn scanNumber(lex: *unsafe mut Lexer) -> i32 {
104 104
    let mut value: i32 = 0;
105 105
    while let ch = peek(lex); isDigit(ch) {
106 106
        set value = value * 10 + (ch - 48) as i32;
107 107
        advance(lex);
108 108
    }
109 109
    return value;
110 110
}
111 111
112 112
/// Get the next token from the lexer.
113 -
fn nextToken(lex: *mut Lexer) -> Token {
113 +
unsafe fn nextToken(lex: *unsafe mut Lexer) -> Token {
114 114
    skipWhitespace(lex);
115 115
116 116
    if let ch = peek(lex) {
117 117
        if isDigit(ch) {
118 118
            return Token::Number(scanNumber(lex));
131 131
    }
132 132
    return Token::Eof;
133 133
}
134 134
135 135
/// Tokenize the entire source into a token list.
136 -
fn tokenize(lex: *mut Lexer, list: *mut TokenList) -> bool {
136 +
unsafe fn tokenize(lex: *unsafe mut Lexer, list: *unsafe mut TokenList) -> bool {
137 137
    let mut done: bool = false;
138 138
    while not done {
139 139
        let tok = nextToken(lex);
140 140
        match tok {
141 141
            case Token::Invalid(_) => {
157 157
    }
158 158
    return true;
159 159
}
160 160
161 161
/// Allocate a new expression node.
162 -
fn newExpr(pool: *mut ExprPool, expr: Expr) -> u32 {
162 +
unsafe fn newExpr(pool: *unsafe mut ExprPool, expr: Expr) -> u32 {
163 163
    let idx = pool.count;
164 164
    set pool.nodes[idx] = expr;
165 165
    set pool.count += 1;
166 166
    return idx;
167 167
}
168 168
169 169
/// Get the current token in the parser.
170 -
fn currentToken(p: *Parser) -> Token {
170 +
unsafe fn currentToken(p: *unsafe Parser) -> Token {
171 171
    if p.pos < p.tokenCount {
172 172
        return p.tokens[p.pos];
173 173
    }
174 174
    return Token::Eof;
175 175
}
176 176
177 177
/// Advance the parser to the next token.
178 -
fn advanceParser(p: *mut Parser) {
178 +
unsafe fn advanceParser(p: *unsafe mut Parser) {
179 179
    if p.pos < p.tokenCount {
180 180
        set p.pos += 1;
181 181
    }
182 182
}
183 183
184 184
/// Parse a primary expression (number, parenthesized expression, or unary minus).
185 -
fn parsePrimary(p: *mut Parser) -> ?u32 {
185 +
unsafe fn parsePrimary(p: *unsafe mut Parser) -> ?u32 {
186 186
    let tok = currentToken(p);
187 187
188 188
    match tok {
189 189
        case Token::Number(n) => {
190 190
            advanceParser(p);
220 220
        }
221 221
    }
222 222
}
223 223
224 224
/// Parse multiplication and division (higher precedence).
225 -
fn parseMulDiv(p: *mut Parser, left: u32) -> u32 {
225 +
unsafe fn parseMulDiv(p: *unsafe mut Parser, left: u32) -> u32 {
226 226
    let mut result: u32 = left;
227 227
    let mut cont: bool = true;
228 228
229 229
    while cont {
230 230
        let tok = currentToken(p);
252 252
    }
253 253
    return result;
254 254
}
255 255
256 256
/// Parse addition and subtraction (lower precedence).
257 -
fn parseAddSub(p: *mut Parser, left: u32) -> u32 {
257 +
unsafe fn parseAddSub(p: *unsafe mut Parser, left: u32) -> u32 {
258 258
    let mut result: u32 = parseMulDiv(p, left);
259 259
    let mut cont: bool = true;
260 260
261 261
    while cont {
262 262
        let tok = currentToken(p);
286 286
    }
287 287
    return result;
288 288
}
289 289
290 290
/// Parse a full expression.
291 -
fn parseExpr(p: *mut Parser) -> ?u32 {
291 +
unsafe fn parseExpr(p: *unsafe mut Parser) -> ?u32 {
292 292
    let left = parsePrimary(p) else {
293 293
        return nil;
294 294
    };
295 295
    return parseAddSub(p, left);
296 296
}
297 297
298 298
/// Evaluate an expression tree.
299 -
fn eval(nodes: *[Expr], idx: u32) -> i32 {
299 +
unsafe fn eval(nodes: *unsafe [Expr], idx: u32) -> i32 {
300 300
    let node = nodes[idx];
301 301
    match node {
302 302
        case Expr::Num(n) => {
303 303
            return n;
304 304
        }
324 324
        }
325 325
    }
326 326
}
327 327
328 328
/// Count nodes in an expression tree.
329 -
fn countNodes(nodes: *[Expr], idx: u32) -> u32 {
329 +
unsafe fn countNodes(nodes: *unsafe [Expr], idx: u32) -> u32 {
330 330
    let node = nodes[idx];
331 331
    match node {
332 332
        case Expr::Num(_) => { return 1; }
333 333
        case Expr::BinOp(data) => {
334 334
            return 1 + countNodes(nodes, data.left) + countNodes(nodes, data.right);
338 338
        }
339 339
    }
340 340
}
341 341
342 342
/// Helper: tokenize, parse, and evaluate a string expression.
343 -
fn evaluate(
343 +
unsafe fn evaluate(
344 344
    source: *[u8],
345 -
    tokenBuf: *mut [Token],
346 -
    exprBuf: *mut [Expr]
345 +
    tokenBuf: *unsafe mut [Token],
346 +
    exprBuf: *unsafe mut [Expr]
347 347
) -> ?i32 {
348 348
    let mut lex = Lexer { source, pos: 0 };
349 349
    let mut list = TokenList { tokens: tokenBuf, count: 0 };
350 350
351 351
    if not tokenize(&mut lex, &mut list) {
365 365
    };
366 366
    return eval(exprBuf, root);
367 367
}
368 368
369 369
/// Test simple number.
370 -
fn testNumber(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
370 +
unsafe fn testNumber(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
371 371
    let result = evaluate("42", tokenBuf, exprBuf) else {
372 372
        return 1;
373 373
    };
374 374
    assert result == 42;
375 375
    return 0;
376 376
}
377 377
378 378
/// Test addition.
379 -
fn testAdd(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
379 +
unsafe fn testAdd(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
380 380
    let result = evaluate("3 + 4", tokenBuf, exprBuf) else {
381 381
        return 1;
382 382
    };
383 383
    assert result == 7;
384 384
    return 0;
385 385
}
386 386
387 387
/// Test precedence: multiplication before addition.
388 -
fn testPrecedence(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
388 +
unsafe fn testPrecedence(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
389 389
    let result = evaluate("2 + 3 * 4", tokenBuf, exprBuf) else {
390 390
        return 1;
391 391
    };
392 392
    assert result == 14;
393 393
    return 0;
394 394
}
395 395
396 396
/// Test parenthesized expression.
397 -
fn testParens(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
397 +
unsafe fn testParens(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
398 398
    let result = evaluate("(2 + 3) * 4", tokenBuf, exprBuf) else {
399 399
        return 1;
400 400
    };
401 401
    assert result == 20;
402 402
    return 0;
403 403
}
404 404
405 405
/// Test negation.
406 -
fn testNeg(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
406 +
unsafe fn testNeg(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
407 407
    let result = evaluate("-5 + 8", tokenBuf, exprBuf) else {
408 408
        return 1;
409 409
    };
410 410
    assert result == 3;
411 411
    return 0;
412 412
}
413 413
414 414
/// Test complex expression.
415 -
fn testComplex(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
415 +
unsafe fn testComplex(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
416 416
    // (10 - 3) * (2 + 1) = 7 * 3 = 21
417 417
    let result = evaluate("(10 - 3) * (2 + 1)", tokenBuf, exprBuf) else {
418 418
        return 1;
419 419
    };
420 420
    assert result == 21;
421 421
    return 0;
422 422
}
423 423
424 424
/// Test chained operations.
425 -
fn testChained(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
425 +
unsafe fn testChained(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
426 426
    // 100 - 20 - 30 - 10 = 40
427 427
    let result = evaluate("100 - 20 - 30 - 10", tokenBuf, exprBuf) else {
428 428
        return 1;
429 429
    };
430 430
    assert result == 40;
431 431
    return 0;
432 432
}
433 433
434 434
/// Test token counting via for-in.
435 -
fn testTokenCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
435 +
unsafe fn testTokenCount(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
436 436
    let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 };
437 437
    let mut list = TokenList { tokens: tokenBuf, count: 0 };
438 438
    assert tokenize(&mut lex, &mut list);
439 439
440 440
    // Should be: 1, +, 2, *, 3, Eof = 6 tokens
455 455
456 456
    return 0;
457 457
}
458 458
459 459
/// Test division and mixed operations.
460 -
fn testDivision(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
460 +
unsafe fn testDivision(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
461 461
    // 20 / 4 + 3 = 5 + 3 = 8
462 462
    let result = evaluate("20 / 4 + 3", tokenBuf, exprBuf) else {
463 463
        return 1;
464 464
    };
465 465
    assert result == 8;
466 466
    return 0;
467 467
}
468 468
469 469
/// Test deeply nested parentheses.
470 -
fn testDeepNesting(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
470 +
unsafe fn testDeepNesting(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
471 471
    // ((((5)))) = 5
472 472
    let result = evaluate("((((5))))", tokenBuf, exprBuf) else {
473 473
        return 1;
474 474
    };
475 475
    assert result == 5;
476 476
    return 0;
477 477
}
478 478
479 479
/// Test node count of complex expression.
480 -
fn testNodeCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 {
480 +
unsafe fn testNodeCount(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 {
481 481
    let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 };
482 482
    let mut list = TokenList { tokens: tokenBuf, count: 0 };
483 483
    assert tokenize(&mut lex, &mut list);
484 484
485 485
    let mut pool = ExprPool { nodes: exprBuf, count: 0 };
498 498
    let count = countNodes(exprBuf, root);
499 499
    assert count == 5;
500 500
    return 0;
501 501
}
502 502
503 -
@default fn main() -> i32 {
503 +
@default unsafe fn main() -> i32 {
504 504
    let mut tokenBuf: [Token; 128] = [Token::Eof; 128];
505 505
    let mut exprBuf: [Expr; 128] = [Expr::Num(0); 128];
506 506
507 507
    let r1 = testNumber(&mut tokenBuf[..], &mut exprBuf[..]);
508 508
    if r1 <> 0 {
test/tests/prog.vm.rad +27 -27
56 56
    localBase: u32,
57 57
}
58 58
59 59
/// The VM state.
60 60
record VM: Copy {
61 -
    code: *[Op],
61 +
    code: *unsafe [Op],
62 62
    codeLen: u32,
63 -
    stack: *mut [i32],
63 +
    stack: *unsafe mut [i32],
64 64
    sp: u32,
65 -
    locals: *mut [i32],
66 -
    frames: *mut [Frame],
65 +
    locals: *unsafe mut [i32],
66 +
    frames: *unsafe mut [Frame],
67 67
    frameCount: u32,
68 68
    pc: u32,
69 69
}
70 70
71 71
/// VM error types.
81 81
    /// Too many nested calls.
82 82
    CallOverflow,
83 83
}
84 84
85 85
/// Push a value onto the stack.
86 -
fn push(vm: *mut VM, value: i32) throws (VmError) {
86 +
unsafe fn push(vm: *unsafe mut VM, value: i32) throws (VmError) {
87 87
    if vm.sp >= MAX_STACK {
88 88
        throw VmError::StackOverflow;
89 89
    }
90 90
    set vm.stack[vm.sp] = value;
91 91
    set vm.sp += 1;
92 92
}
93 93
94 94
/// Pop a value from the stack.
95 -
fn pop(vm: *mut VM) -> i32 throws (VmError) {
95 +
unsafe fn pop(vm: *unsafe mut VM) -> i32 throws (VmError) {
96 96
    if vm.sp == 0 {
97 97
        throw VmError::StackUnderflow;
98 98
    }
99 99
    set vm.sp -= 1;
100 100
    return vm.stack[vm.sp];
101 101
}
102 102
103 103
/// Peek at the top of the stack without removing.
104 -
fn peek(vm: *VM) -> i32 throws (VmError) {
104 +
unsafe fn peek(vm: *unsafe VM) -> i32 throws (VmError) {
105 105
    if vm.sp == 0 {
106 106
        throw VmError::StackUnderflow;
107 107
    }
108 108
    return vm.stack[vm.sp - 1];
109 109
}
110 110
111 111
/// Execute the bytecode program.
112 -
fn execute(vm: *mut VM) -> i32 throws (VmError) {
112 +
unsafe fn execute(vm: *unsafe mut VM) -> i32 throws (VmError) {
113 113
    while vm.pc < vm.codeLen {
114 114
        let instr = vm.code[vm.pc];
115 115
        set vm.pc += 1;
116 116
117 117
        match instr {
234 234
    }
235 235
    throw VmError::InvalidPC;
236 236
}
237 237
238 238
/// Helper to initialize VM and run a program.
239 -
fn runProgram(
240 -
    code: *[Op],
239 +
unsafe fn runProgram(
240 +
    code: *unsafe [Op],
241 241
    codeLen: u32,
242 -
    stackBuf: *mut [i32],
243 -
    localsBuf: *mut [i32],
244 -
    framesBuf: *mut [Frame]
242 +
    stackBuf: *unsafe mut [i32],
243 +
    localsBuf: *unsafe mut [i32],
244 +
    framesBuf: *unsafe mut [Frame]
245 245
) -> i32 throws (VmError) {
246 246
    let mut vm = VM {
247 247
        code,
248 248
        codeLen,
249 249
        stack: stackBuf,
255 255
    };
256 256
    return try execute(&mut vm);
257 257
}
258 258
259 259
/// Test basic arithmetic: 3 + 4 * 2 = 11
260 -
fn testArith(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
260 +
unsafe fn testArith(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
261 261
    let mut code: [Op; 8] = [Op::Halt; 8];
262 262
    set code[0] = Op::Push(3);
263 263
    set code[1] = Op::Push(4);
264 264
    set code[2] = Op::Push(2);
265 265
    set code[3] = Op::Mul;
270 270
    assert result == 11;
271 271
    return 0;
272 272
}
273 273
274 274
/// Test local variables: x = 5, y = 7, push x + y.
275 -
fn testLocals(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
275 +
unsafe fn testLocals(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
276 276
    let mut code: [Op; 16] = [Op::Halt; 16];
277 277
    set code[0] = Op::Push(5);
278 278
    set code[1] = Op::Store(0);   // x = 5
279 279
    set code[2] = Op::Push(7);
280 280
    set code[3] = Op::Store(1);   // y = 7
287 287
    assert result == 12;
288 288
    return 0;
289 289
}
290 290
291 291
/// Test conditional jump: if 3 > 2 then push 42 else push 99.
292 -
fn testConditional(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
292 +
unsafe fn testConditional(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
293 293
    let mut code: [Op; 16] = [Op::Halt; 16];
294 294
    set code[0] = Op::Push(3);
295 295
    set code[1] = Op::Push(2);
296 296
    set code[2] = Op::Gt;            // 3 > 2 => 1
297 297
    set code[3] = Op::JumpIfZero(6); // if false, jump to 6
305 305
    return 0;
306 306
}
307 307
308 308
/// Test loop: sum 1..5 using jumps.
309 309
/// local[0] = counter (starts at 1), local[1] = sum (starts at 0).
310 -
fn testLoop(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
310 +
unsafe fn testLoop(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
311 311
    let mut code: [Op; 32] = [Op::Halt; 32];
312 312
    set code[0] = Op::Push(1);
313 313
    set code[1] = Op::Store(0);      // counter = 1
314 314
    set code[2] = Op::Push(0);
315 315
    set code[3] = Op::Store(1);      // sum = 0
335 335
    assert result == 15;
336 336
    return 0;
337 337
}
338 338
339 339
/// Test function call: call a function that computes n*2+1 for n=10.
340 -
fn testCall(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
340 +
unsafe fn testCall(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
341 341
    let mut code: [Op; 32] = [Op::Halt; 32];
342 342
343 343
    // Main: push argument on stack, call function, halt.
344 344
    set code[0] = Op::Push(10);      // push argument
345 345
    set code[1] = Op::Call(5);       // call function at 5
362 362
    assert result == 21;
363 363
    return 0;
364 364
}
365 365
366 366
/// Test division by zero detection using try...catch with error binding.
367 -
fn testDivByZero(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
367 +
unsafe fn testDivByZero(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
368 368
    let mut code: [Op; 8] = [Op::Halt; 8];
369 369
    set code[0] = Op::Push(42);
370 370
    set code[1] = Op::Push(0);
371 371
    set code[2] = Op::Div;
372 372
    set code[3] = Op::Halt;
382 382
    assert caught == 1;
383 383
    return 0;
384 384
}
385 385
386 386
/// Test negation and equality.
387 -
fn testNegAndEq(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
387 +
unsafe fn testNegAndEq(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
388 388
    let mut code: [Op; 16] = [Op::Halt; 16];
389 389
    set code[0] = Op::Push(5);
390 390
    set code[1] = Op::Neg;           // -5
391 391
    set code[2] = Op::Push(-5);
392 392
    set code[3] = Op::Eq;            // -5 == -5 => 1
396 396
    assert result == 1;
397 397
    return 0;
398 398
}
399 399
400 400
/// Test factorial using recursive calls: fact(6) = 720.
401 -
fn testFactorial(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
401 +
unsafe fn testFactorial(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
402 402
    let mut code: [Op; 32] = [Op::Halt; 32];
403 403
404 404
    // Main: push 6, call fact, halt.
405 405
    set code[0] = Op::Push(6);
406 406
    set code[1] = Op::Call(4);       // call fact at 4
432 432
    assert result == 720;
433 433
    return 0;
434 434
}
435 435
436 436
/// Test dup instruction.
437 -
fn testDup(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
437 +
unsafe fn testDup(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
438 438
    let mut code: [Op; 8] = [Op::Halt; 8];
439 439
    set code[0] = Op::Push(7);
440 440
    set code[1] = Op::Dup;
441 441
    set code[2] = Op::Add;           // 7 + 7 = 14
442 442
    set code[3] = Op::Halt;
445 445
    assert result == 14;
446 446
    return 0;
447 447
}
448 448
449 449
/// Test stack underflow detection using try...catch with error binding.
450 -
fn testStackUnderflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
450 +
unsafe fn testStackUnderflow(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
451 451
    let mut code: [Op; 4] = [Op::Halt; 4];
452 452
    set code[0] = Op::Add;  // nothing on stack
453 453
    set code[1] = Op::Halt;
454 454
455 455
    let mut caught: i32 = 0;
463 463
    assert caught == 1;
464 464
    return 0;
465 465
}
466 466
467 467
/// Test that try...catch on success path does not execute catch block.
468 -
fn testSuccessNoCatch(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
468 +
unsafe fn testSuccessNoCatch(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
469 469
    let mut code: [Op; 4] = [Op::Halt; 4];
470 470
    set code[0] = Op::Push(99);
471 471
    set code[1] = Op::Halt;
472 472
473 473
    let mut caught: i32 = 0;
481 481
    assert result == 99;
482 482
    return 0;
483 483
}
484 484
485 485
/// Test call overflow detection by exhausting frames.
486 -
fn testCallOverflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
486 +
unsafe fn testCallOverflow(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
487 487
    let mut code: [Op; 4] = [Op::Halt; 4];
488 488
    // Infinite recursion: function calls itself.
489 489
    set code[0] = Op::Call(0);
490 490
    set code[1] = Op::Halt;
491 491
500 500
    assert caught == 1;
501 501
    return 0;
502 502
}
503 503
504 504
/// Test that catch with no binding works (discard the error).
505 -
fn testCatchNoBinding(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
505 +
unsafe fn testCatchNoBinding(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 {
506 506
    let mut code: [Op; 4] = [Op::Halt; 4];
507 507
    set code[0] = Op::Pop;  // underflow
508 508
    set code[1] = Op::Halt;
509 509
510 510
    // Catch without binding - just swallow the error.
511 511
    try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch {};
512 512
    return 0;
513 513
}
514 514
515 -
@default fn main() -> i32 {
515 +
@default unsafe fn main() -> i32 {
516 516
    let mut stackBuf: [i32; 64] = [0; 64];
517 517
    let mut localsBuf: [i32; 128] = [0; 128];
518 518
    let mut framesBuf: [Frame; 8] = [Frame { returnAddr: 0, localBase: 0 }; 8];
519 519
520 520
    let r1 = testArith(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
test/tests/ptr.addressof.local.rad +3 -3
1 1
record Point: Copy { x: i32, y: i32 }
2 2
3 3
/// Address of a record local (aggregate) - value is already a pointer.
4 -
fn addressOfRecordLocal() -> *Point {
4 +
unsafe fn addressOfRecordLocal() -> *unsafe Point {
5 5
    let p = Point { x: 1, y: 2 };
6 6
    return &p;
7 7
}
8 8
9 9
/// Address of a scalar local - requires stack allocation.
10 -
fn addressOfScalarLocal() -> *i32 {
10 +
unsafe fn addressOfScalarLocal() -> *unsafe i32 {
11 11
    let x: i32 = 42;
12 12
    return &x;
13 13
}
14 14
15 15
/// Address of dereference - should return the original pointer.
16 -
fn addressOfDeref(ptr: *i32) -> *i32 {
16 +
unsafe fn addressOfDeref(ptr: &i32) -> *unsafe i32 {
17 17
    return &(*ptr);
18 18
}
test/tests/ptr.addressof.rad +1 -1
1 1
/// Takes the address of an array element.
2 -
fn addressOfElem(arr: [i32; 4], idx: u32) -> *i32 {
2 +
unsafe fn addressOfElem(arr: [i32; 4], idx: u32) -> *unsafe i32 {
3 3
    return &arr[idx];
4 4
}
test/tests/ptr.assign.rad +2 -2
1 1
//! returns: 42
2 -
@default fn main() -> i32 {
2 +
@default unsafe fn main() -> i32 {
3 3
    let mut x: i32 = 1;
4 -
    let mut ptr: *mut i32 = &mut x;
4 +
    let mut ptr: *unsafe mut i32 = &mut x;
5 5
6 6
    set *ptr = 42;
7 7
8 8
    return x;
9 9
}
test/tests/ptr.deref.rad +9 -9
1 1
//! returns: 65
2 2
//! Test pointer dereference in various contexts.
3 3
4 -
fn derefArrayIndex(ary: [i32; 3]) -> i32 {
5 -
    let x: *i32 = &ary[1];
4 +
unsafe fn derefArrayIndex(ary: [i32; 3]) -> i32 {
5 +
    let x: *unsafe i32 = &ary[1];
6 6
    return *x;
7 7
}
8 8
9 -
fn derefSliceIndex(slc: *[i32]) -> i32 {
10 -
    let x: *i32 = &slc[2];
9 +
unsafe fn derefSliceIndex(slc: &[i32]) -> i32 {
10 +
    let x: *unsafe i32 = &slc[2];
11 11
    return *x;
12 12
}
13 13
14 -
fn derefBinop(x: i32, y: i32) -> i32 {
15 -
    let px: *i32 = &x;
16 -
    let py: *i32 = &y;
14 +
unsafe fn derefBinop(x: i32, y: i32) -> i32 {
15 +
    let px: *unsafe i32 = &x;
16 +
    let py: *unsafe i32 = &y;
17 17
18 18
    return *px + *py;
19 19
}
20 20
21 -
@default fn main() -> i32 {
21 +
@default unsafe fn main() -> i32 {
22 22
    let x: i32  = 42;
23 -
    let y: *i32 = &x;
23 +
    let y: *unsafe i32 = &x;
24 24
    let z: i32  = *y; // 42
25 25
    let r: i32 = derefBinop(3, 6); // 9
26 26
    let a: i32 = derefArrayIndex([7, 8, 9]); // 8
27 27
    let s: i32 = derefSliceIndex(&[2, 4, 6]); // 6
28 28
test/tests/ptr.eq.rad +10 -10
4 4
    x: i32,
5 5
    y: i32,
6 6
}
7 7
8 8
// Test that pointer equality uses address comparison, not value comparison.
9 -
fn testPtrSameAddress() -> bool {
9 +
unsafe fn testPtrSameAddress() -> bool {
10 10
    let p1 = Point { x: 1, y: 2 };
11 -
    let a = &p1;
12 -
    let b = &p1;
11 +
    let a: *unsafe Point = &p1;
12 +
    let b: *unsafe Point = &p1;
13 13
14 14
    return a == b;  // Same address, should be equal.
15 15
}
16 16
17 -
fn testPtrDifferentAddressSameValues() -> bool {
17 +
unsafe fn testPtrDifferentAddressSameValues() -> bool {
18 18
    let p1 = Point { x: 1, y: 2 };
19 19
    let p2 = Point { x: 1, y: 2 };  // Same values as p1, but different address.
20 -
    let a = &p1;
21 -
    let b = &p2;
20 +
    let a: *unsafe Point = &p1;
21 +
    let b: *unsafe Point = &p2;
22 22
23 23
    return not (a == b);  // Different addresses, should NOT be equal.
24 24
}
25 25
26 -
fn testPtrDifferentAddressDifferentValues() -> bool {
26 +
unsafe fn testPtrDifferentAddressDifferentValues() -> bool {
27 27
    let p1 = Point { x: 1, y: 2 };
28 28
    let p2 = Point { x: 3, y: 4 };
29 -
    let a = &p1;
30 -
    let b = &p2;
29 +
    let a: *unsafe Point = &p1;
30 +
    let b: *unsafe Point = &p2;
31 31
32 32
    return not (a == b);  // Different addresses, should NOT be equal.
33 33
}
34 34
35 -
@default fn main() -> i32 {
35 +
@default unsafe fn main() -> i32 {
36 36
    assert testPtrSameAddress();
37 37
    assert testPtrDifferentAddressSameValues();
38 38
    assert testPtrDifferentAddressDifferentValues();
39 39
    return 0;
40 40
}
test/tests/ptr.mutate.rad +2 -2
1 1
//! returns: 42
2 2
3 -
fn mutate1(ptr: *mut i32) {
3 +
fn mutate1(ptr: &mut i32) {
4 4
    set *ptr = 39;
5 5
}
6 6
7 -
fn mutate2(ptr: *mut i32) {
7 +
fn mutate2(ptr: &mut i32) {
8 8
    set *ptr += 2;
9 9
    set *ptr += 1;
10 10
}
11 11
12 12
@default fn main() -> i32 {
test/tests/ptr.opaque.rad +18 -18
1 1
//! returns: 0
2 2
//! Test basic opaque pointer usage (automatic coercion).
3 -
fn testOpaqueCasting() -> bool {
3 +
unsafe fn testOpaqueCasting() -> bool {
4 4
    let x: u32 = 42;
5 -
    let ptr: *u8 = &x as *u8;
6 -
    let opq: *opaque = ptr;  // Automatic coercion from *u8 to *opaque.
7 -
    let back: *u8 = opq as *u8;
5 +
    let ptr: *unsafe u8 = &x as *unsafe u8;
6 +
    let opq: *unsafe opaque = ptr;  // Automatic coercion from *u8 to *opaque.
7 +
    let back: *unsafe u8 = opq as *unsafe u8;
8 8
9 9
    return back == ptr;
10 10
}
11 11
12 12
/// Test that opaque can be used in function parameters.
13 -
fn takesOpaque(ptr: *opaque, orig: *u8) -> bool {
14 -
    let back: *u8 = ptr as *u8;
13 +
unsafe fn takesOpaque(ptr: *unsafe opaque, orig: *unsafe u8) -> bool {
14 +
    let back: *unsafe u8 = ptr as *unsafe u8;
15 15
    return back == orig;
16 16
}
17 17
18 -
fn testOpaqueParams() -> bool {
18 +
unsafe fn testOpaqueParams() -> bool {
19 19
    let x: u32 = 42;
20 -
    let ptr: *u8 = &x as *u8;
20 +
    let ptr: *unsafe u8 = &x as *unsafe u8;
21 21
    return takesOpaque(ptr, ptr);  // Automatic coercion in function call.
22 22
}
23 23
24 24
/// Test that opaque can be used in return types.
25 -
fn returnsOpaque(ptr: *u8) -> *opaque {
25 +
fn returnsOpaque(ptr: *unsafe u8) -> *unsafe opaque {
26 26
    return ptr;  // Automatic coercion in return.
27 27
}
28 28
29 -
fn testOpaqueReturn() -> bool {
29 +
unsafe fn testOpaqueReturn() -> bool {
30 30
    let x: u32 = 42;
31 -
    let ptr: *u8 = &x as *u8;
32 -
    let opq: *opaque = returnsOpaque(ptr);
33 -
    return opq as *u8 == ptr;
31 +
    let ptr: *unsafe u8 = &x as *unsafe u8;
32 +
    let opq: *unsafe opaque = returnsOpaque(ptr);
33 +
    return opq as *unsafe u8 == ptr;
34 34
}
35 35
36 36
/// Test nullable opaque pointers.
37 -
fn testNullableOpaque() -> bool {
38 -
    let mut opt: ?*opaque = nil;
37 +
unsafe fn testNullableOpaque() -> bool {
38 +
    let mut opt: ?*unsafe opaque = nil;
39 39
    if opt == nil {
40 40
        let x: u32 = 42;
41 -
        let ptr: *u8 = &x as *u8;
41 +
        let ptr: *unsafe u8 = &x as *unsafe u8;
42 42
        set opt = ptr;  // Automatic coercion in assignment.
43 43
44 44
        if let p = opt {
45 -
            return p as *u8 == ptr;
45 +
            return p as *unsafe u8 == ptr;
46 46
        }
47 47
    }
48 48
    return false;
49 49
}
50 50
51 -
@default fn main() -> u32 {
51 +
@default unsafe fn main() -> u32 {
52 52
    assert testOpaqueCasting();
53 53
    assert testOpaqueParams();
54 54
    assert testOpaqueReturn();
55 55
    assert testNullableOpaque();
56 56
    return 0;
test/tests/range.arithmetic.rad +4 -4
1 1
//! returns: 0
2 2
3 -
@default fn main() -> i32 {
3 +
@default unsafe fn main() -> i32 {
4 4
    let arr: [i32; 6] = [10, 20, 30, 40, 50, 60];
5 5
    let len: u32 = 6;
6 6
7 7
    // Arithmetic in range start (no parens needed).
8 -
    let s = &arr[len - 3..len];
8 +
    let s: *unsafe [i32] = &arr[len - 3..len];
9 9
    if s.len <> 3 { return 1; }
10 10
    if s[0] <> 40 { return 2; }
11 11
    if s[2] <> 60 { return 3; }
12 12
13 13
    // Arithmetic in range end.
14 -
    let s2 = &arr[0..len - 2];
14 +
    let s2: *unsafe [i32] = &arr[0..len - 2];
15 15
    if s2.len <> 4 { return 4; }
16 16
    if s2[3] <> 40 { return 5; }
17 17
18 18
    // Arithmetic in both.
19 -
    let s3 = &arr[len - 4..len - 1];
19 +
    let s3: *unsafe [i32] = &arr[len - 4..len - 1];
20 20
    if s3.len <> 3 { return 6; }
21 21
    if s3[0] <> 30 { return 7; }
22 22
    if s3[2] <> 50 { return 8; }
23 23
24 24
    // Arithmetic in subscript.
test/tests/record.ptr.access.rad +2 -2
3 3
record Point: Copy {
4 4
    x: i32,
5 5
    y: i32,
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let mut p: Point = Point { x: 11, y: 31 };
10 -
    let mut r: *Point = &p;
10 +
    let mut r: *unsafe Point = &p;
11 11
12 12
    let mut x: i32 = r.x;
13 13
    let mut y: i32 = r.y;
14 14
15 15
    return (x + y) - 42;
test/tests/record.ptr.mutate.rad +1 -1
3 3
record Point: Copy {
4 4
    x: i32,
5 5
    y: i32,
6 6
}
7 7
8 -
fn mutate(p: *mut Point) {
8 +
fn mutate(p: &mut Point) {
9 9
    set p.x = 4;
10 10
    set p.y = 38;
11 11
}
12 12
13 13
@default fn main() -> i32 {
test/tests/ref.if.bug.rad +1 -1
2 2
//! Regression test: address-of inside an if branch.
3 3
//!
4 4
//! When `&mut var` appears in only one branch of an if/else, the merge
5 5
//! block's phi merges the original integer value with a stack pointer.
6 6
7 -
fn store(ptr: *mut u32, val: u32) {
7 +
fn store(ptr: &mut u32, val: u32) {
8 8
    set *ptr = val;
9 9
}
10 10
11 11
fn testIfBranch(cond: bool) -> u32 {
12 12
    let mut val: u32 = 42;
test/tests/ref.immut.loop.bug.rad +1 -1
4 4
//! Even though the variable is not `mut`, taking its address inside a
5 5
//! loop can produce the same SSA/pointer phi conflict as with mutable
6 6
//! variables: the loop header merges the original integer with a pointer
7 7
//! from the stack slot created by `&x`.
8 8
9 -
fn read(ptr: *u32) -> u32 {
9 +
fn read(ptr: &u32) -> u32 {
10 10
    return *ptr;
11 11
}
12 12
13 13
fn testZeroIter(n: u32) -> u32 {
14 14
    let val: u32 = 42;
test/tests/ref.mut.ptr.rad +3 -3
1 1
//! returns: 84
2 2
//! Test mutable pointer references.
3 3
4 -
fn store(ptr: *mut i32) {
4 +
unsafe fn store(ptr: *unsafe mut i32) {
5 5
    set *ptr = 42;
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let mut a: i32 = 0;
10 -
    let p: *mut i32 = &mut a;
10 +
    let p: *unsafe mut i32 = &mut a;
11 11
    store(p);
12 12
13 13
    let mut b: i32 = 0;
14 14
    store(&mut b);
15 15
test/tests/slice.alloc.loop.rad +3 -3
1 1
//! returns: 0
2 2
//! Test returning a slice from a function and iterating over it.
3 3
//! Exercises the same code path as createBlock's vars initialization.
4 4
//! The slice is returned through a return buffer (> 8 bytes).
5 5
6 -
fn makeSlice(buf: *mut [u8], count: u32) -> *mut [u8] {
6 +
unsafe fn makeSlice(buf: *unsafe mut [u8], count: u32) -> *unsafe mut [u8] {
7 7
    if count == 0 {
8 8
        return &mut [];
9 9
    }
10 10
    return @sliceOf(&mut buf[0], count);
11 11
}
12 12
13 -
fn initSlice(buf: *mut [u8], count: u32) -> i32 {
13 +
unsafe fn initSlice(buf: *unsafe mut [u8], count: u32) -> i32 {
14 14
    let s = makeSlice(buf, count);
15 15
16 16
    // This loop pattern matches createBlock's vars initialization.
17 17
    for i in 0..s.len {
18 18
        set s[i] = 0;
19 19
    }
20 20
21 21
    return s.len as i32;
22 22
}
23 23
24 -
@default fn main() -> i32 {
24 +
@default unsafe fn main() -> i32 {
25 25
    let mut buf: [u8; 64] = undefined;
26 26
27 27
    let n = initSlice(&mut buf[..], 5);
28 28
    assert n == 5;
29 29
test/tests/slice.append.rad +3 -2
9 9
10 10
fn newArena(data: *mut [u8]) -> Arena {
11 11
    return Arena { data, offset: 0 };
12 12
}
13 13
14 -
fn arenaAlloc(arena: *mut Arena, size: u32, al: u32) -> *mut opaque {
14 +
fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque {
15 15
    let aligned = (arena.offset + al - 1) / al * al;
16 16
    let newOffset = aligned + size;
17 17
18 18
    assert newOffset <= arena.data.len as u32;
19 19
42 42
}
43 43
44 44
static BUF: [u8; 4096] = undefined;
45 45
46 46
@default fn main() -> i32 {
47 -
    let mut arena = newArena(&mut BUF[..]);
47 +
    static arena: Arena = undefined;
48 +
    set arena = newArena(&mut BUF[..]);
48 49
    let a = arenaAllocator(&mut arena);
49 50
50 51
    // Allocate initial capacity of 4.
51 52
    let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32));
52 53
    let mut nums = @sliceOf(ptr as *mut i32, 0, 4);
test/tests/slice.append.ril +494 -485
1 1
data mut $BUF align 1 {
2 2
    undef * 4096;
3 3
}
4 4
5 +
data mut $main$nominal$arena align 8 {
6 +
    undef * 24;
7 +
}
8 +
5 9
fn w64 $newArena(w64 %0, w64 %1) {
6 10
  @entry0
7 11
    reserve %2 24 8;
8 12
    blit %2 %1 16;
9 13
    store w32 0 %2 16;
59 63
    store w64 %0 %1 0;
60 64
    store w32 4096 %1 8;
61 65
    store w32 4096 %1 12;
62 66
    reserve %2 24 8;
63 67
    call w64 %3 $newArena(%2, %1);
64 -
    reserve %4 16 8;
65 -
    call w64 %5 $arenaAllocator(%4, %3);
66 -
    mul w32 %6 4 4;
67 -
    call w64 %7 $arenaAlloc(%3, %6, 4);
68 -
    reserve %8 16 8;
69 -
    store w64 %7 %8 0;
70 -
    store w32 0 %8 8;
71 -
    store w32 4 %8 12;
72 -
    load w32 %9 %8 8;
73 -
    load w32 %10 %8 12;
74 -
    br.ult w32 %9 %10 @append.store1 @append.grow2;
68 +
    copy %4 $main$nominal$arena;
69 +
    blit %4 %3 24;
70 +
    copy %5 $main$nominal$arena;
71 +
    reserve %6 16 8;
72 +
    call w64 %7 $arenaAllocator(%6, %5);
73 +
    copy %8 $main$nominal$arena;
74 +
    mul w32 %9 4 4;
75 +
    call w64 %10 $arenaAlloc(%8, %9, 4);
76 +
    reserve %11 16 8;
77 +
    store w64 %10 %11 0;
78 +
    store w32 0 %11 8;
79 +
    store w32 4 %11 12;
80 +
    load w32 %12 %11 8;
81 +
    load w32 %13 %11 12;
82 +
    br.ult w32 %12 %13 @append.store1 @append.grow2;
75 83
  @append.store1
76 -
    load w64 %24 %8 0;
77 -
    mul w64 %25 %9 4;
78 -
    add w64 %26 %24 %25;
79 -
    store w32 10 %26 0;
80 -
    add w32 %27 %9 1;
81 -
    store w32 %27 %8 8;
82 -
    load w32 %32 %8 8;
83 -
    load w32 %33 %8 12;
84 -
    br.ult w32 %32 %33 @append.store6 @append.grow7;
84 +
    load w64 %27 %11 0;
85 +
    mul w64 %28 %12 4;
86 +
    add w64 %29 %27 %28;
87 +
    store w32 10 %29 0;
88 +
    add w32 %30 %12 1;
89 +
    store w32 %30 %11 8;
90 +
    load w32 %35 %11 8;
91 +
    load w32 %36 %11 12;
92 +
    br.ult w32 %35 %36 @append.store6 @append.grow7;
85 93
  @append.grow2
86 -
    shl w32 %11 %10 1;
87 -
    or w32 %12 %11 1;
88 -
    load w64 %13 %5 0;
89 -
    load w64 %14 %5 8;
90 -
    mul w32 %15 %12 4;
91 -
    call w64 %16 %13(%14, %15, 4);
92 -
    load w64 %17 %8 0;
93 -
    mul w32 %18 %9 4;
94 +
    shl w32 %14 %13 1;
95 +
    or w32 %15 %14 1;
96 +
    load w64 %16 %7 0;
97 +
    load w64 %17 %7 8;
98 +
    mul w32 %18 %15 4;
99 +
    call w64 %19 %16(%17, %18, 4);
100 +
    load w64 %20 %11 0;
101 +
    mul w32 %21 %12 4;
94 102
    jmp @append3(0);
95 -
  @append3(w32 %19)
96 -
    br.ult w32 %19 %18 @append4 @append5;
103 +
  @append3(w32 %22)
104 +
    br.ult w32 %22 %21 @append4 @append5;
97 105
  @append4
98 -
    add w64 %20 %17 %19;
99 -
    load w8 %21 %20 0;
100 -
    add w64 %22 %16 %19;
101 -
    store w8 %21 %22 0;
102 -
    add w32 %23 %19 1;
103 -
    jmp @append3(%23);
106 +
    add w64 %23 %20 %22;
107 +
    load w8 %24 %23 0;
108 +
    add w64 %25 %19 %22;
109 +
    store w8 %24 %25 0;
110 +
    add w32 %26 %22 1;
111 +
    jmp @append3(%26);
104 112
  @append5
105 -
    store w64 %16 %8 0;
106 -
    store w32 %12 %8 12;
113 +
    store w64 %19 %11 0;
114 +
    store w32 %15 %11 12;
107 115
    jmp @append.store1;
108 116
  @append.store6
109 -
    load w64 %47 %8 0;
110 -
    mul w64 %48 %32 4;
111 -
    add w64 %49 %47 %48;
112 -
    store w32 20 %49 0;
113 -
    add w32 %50 %32 1;
114 -
    store w32 %50 %8 8;
115 -
    load w32 %55 %8 8;
116 -
    load w32 %56 %8 12;
117 -
    br.ult w32 %55 %56 @append.store11 @append.grow12;
117 +
    load w64 %50 %11 0;
118 +
    mul w64 %51 %35 4;
119 +
    add w64 %52 %50 %51;
120 +
    store w32 20 %52 0;
121 +
    add w32 %53 %35 1;
122 +
    store w32 %53 %11 8;
123 +
    load w32 %58 %11 8;
124 +
    load w32 %59 %11 12;
125 +
    br.ult w32 %58 %59 @append.store11 @append.grow12;
118 126
  @append.grow7
119 -
    shl w32 %34 %33 1;
120 -
    or w32 %35 %34 1;
121 -
    load w64 %36 %5 0;
122 -
    load w64 %37 %5 8;
123 -
    mul w32 %38 %35 4;
124 -
    call w64 %39 %36(%37, %38, 4);
125 -
    load w64 %40 %8 0;
126 -
    mul w32 %41 %32 4;
127 +
    shl w32 %37 %36 1;
128 +
    or w32 %38 %37 1;
129 +
    load w64 %39 %7 0;
130 +
    load w64 %40 %7 8;
131 +
    mul w32 %41 %38 4;
132 +
    call w64 %42 %39(%40, %41, 4);
133 +
    load w64 %43 %11 0;
134 +
    mul w32 %44 %35 4;
127 135
    jmp @append8(0);
128 -
  @append8(w32 %42)
129 -
    br.ult w32 %42 %41 @append9 @append10;
136 +
  @append8(w32 %45)
137 +
    br.ult w32 %45 %44 @append9 @append10;
130 138
  @append9
131 -
    add w64 %43 %40 %42;
132 -
    load w8 %44 %43 0;
133 -
    add w64 %45 %39 %42;
134 -
    store w8 %44 %45 0;
135 -
    add w32 %46 %42 1;
136 -
    jmp @append8(%46);
139 +
    add w64 %46 %43 %45;
140 +
    load w8 %47 %46 0;
141 +
    add w64 %48 %42 %45;
142 +
    store w8 %47 %48 0;
143 +
    add w32 %49 %45 1;
144 +
    jmp @append8(%49);
137 145
  @append10
138 -
    store w64 %39 %8 0;
139 -
    store w32 %35 %8 12;
146 +
    store w64 %42 %11 0;
147 +
    store w32 %38 %11 12;
140 148
    jmp @append.store6;
141 149
  @append.store11
142 -
    load w64 %70 %8 0;
143 -
    mul w64 %71 %55 4;
144 -
    add w64 %72 %70 %71;
145 -
    store w32 30 %72 0;
146 -
    add w32 %73 %55 1;
147 -
    store w32 %73 %8 8;
148 -
    load w32 %76 %8 8;
149 -
    br.ne w32 %76 3 @then16 @merge17;
150 +
    load w64 %73 %11 0;
151 +
    mul w64 %74 %58 4;
152 +
    add w64 %75 %73 %74;
153 +
    store w32 30 %75 0;
154 +
    add w32 %76 %58 1;
155 +
    store w32 %76 %11 8;
156 +
    load w32 %79 %11 8;
157 +
    br.ne w32 %79 3 @then16 @merge17;
150 158
  @append.grow12
151 -
    shl w32 %57 %56 1;
152 -
    or w32 %58 %57 1;
153 -
    load w64 %59 %5 0;
154 -
    load w64 %60 %5 8;
155 -
    mul w32 %61 %58 4;
156 -
    call w64 %62 %59(%60, %61, 4);
157 -
    load w64 %63 %8 0;
158 -
    mul w32 %64 %55 4;
159 +
    shl w32 %60 %59 1;
160 +
    or w32 %61 %60 1;
161 +
    load w64 %62 %7 0;
162 +
    load w64 %63 %7 8;
163 +
    mul w32 %64 %61 4;
164 +
    call w64 %65 %62(%63, %64, 4);
165 +
    load w64 %66 %11 0;
166 +
    mul w32 %67 %58 4;
159 167
    jmp @append13(0);
160 -
  @append13(w32 %65)
161 -
    br.ult w32 %65 %64 @append14 @append15;
168 +
  @append13(w32 %68)
169 +
    br.ult w32 %68 %67 @append14 @append15;
162 170
  @append14
163 -
    add w64 %66 %63 %65;
164 -
    load w8 %67 %66 0;
165 -
    add w64 %68 %62 %65;
166 -
    store w8 %67 %68 0;
167 -
    add w32 %69 %65 1;
168 -
    jmp @append13(%69);
171 +
    add w64 %69 %66 %68;
172 +
    load w8 %70 %69 0;
173 +
    add w64 %71 %65 %68;
174 +
    store w8 %70 %71 0;
175 +
    add w32 %72 %68 1;
176 +
    jmp @append13(%72);
169 177
  @append15
170 -
    store w64 %62 %8 0;
171 -
    store w32 %58 %8 12;
178 +
    store w64 %65 %11 0;
179 +
    store w32 %61 %11 12;
172 180
    jmp @append.store11;
173 181
  @then16
174 182
    ret 1;
175 183
  @merge17
176 -
    load w32 %77 %8 12;
177 -
    br.ne w32 %77 4 @then18 @merge19;
184 +
    load w32 %80 %11 12;
185 +
    br.ne w32 %80 4 @then18 @merge19;
178 186
  @then18
179 187
    ret 2;
180 188
  @merge19
181 -
    load w32 %78 %8 8;
182 -
    br.ult w32 0 %78 @guard#pass22 @guard#trap23;
189 +
    load w32 %81 %11 8;
190 +
    br.ult w32 0 %81 @guard#pass22 @guard#trap23;
183 191
  @then20
184 192
    ret 3;
185 193
  @merge21
186 -
    load w32 %81 %8 8;
187 -
    br.ult w32 1 %81 @guard#pass26 @guard#trap27;
194 +
    load w32 %84 %11 8;
195 +
    br.ult w32 1 %84 @guard#pass26 @guard#trap27;
188 196
  @guard#pass22
189 -
    load w64 %79 %8 0;
190 -
    sload w32 %80 %79 0;
191 -
    br.ne w32 %80 10 @then20 @merge21;
197 +
    load w64 %82 %11 0;
198 +
    sload w32 %83 %82 0;
199 +
    br.ne w32 %83 10 @then20 @merge21;
192 200
  @guard#trap23
193 201
    ebreak;
194 202
    unreachable;
195 203
  @then24
196 204
    ret 4;
197 205
  @merge25
198 -
    load w32 %86 %8 8;
199 -
    br.ult w32 2 %86 @guard#pass30 @guard#trap31;
206 +
    load w32 %89 %11 8;
207 +
    br.ult w32 2 %89 @guard#pass30 @guard#trap31;
200 208
  @guard#pass26
201 -
    load w64 %82 %8 0;
202 -
    mul w64 %83 1 4;
203 -
    add w64 %84 %82 %83;
204 -
    sload w32 %85 %84 0;
205 -
    br.ne w32 %85 20 @then24 @merge25;
209 +
    load w64 %85 %11 0;
210 +
    mul w64 %86 1 4;
211 +
    add w64 %87 %85 %86;
212 +
    sload w32 %88 %87 0;
213 +
    br.ne w32 %88 20 @then24 @merge25;
206 214
  @guard#trap27
207 215
    ebreak;
208 216
    unreachable;
209 217
  @then28
210 218
    ret 5;
211 219
  @merge29
212 -
    load w32 %93 %8 8;
213 -
    load w32 %94 %8 12;
214 -
    br.ult w32 %93 %94 @append.store32 @append.grow33;
220 +
    load w32 %96 %11 8;
221 +
    load w32 %97 %11 12;
222 +
    br.ult w32 %96 %97 @append.store32 @append.grow33;
215 223
  @guard#pass30
216 -
    load w64 %87 %8 0;
217 -
    mul w64 %88 2 4;
218 -
    add w64 %89 %87 %88;
219 -
    sload w32 %90 %89 0;
220 -
    br.ne w32 %90 30 @then28 @merge29;
224 +
    load w64 %90 %11 0;
225 +
    mul w64 %91 2 4;
226 +
    add w64 %92 %90 %91;
227 +
    sload w32 %93 %92 0;
228 +
    br.ne w32 %93 30 @then28 @merge29;
221 229
  @guard#trap31
222 230
    ebreak;
223 231
    unreachable;
224 232
  @append.store32
225 -
    load w64 %108 %8 0;
226 -
    mul w64 %109 %93 4;
227 -
    add w64 %110 %108 %109;
228 -
    store w32 40 %110 0;
229 -
    add w32 %111 %93 1;
230 -
    store w32 %111 %8 8;
231 -
    load w32 %114 %8 8;
232 -
    br.ne w32 %114 4 @then37 @merge38;
233 +
    load w64 %111 %11 0;
234 +
    mul w64 %112 %96 4;
235 +
    add w64 %113 %111 %112;
236 +
    store w32 40 %113 0;
237 +
    add w32 %114 %96 1;
238 +
    store w32 %114 %11 8;
239 +
    load w32 %117 %11 8;
240 +
    br.ne w32 %117 4 @then37 @merge38;
233 241
  @append.grow33
234 -
    shl w32 %95 %94 1;
235 -
    or w32 %96 %95 1;
236 -
    load w64 %97 %5 0;
237 -
    load w64 %98 %5 8;
238 -
    mul w32 %99 %96 4;
239 -
    call w64 %100 %97(%98, %99, 4);
240 -
    load w64 %101 %8 0;
241 -
    mul w32 %102 %93 4;
242 +
    shl w32 %98 %97 1;
243 +
    or w32 %99 %98 1;
244 +
    load w64 %100 %7 0;
245 +
    load w64 %101 %7 8;
246 +
    mul w32 %102 %99 4;
247 +
    call w64 %103 %100(%101, %102, 4);
248 +
    load w64 %104 %11 0;
249 +
    mul w32 %105 %96 4;
242 250
    jmp @append34(0);
243 -
  @append34(w32 %103)
244 -
    br.ult w32 %103 %102 @append35 @append36;
251 +
  @append34(w32 %106)
252 +
    br.ult w32 %106 %105 @append35 @append36;
245 253
  @append35
246 -
    add w64 %104 %101 %103;
247 -
    load w8 %105 %104 0;
248 -
    add w64 %106 %100 %103;
249 -
    store w8 %105 %106 0;
250 -
    add w32 %107 %103 1;
251 -
    jmp @append34(%107);
254 +
    add w64 %107 %104 %106;
255 +
    load w8 %108 %107 0;
256 +
    add w64 %109 %103 %106;
257 +
    store w8 %108 %109 0;
258 +
    add w32 %110 %106 1;
259 +
    jmp @append34(%110);
252 260
  @append36
253 -
    store w64 %100 %8 0;
254 -
    store w32 %96 %8 12;
261 +
    store w64 %103 %11 0;
262 +
    store w32 %99 %11 12;
255 263
    jmp @append.store32;
256 264
  @then37
257 265
    ret 6;
258 266
  @merge38
259 -
    load w32 %115 %8 12;
260 -
    br.ne w32 %115 4 @then39 @merge40;
267 +
    load w32 %118 %11 12;
268 +
    br.ne w32 %118 4 @then39 @merge40;
261 269
  @then39
262 270
    ret 7;
263 271
  @merge40
264 -
    load w32 %118 %8 8;
265 -
    load w32 %119 %8 12;
266 -
    br.ult w32 %118 %119 @append.store41 @append.grow42;
272 +
    load w32 %121 %11 8;
273 +
    load w32 %122 %11 12;
274 +
    br.ult w32 %121 %122 @append.store41 @append.grow42;
267 275
  @append.store41
268 -
    load w64 %133 %8 0;
269 -
    mul w64 %134 %118 4;
270 -
    add w64 %135 %133 %134;
271 -
    store w32 50 %135 0;
272 -
    add w32 %136 %118 1;
273 -
    store w32 %136 %8 8;
274 -
    load w32 %139 %8 8;
275 -
    br.ne w32 %139 5 @then46 @merge47;
276 +
    load w64 %136 %11 0;
277 +
    mul w64 %137 %121 4;
278 +
    add w64 %138 %136 %137;
279 +
    store w32 50 %138 0;
280 +
    add w32 %139 %121 1;
281 +
    store w32 %139 %11 8;
282 +
    load w32 %142 %11 8;
283 +
    br.ne w32 %142 5 @then46 @merge47;
276 284
  @append.grow42
277 -
    shl w32 %120 %119 1;
278 -
    or w32 %121 %120 1;
279 -
    load w64 %122 %5 0;
280 -
    load w64 %123 %5 8;
281 -
    mul w32 %124 %121 4;
282 -
    call w64 %125 %122(%123, %124, 4);
283 -
    load w64 %126 %8 0;
284 -
    mul w32 %127 %118 4;
285 +
    shl w32 %123 %122 1;
286 +
    or w32 %124 %123 1;
287 +
    load w64 %125 %7 0;
288 +
    load w64 %126 %7 8;
289 +
    mul w32 %127 %124 4;
290 +
    call w64 %128 %125(%126, %127, 4);
291 +
    load w64 %129 %11 0;
292 +
    mul w32 %130 %121 4;
285 293
    jmp @append43(0);
286 -
  @append43(w32 %128)
287 -
    br.ult w32 %128 %127 @append44 @append45;
294 +
  @append43(w32 %131)
295 +
    br.ult w32 %131 %130 @append44 @append45;
288 296
  @append44
289 -
    add w64 %129 %126 %128;
290 -
    load w8 %130 %129 0;
291 -
    add w64 %131 %125 %128;
292 -
    store w8 %130 %131 0;
293 -
    add w32 %132 %128 1;
294 -
    jmp @append43(%132);
297 +
    add w64 %132 %129 %131;
298 +
    load w8 %133 %132 0;
299 +
    add w64 %134 %128 %131;
300 +
    store w8 %133 %134 0;
301 +
    add w32 %135 %131 1;
302 +
    jmp @append43(%135);
295 303
  @append45
296 -
    store w64 %125 %8 0;
297 -
    store w32 %121 %8 12;
304 +
    store w64 %128 %11 0;
305 +
    store w32 %124 %11 12;
298 306
    jmp @append.store41;
299 307
  @then46
300 308
    ret 8;
301 309
  @merge47
302 -
    load w32 %140 %8 12;
303 -
    br.ne w32 %140 9 @then48 @merge49;
310 +
    load w32 %143 %11 12;
311 +
    br.ne w32 %143 9 @then48 @merge49;
304 312
  @then48
305 313
    ret 9;
306 314
  @merge49
307 -
    load w32 %141 %8 8;
308 -
    br.ult w32 4 %141 @guard#pass52 @guard#trap53;
315 +
    load w32 %144 %11 8;
316 +
    br.ult w32 4 %144 @guard#pass52 @guard#trap53;
309 317
  @then50
310 318
    ret 10;
311 319
  @merge51
312 -
    load w32 %146 %8 8;
313 -
    br.ult w32 0 %146 @guard#pass56 @guard#trap57;
320 +
    load w32 %149 %11 8;
321 +
    br.ult w32 0 %149 @guard#pass56 @guard#trap57;
314 322
  @guard#pass52
315 -
    load w64 %142 %8 0;
316 -
    mul w64 %143 4 4;
317 -
    add w64 %144 %142 %143;
318 -
    sload w32 %145 %144 0;
319 -
    br.ne w32 %145 50 @then50 @merge51;
323 +
    load w64 %145 %11 0;
324 +
    mul w64 %146 4 4;
325 +
    add w64 %147 %145 %146;
326 +
    sload w32 %148 %147 0;
327 +
    br.ne w32 %148 50 @then50 @merge51;
320 328
  @guard#trap53
321 329
    ebreak;
322 330
    unreachable;
323 331
  @then54
324 332
    ret 11;
325 333
  @merge55
326 -
    load w32 %149 %8 8;
327 -
    br.ult w32 3 %149 @guard#pass60 @guard#trap61;
334 +
    load w32 %152 %11 8;
335 +
    br.ult w32 3 %152 @guard#pass60 @guard#trap61;
328 336
  @guard#pass56
329 -
    load w64 %147 %8 0;
330 -
    sload w32 %148 %147 0;
331 -
    br.ne w32 %148 10 @then54 @merge55;
337 +
    load w64 %150 %11 0;
338 +
    sload w32 %151 %150 0;
339 +
    br.ne w32 %151 10 @then54 @merge55;
332 340
  @guard#trap57
333 341
    ebreak;
334 342
    unreachable;
335 343
  @then58
336 344
    ret 12;
337 345
  @merge59
338 -
    reserve %164 16 8;
339 -
    store w64 %7 %164 0;
340 -
    store w32 0 %164 8;
341 -
    store w32 0 %164 12;
342 -
    load w32 %167 %164 8;
343 -
    load w32 %168 %164 12;
344 -
    br.ult w32 %167 %168 @append.store62 @append.grow63;
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;
345 353
  @guard#pass60
346 -
    load w64 %150 %8 0;
347 -
    mul w64 %151 3 4;
348 -
    add w64 %152 %150 %151;
349 -
    sload w32 %153 %152 0;
350 -
    br.ne w32 %153 40 @then58 @merge59;
354 +
    load w64 %153 %11 0;
355 +
    mul w64 %154 3 4;
356 +
    add w64 %155 %153 %154;
357 +
    sload w32 %156 %155 0;
358 +
    br.ne w32 %156 40 @then58 @merge59;
351 359
  @guard#trap61
352 360
    ebreak;
353 361
    unreachable;
354 362
  @append.store62
355 -
    load w64 %182 %164 0;
356 -
    mul w64 %183 %167 4;
357 -
    add w64 %184 %182 %183;
358 -
    store w32 99 %184 0;
359 -
    add w32 %185 %167 1;
360 -
    store w32 %185 %164 8;
361 -
    load w32 %188 %164 8;
362 -
    br.ne w32 %188 1 @then67 @merge68;
363 +
    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 371
  @append.grow63
364 -
    shl w32 %169 %168 1;
365 -
    or w32 %170 %169 1;
366 -
    load w64 %171 %5 0;
367 -
    load w64 %172 %5 8;
368 -
    mul w32 %173 %170 4;
369 -
    call w64 %174 %171(%172, %173, 4);
370 -
    load w64 %175 %164 0;
371 -
    mul w32 %176 %167 4;
372 +
    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 380
    jmp @append64(0);
373 -
  @append64(w32 %177)
374 -
    br.ult w32 %177 %176 @append65 @append66;
381 +
  @append64(w32 %180)
382 +
    br.ult w32 %180 %179 @append65 @append66;
375 383
  @append65
376 -
    add w64 %178 %175 %177;
377 -
    load w8 %179 %178 0;
378 -
    add w64 %180 %174 %177;
379 -
    store w8 %179 %180 0;
380 -
    add w32 %181 %177 1;
381 -
    jmp @append64(%181);
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);
382 390
  @append66
383 -
    store w64 %174 %164 0;
384 -
    store w32 %170 %164 12;
391 +
    store w64 %177 %167 0;
392 +
    store w32 %173 %167 12;
385 393
    jmp @append.store62;
386 394
  @then67
387 395
    ret 13;
388 396
  @merge68
389 -
    load w32 %189 %164 12;
390 -
    br.ne w32 %189 1 @then69 @merge70;
397 +
    load w32 %192 %167 12;
398 +
    br.ne w32 %192 1 @then69 @merge70;
391 399
  @then69
392 400
    ret 14;
393 401
  @merge70
394 -
    load w32 %190 %164 8;
395 -
    br.ult w32 0 %190 @guard#pass73 @guard#trap74;
402 +
    load w32 %193 %167 8;
403 +
    br.ult w32 0 %193 @guard#pass73 @guard#trap74;
396 404
  @then71
397 405
    ret 15;
398 406
  @merge72
399 -
    load w32 %195 %8 8;
400 -
    load w64 %196 %8 0;
407 +
    load w32 %198 %11 8;
408 +
    load w64 %199 %11 0;
401 409
    jmp @loop75(0, 0);
402 410
  @guard#pass73
403 -
    load w64 %191 %164 0;
404 -
    sload w32 %192 %191 0;
405 -
    br.ne w32 %192 99 @then71 @merge72;
411 +
    load w64 %194 %167 0;
412 +
    sload w32 %195 %194 0;
413 +
    br.ne w32 %195 99 @then71 @merge72;
406 414
  @guard#trap74
407 415
    ebreak;
408 416
    unreachable;
409 -
  @loop75(w32 %197, w32 %201)
410 -
    br.slt w32 %197 %195 @body76 @merge77;
417 +
  @loop75(w32 %200, w32 %204)
418 +
    br.slt w32 %200 %198 @body76 @merge77;
411 419
  @body76
412 -
    mul w64 %198 %197 4;
413 -
    add w64 %199 %196 %198;
414 -
    sload w32 %200 %199 0;
415 -
    add w32 %202 %201 %200;
416 -
    add w32 %203 %197 1;
417 -
    jmp @loop75(%203, %202);
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);
418 426
  @merge77
419 -
    br.ne w32 %201 150 @then78 @merge79;
427 +
    br.ne w32 %204 150 @then78 @merge79;
420 428
  @then78
421 429
    ret 16;
422 430
  @merge79
423 -
    call w64 %217 $arenaAlloc(%3, 2, 1);
424 -
    reserve %218 16 8;
425 -
    store w64 %217 %218 0;
426 -
    store w32 0 %218 8;
427 -
    store w32 2 %218 12;
428 -
    load w32 %222 %218 8;
429 -
    load w32 %223 %218 12;
430 -
    br.ult w32 %222 %223 @append.store80 @append.grow81;
431 +
    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 440
  @append.store80
432 -
    load w64 %237 %218 0;
433 -
    add w64 %238 %237 %222;
434 -
    store w8 171 %238 0;
435 -
    add w32 %239 %222 1;
436 -
    store w32 %239 %218 8;
437 -
    load w32 %244 %218 8;
438 -
    load w32 %245 %218 12;
439 -
    br.ult w32 %244 %245 @append.store85 @append.grow86;
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;
440 449
  @append.grow81
441 -
    shl w32 %224 %223 1;
442 -
    or w32 %225 %224 1;
443 -
    load w64 %226 %5 0;
444 -
    load w64 %227 %5 8;
445 -
    mul w32 %228 %225 1;
446 -
    call w64 %229 %226(%227, %228, 1);
447 -
    load w64 %230 %218 0;
448 -
    mul w32 %231 %222 1;
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;
449 458
    jmp @append82(0);
450 -
  @append82(w32 %232)
451 -
    br.ult w32 %232 %231 @append83 @append84;
459 +
  @append82(w32 %223)
460 +
    br.ult w32 %223 %222 @append83 @append84;
452 461
  @append83
453 -
    add w64 %233 %230 %232;
454 -
    load w8 %234 %233 0;
455 -
    add w64 %235 %229 %232;
456 -
    store w8 %234 %235 0;
457 -
    add w32 %236 %232 1;
458 -
    jmp @append82(%236);
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);
459 468
  @append84
460 -
    store w64 %229 %218 0;
461 -
    store w32 %225 %218 12;
469 +
    store w64 %220 %209 0;
470 +
    store w32 %216 %209 12;
462 471
    jmp @append.store80;
463 472
  @append.store85
464 -
    load w64 %259 %218 0;
465 -
    add w64 %260 %259 %244;
466 -
    store w8 205 %260 0;
467 -
    add w32 %261 %244 1;
468 -
    store w32 %261 %218 8;
469 -
    load w32 %264 %218 8;
470 -
    br.ne w32 %264 2 @then90 @merge91;
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;
471 480
  @append.grow86
472 -
    shl w32 %246 %245 1;
473 -
    or w32 %247 %246 1;
474 -
    load w64 %248 %5 0;
475 -
    load w64 %249 %5 8;
476 -
    mul w32 %250 %247 1;
477 -
    call w64 %251 %248(%249, %250, 1);
478 -
    load w64 %252 %218 0;
479 -
    mul w32 %253 %244 1;
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;
480 489
    jmp @append87(0);
481 -
  @append87(w32 %254)
482 -
    br.ult w32 %254 %253 @append88 @append89;
490 +
  @append87(w32 %245)
491 +
    br.ult w32 %245 %244 @append88 @append89;
483 492
  @append88
484 -
    add w64 %255 %252 %254;
485 -
    load w8 %256 %255 0;
486 -
    add w64 %257 %251 %254;
487 -
    store w8 %256 %257 0;
488 -
    add w32 %258 %254 1;
489 -
    jmp @append87(%258);
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);
490 499
  @append89
491 -
    store w64 %251 %218 0;
492 -
    store w32 %247 %218 12;
500 +
    store w64 %242 %209 0;
501 +
    store w32 %238 %209 12;
493 502
    jmp @append.store85;
494 503
  @then90
495 504
    ret 17;
496 505
  @merge91
497 -
    load w32 %265 %218 8;
498 -
    br.ult w32 0 %265 @guard#pass94 @guard#trap95;
506 +
    load w32 %256 %209 8;
507 +
    br.ult w32 0 %256 @guard#pass94 @guard#trap95;
499 508
  @then92
500 509
    ret 18;
501 510
  @merge93
502 -
    load w32 %268 %218 8;
503 -
    br.ult w32 1 %268 @guard#pass98 @guard#trap99;
511 +
    load w32 %259 %209 8;
512 +
    br.ult w32 1 %259 @guard#pass98 @guard#trap99;
504 513
  @guard#pass94
505 -
    load w64 %266 %218 0;
506 -
    load w8 %267 %266 0;
507 -
    br.ne w8 %267 171 @then92 @merge93;
514 +
    load w64 %257 %209 0;
515 +
    load w8 %258 %257 0;
516 +
    br.ne w8 %258 171 @then92 @merge93;
508 517
  @guard#trap95
509 518
    ebreak;
510 519
    unreachable;
511 520
  @then96
512 521
    ret 19;
513 522
  @merge97
514 -
    load w32 %274 %218 8;
515 -
    load w32 %275 %218 12;
516 -
    br.ult w32 %274 %275 @append.store100 @append.grow101;
523 +
    load w32 %265 %209 8;
524 +
    load w32 %266 %209 12;
525 +
    br.ult w32 %265 %266 @append.store100 @append.grow101;
517 526
  @guard#pass98
518 -
    load w64 %269 %218 0;
519 -
    add w64 %270 %269 1;
520 -
    load w8 %271 %270 0;
521 -
    br.ne w8 %271 205 @then96 @merge97;
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;
522 531
  @guard#trap99
523 532
    ebreak;
524 533
    unreachable;
525 534
  @append.store100
526 -
    load w64 %289 %218 0;
527 -
    add w64 %290 %289 %274;
528 -
    store w8 239 %290 0;
529 -
    add w32 %291 %274 1;
530 -
    store w32 %291 %218 8;
531 -
    load w32 %294 %218 8;
532 -
    br.ne w32 %294 3 @then105 @merge106;
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;
533 542
  @append.grow101
534 -
    shl w32 %276 %275 1;
535 -
    or w32 %277 %276 1;
536 -
    load w64 %278 %5 0;
537 -
    load w64 %279 %5 8;
538 -
    mul w32 %280 %277 1;
539 -
    call w64 %281 %278(%279, %280, 1);
540 -
    load w64 %282 %218 0;
541 -
    mul w32 %283 %274 1;
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;
542 551
    jmp @append102(0);
543 -
  @append102(w32 %284)
544 -
    br.ult w32 %284 %283 @append103 @append104;
552 +
  @append102(w32 %275)
553 +
    br.ult w32 %275 %274 @append103 @append104;
545 554
  @append103
546 -
    add w64 %285 %282 %284;
547 -
    load w8 %286 %285 0;
548 -
    add w64 %287 %281 %284;
549 -
    store w8 %286 %287 0;
550 -
    add w32 %288 %284 1;
551 -
    jmp @append102(%288);
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);
552 561
  @append104
553 -
    store w64 %281 %218 0;
554 -
    store w32 %277 %218 12;
562 +
    store w64 %272 %209 0;
563 +
    store w32 %268 %209 12;
555 564
    jmp @append.store100;
556 565
  @then105
557 566
    ret 20;
558 567
  @merge106
559 -
    load w32 %295 %218 12;
560 -
    br.ne w32 %295 5 @then107 @merge108;
568 +
    load w32 %286 %209 12;
569 +
    br.ne w32 %286 5 @then107 @merge108;
561 570
  @then107
562 571
    ret 21;
563 572
  @merge108
564 -
    load w32 %296 %218 8;
565 -
    br.ult w32 2 %296 @guard#pass111 @guard#trap112;
573 +
    load w32 %287 %209 8;
574 +
    br.ult w32 2 %287 @guard#pass111 @guard#trap112;
566 575
  @then109
567 576
    ret 22;
568 577
  @merge110
569 -
    load w32 %300 %218 8;
570 -
    br.ult w32 0 %300 @guard#pass115 @guard#trap116;
578 +
    load w32 %291 %209 8;
579 +
    br.ult w32 0 %291 @guard#pass115 @guard#trap116;
571 580
  @guard#pass111
572 -
    load w64 %297 %218 0;
573 -
    add w64 %298 %297 2;
574 -
    load w8 %299 %298 0;
575 -
    br.ne w8 %299 239 @then109 @merge110;
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;
576 585
  @guard#trap112
577 586
    ebreak;
578 587
    unreachable;
579 588
  @then113
580 589
    ret 23;
581 590
  @merge114
582 -
    mul w32 %309 4 2;
583 -
    call w64 %310 $arenaAlloc(%3, %309, 4);
584 -
    reserve %311 16 8;
585 -
    store w64 %310 %311 0;
586 -
    store w32 0 %311 8;
587 -
    store w32 2 %311 12;
588 -
    load w32 %314 %311 8;
589 -
    load w32 %315 %311 12;
590 -
    br.ult w32 %314 %315 @append.store117 @append.grow118;
591 +
    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 601
  @guard#pass115
592 -
    load w64 %301 %218 0;
593 -
    load w8 %302 %301 0;
594 -
    br.ne w8 %302 171 @then113 @merge114;
602 +
    load w64 %292 %209 0;
603 +
    load w8 %293 %292 0;
604 +
    br.ne w8 %293 171 @then113 @merge114;
595 605
  @guard#trap116
596 606
    ebreak;
597 607
    unreachable;
598 608
  @append.store117
599 -
    load w64 %329 %311 0;
600 -
    mul w64 %330 %314 4;
601 -
    add w64 %331 %329 %330;
602 -
    store w32 42 %331 0;
603 -
    add w32 %332 %314 1;
604 -
    store w32 %332 %311 8;
605 -
    blit %311 %311 16;
606 -
    load w32 %335 %311 8;
607 -
    br.eq w32 %335 1 @assert.ok123 @assert.fail122;
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;
608 618
  @append.grow118
609 -
    shl w32 %316 %315 1;
610 -
    or w32 %317 %316 1;
611 -
    load w64 %318 %5 0;
612 -
    load w64 %319 %5 8;
613 -
    mul w32 %320 %317 4;
614 -
    call w64 %321 %318(%319, %320, 4);
615 -
    load w64 %322 %311 0;
616 -
    mul w32 %323 %314 4;
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;
617 627
    jmp @append119(0);
618 -
  @append119(w32 %324)
619 -
    br.ult w32 %324 %323 @append120 @append121;
628 +
  @append119(w32 %310)
629 +
    br.ult w32 %310 %309 @append120 @append121;
620 630
  @append120
621 -
    add w64 %325 %322 %324;
622 -
    load w8 %326 %325 0;
623 -
    add w64 %327 %321 %324;
624 -
    store w8 %326 %327 0;
625 -
    add w32 %328 %324 1;
626 -
    jmp @append119(%328);
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);
627 637
  @append121
628 -
    store w64 %321 %311 0;
629 -
    store w32 %317 %311 12;
638 +
    store w64 %307 %297 0;
639 +
    store w32 %303 %297 12;
630 640
    jmp @append.store117;
631 641
  @assert.fail122
632 642
    unreachable;
633 643
  @assert.ok123
634 -
    load w32 %336 %311 8;
635 -
    br.ult w32 0 %336 @guard#pass126 @guard#trap127;
644 +
    load w32 %322 %297 8;
645 +
    br.ult w32 0 %322 @guard#pass126 @guard#trap127;
636 646
  @assert.fail124
637 647
    unreachable;
638 648
  @assert.ok125
639 -
    load w32 %341 %311 8;
640 -
    load w32 %342 %311 12;
641 -
    br.ult w32 %341 %342 @append.store128 @append.grow129;
649 +
    load w32 %327 %297 8;
650 +
    load w32 %328 %297 12;
651 +
    br.ult w32 %327 %328 @append.store128 @append.grow129;
642 652
  @guard#pass126
643 -
    load w64 %337 %311 0;
644 -
    sload w32 %338 %337 0;
645 -
    br.eq w32 %338 42 @assert.ok125 @assert.fail124;
653 +
    load w64 %323 %297 0;
654 +
    sload w32 %324 %323 0;
655 +
    br.eq w32 %324 42 @assert.ok125 @assert.fail124;
646 656
  @guard#trap127
647 657
    ebreak;
648 658
    unreachable;
649 659
  @append.store128
650 -
    load w64 %356 %311 0;
651 -
    mul w64 %357 %341 4;
652 -
    add w64 %358 %356 %357;
653 -
    store w32 43 %358 0;
654 -
    add w32 %359 %341 1;
655 -
    store w32 %359 %311 8;
656 -
    blit %311 %311 16;
657 -
    load w32 %362 %311 8;
658 -
    br.eq w32 %362 2 @assert.ok134 @assert.fail133;
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;
659 669
  @append.grow129
660 -
    shl w32 %343 %342 1;
661 -
    or w32 %344 %343 1;
662 -
    load w64 %345 %5 0;
663 -
    load w64 %346 %5 8;
664 -
    mul w32 %347 %344 4;
665 -
    call w64 %348 %345(%346, %347, 4);
666 -
    load w64 %349 %311 0;
667 -
    mul w32 %350 %341 4;
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;
668 678
    jmp @append130(0);
669 -
  @append130(w32 %351)
670 -
    br.ult w32 %351 %350 @append131 @append132;
679 +
  @append130(w32 %337)
680 +
    br.ult w32 %337 %336 @append131 @append132;
671 681
  @append131
672 -
    add w64 %352 %349 %351;
673 -
    load w8 %353 %352 0;
674 -
    add w64 %354 %348 %351;
675 -
    store w8 %353 %354 0;
676 -
    add w32 %355 %351 1;
677 -
    jmp @append130(%355);
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);
678 688
  @append132
679 -
    store w64 %348 %311 0;
680 -
    store w32 %344 %311 12;
689 +
    store w64 %334 %297 0;
690 +
    store w32 %330 %297 12;
681 691
    jmp @append.store128;
682 692
  @assert.fail133
683 693
    unreachable;
684 694
  @assert.ok134
685 -
    load w32 %363 %311 8;
686 -
    br.ult w32 0 %363 @guard#pass135 @guard#trap136;
695 +
    load w32 %349 %297 8;
696 +
    br.ult w32 0 %349 @guard#pass135 @guard#trap136;
687 697
  @guard#pass135
688 -
    load w64 %364 %311 0;
689 -
    load w32 %367 %311 8;
690 -
    load w32 %368 %311 12;
691 -
    br.ult w32 %367 %368 @append.store137 @append.grow138;
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;
692 702
  @guard#trap136
693 703
    ebreak;
694 704
    unreachable;
695 705
  @append.store137
696 -
    load w64 %382 %311 0;
697 -
    mul w64 %383 %367 4;
698 -
    add w64 %384 %382 %383;
699 -
    store w32 44 %384 0;
700 -
    add w32 %385 %367 1;
701 -
    store w32 %385 %311 8;
702 -
    blit %311 %311 16;
703 -
    load w32 %388 %311 8;
704 -
    br.eq w32 %388 3 @assert.ok143 @assert.fail142;
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;
705 715
  @append.grow138
706 -
    shl w32 %369 %368 1;
707 -
    or w32 %370 %369 1;
708 -
    load w64 %371 %5 0;
709 -
    load w64 %372 %5 8;
710 -
    mul w32 %373 %370 4;
711 -
    call w64 %374 %371(%372, %373, 4);
712 -
    load w64 %375 %311 0;
713 -
    mul w32 %376 %367 4;
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;
714 724
    jmp @append139(0);
715 -
  @append139(w32 %377)
716 -
    br.ult w32 %377 %376 @append140 @append141;
725 +
  @append139(w32 %363)
726 +
    br.ult w32 %363 %362 @append140 @append141;
717 727
  @append140
718 -
    add w64 %378 %375 %377;
719 -
    load w8 %379 %378 0;
720 -
    add w64 %380 %374 %377;
721 -
    store w8 %379 %380 0;
722 -
    add w32 %381 %377 1;
723 -
    jmp @append139(%381);
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);
724 734
  @append141
725 -
    store w64 %374 %311 0;
726 -
    store w32 %370 %311 12;
735 +
    store w64 %360 %297 0;
736 +
    store w32 %356 %297 12;
727 737
    jmp @append.store137;
728 738
  @assert.fail142
729 739
    unreachable;
730 740
  @assert.ok143
731 -
    load w32 %389 %311 12;
732 -
    br.eq w32 %389 5 @assert.ok145 @assert.fail144;
741 +
    load w32 %375 %297 12;
742 +
    br.eq w32 %375 5 @assert.ok145 @assert.fail144;
733 743
  @assert.fail144
734 744
    unreachable;
735 745
  @assert.ok145
736 -
    load w32 %390 %311 8;
737 -
    br.ult w32 0 %390 @guard#pass146 @guard#trap147;
746 +
    load w32 %376 %297 8;
747 +
    br.ult w32 0 %376 @guard#pass146 @guard#trap147;
738 748
  @guard#pass146
739 -
    load w64 %391 %311 0;
740 -
    br.ne w64 %364 %391 @assert.ok149 @assert.fail148;
749 +
    load w64 %377 %297 0;
750 +
    br.ne w64 %350 %377 @assert.ok149 @assert.fail148;
741 751
  @guard#trap147
742 752
    ebreak;
743 753
    unreachable;
744 754
  @assert.fail148
745 755
    unreachable;
746 756
  @assert.ok149
747 -
    load w32 %394 %311 8;
748 -
    br.ult w32 0 %394 @guard#pass152 @guard#trap153;
757 +
    load w32 %380 %297 8;
758 +
    br.ult w32 0 %380 @guard#pass152 @guard#trap153;
749 759
  @assert.fail150
750 760
    unreachable;
751 761
  @assert.ok151
752 -
    load w32 %397 %311 8;
753 -
    br.ult w32 1 %397 @guard#pass156 @guard#trap157;
762 +
    load w32 %383 %297 8;
763 +
    br.ult w32 1 %383 @guard#pass156 @guard#trap157;
754 764
  @guard#pass152
755 -
    load w64 %395 %311 0;
756 -
    sload w32 %396 %395 0;
757 -
    br.eq w32 %396 42 @assert.ok151 @assert.fail150;
765 +
    load w64 %381 %297 0;
766 +
    sload w32 %382 %381 0;
767 +
    br.eq w32 %382 42 @assert.ok151 @assert.fail150;
758 768
  @guard#trap153
759 769
    ebreak;
760 770
    unreachable;
761 771
  @assert.fail154
762 772
    unreachable;
763 773
  @assert.ok155
764 -
    load w32 %402 %311 8;
765 -
    br.ult w32 2 %402 @guard#pass160 @guard#trap161;
774 +
    load w32 %388 %297 8;
775 +
    br.ult w32 2 %388 @guard#pass160 @guard#trap161;
766 776
  @guard#pass156
767 -
    load w64 %398 %311 0;
768 -
    mul w64 %399 1 4;
769 -
    add w64 %400 %398 %399;
770 -
    sload w32 %401 %400 0;
771 -
    br.eq w32 %401 43 @assert.ok155 @assert.fail154;
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;
772 782
  @guard#trap157
773 783
    ebreak;
774 784
    unreachable;
775 785
  @assert.fail158
776 786
    unreachable;
777 787
  @assert.ok159
778 788
    ret 0;
779 789
  @guard#pass160
780 -
    load w64 %403 %311 0;
781 -
    mul w64 %404 2 4;
782 -
    add w64 %405 %403 %404;
783 -
    sload w32 %406 %405 0;
784 -
    br.eq w32 %406 44 @assert.ok159 @assert.fail158;
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;
785 795
  @guard#trap161
786 796
    ebreak;
787 797
    unreachable;
788 798
}
789 -
test/tests/slice.assign.rad +4 -4
1 1
//! returns: 0
2 2
3 -
@default fn main() -> i32 {
3 +
@default unsafe fn main() -> i32 {
4 4
    // Fill entire array.
5 5
    let mut a: [i32; 4] = [1, 2, 3, 4];
6 6
    set a[..] = 0;
7 7
    assert a == [0, 0, 0, 0];
8 8
11 11
    set b[1..3] = 99;
12 12
    assert b == [1, 99, 99, 4];
13 13
14 14
    // Fill mutable slice.
15 15
    let mut c: [i32; 4] = [10, 20, 30, 40];
16 -
    let cs: *mut [i32] = &mut c[..];
16 +
    let cs: *unsafe mut [i32] = &mut c[..];
17 17
    set cs[..] = 7;
18 18
    assert c == [7, 7, 7, 7];
19 19
20 20
    // Fill sub-range of mutable slice.
21 21
    let mut d: [i32; 5] = [1, 2, 3, 4, 5];
22 -
    let ds: *mut [i32] = &mut d[..];
22 +
    let ds: *unsafe mut [i32] = &mut d[..];
23 23
    set ds[1..4] = 0;
24 24
    assert d == [1, 0, 0, 0, 5];
25 25
26 26
    // Copy slice into array.
27 27
    let mut e: [i32; 3] = [0, 0, 0];
40 40
    set g[1..1] = 99;
41 41
    assert g == [1, 2, 3];
42 42
43 43
    // Copy between sub-ranges.
44 44
    let mut i: [i32; 10] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
45 -
    let s: *mut [i32] = &mut i[..];
45 +
    let s: *unsafe mut [i32] = &mut i[..];
46 46
    set s[1..4] = &s[7..10];
47 47
    assert i[1] == 80 and i[2] == 90 and i[3] == 100;
48 48
49 49
    // Fill u8 array.
50 50
    let mut h: [u8; 4] = [65, 66, 67, 68];
test/tests/slice.cap.rad +3 -3
1 1
//! returns: 0
2 2
//! Test slice capacity field and @sliceOf builtin.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    // Test that regular slices have cap == len.
6 6
    let mut arr: [i32; 4] = [10, 20, 30, 40];
7 -
    let s = &mut arr[..];
7 +
    let s: *unsafe mut [i32] = &mut arr[..];
8 8
9 9
    if s.cap <> 4 {
10 10
        return 1;
11 11
    }
12 12
    if s.len <> s.cap {
13 13
        return 2;
14 14
    }
15 15
16 16
    // Test @sliceOf with explicit capacity.
17 -
    let ptr = &mut arr[0];
17 +
    let ptr: *unsafe mut i32 = &mut arr[0];
18 18
    let s2 = @sliceOf(ptr, 2, 4);
19 19
20 20
    if s2.len <> 2 {
21 21
        return 3;
22 22
    }
test/tests/slice.delete.rad +2 -2
1 1
//! returns: 0
2 2
//! Test slice .delete() method.
3 3
4 -
@default fn main() -> i32 {
4 +
@default unsafe fn main() -> i32 {
5 5
    let mut arr: [i32; 5] = [10, 20, 30, 40, 50];
6 -
    let mut s = &mut arr[..];
6 +
    let mut s: *unsafe mut [i32] = &mut arr[..];
7 7
8 8
    // Delete middle element (index 2: value 30).
9 9
    s.delete(2);
10 10
11 11
    if s.len <> 4 {
test/tests/slice.empty.suffix.rad +5 -5
1 1
//! returns: 0
2 2
3 -
fn checkArraySuffix() {
3 +
unsafe fn checkArraySuffix() {
4 4
    let xs: [u8; 3] = [10, 20, 30];
5 -
    let empty = &xs[xs.len..xs.len];
5 +
    let empty: *unsafe [u8] = &xs[xs.len..xs.len];
6 6
7 7
    assert empty.len == 0;
8 8
}
9 9
10 -
fn checkSliceSuffix() {
10 +
unsafe fn checkSliceSuffix() {
11 11
    let xs: [u8; 3] = [10, 20, 30];
12 -
    let slice: *[u8] = &xs[..];
12 +
    let slice: *unsafe [u8] = &xs[..];
13 13
    let empty = &slice[slice.len..slice.len];
14 14
15 15
    assert empty.len == 0;
16 16
}
17 17
20 20
    let empty = &text[text.len..text.len];
21 21
22 22
    assert empty.len == 0;
23 23
}
24 24
25 -
@default fn main() -> i32 {
25 +
@default unsafe fn main() -> i32 {
26 26
    checkArraySuffix();
27 27
    checkSliceSuffix();
28 28
    checkStringSuffix();
29 29
    return 0;
30 30
}
test/tests/slice.of.rad +2 -2
1 1
//! returns: 0
2 2
//! Test that @sliceOf produces a fat pointer with the correct length.
3 3
4 -
fn makeSlice(ptr: *mut i32, count: u32) -> *mut [i32] {
4 +
fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] {
5 5
    return @sliceOf(ptr, count);
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let mut arr: [i32; 4] = [10, 20, 30, 40];
10 10
    let s = makeSlice(&mut arr[0], 4);
11 11
12 12
    assert s.len == 4;
13 13
    assert s[0] == 10;
test/tests/slice.range.bounds.check.rad +2 -2
3 3
4 4
fn id(n: u32) -> u32 {
5 5
    return n;
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let xs: [i32; 3] = [1, 2, 3];
10 -
    let slice: *[i32] = &xs[..];
10 +
    let slice: *unsafe [i32] = &xs[..];
11 11
    let bad = &slice[id(4)..id(4)];
12 12
13 13
    return bad.len as i32;
14 14
}
test/tests/slice.range.dynamic.rad +2 -2
2 2
3 3
fn id(n: u32) -> u32 {
4 4
    return n;
5 5
}
6 6
7 -
@default fn main() -> i32 {
7 +
@default unsafe fn main() -> i32 {
8 8
    let xs: [u8; 5] = [10, 20, 30, 40, 50];
9 -
    let slice: *[u8] = &xs[..];
9 +
    let slice: *unsafe [u8] = &xs[..];
10 10
11 11
    let start = id(1);
12 12
    let end = id(4);
13 13
    let mid = &slice[start..end];
14 14
test/tests/slice.range.order.check.rad +2 -2
3 3
4 4
fn id(n: u32) -> u32 {
5 5
    return n;
6 6
}
7 7
8 -
@default fn main() -> i32 {
8 +
@default unsafe fn main() -> i32 {
9 9
    let xs: [i32; 3] = [1, 2, 3];
10 -
    let slice: *[i32] = &xs[..];
10 +
    let slice: *unsafe [i32] = &xs[..];
11 11
    let bad = &slice[id(2)..id(1)];
12 12
13 13
    return bad.len as i32;
14 14
}
test/tests/slice.range.rad +5 -5
1 1
/// Returns a subslice with explicit bounds.
2 -
fn sliceRange(s: *[i32], start: u32, end: u32) -> *[i32] {
2 +
unsafe fn sliceRange(s: &[i32], start: u32, end: u32) -> *unsafe [i32] {
3 3
    return &s[start..end];
4 4
}
5 5
6 6
/// Returns a subslice with an open end bound.
7 -
fn sliceRangeOpenEnd(s: *[i32], start: u32) -> *[i32] {
7 +
unsafe fn sliceRangeOpenEnd(s: &[i32], start: u32) -> *unsafe [i32] {
8 8
    return &s[start..];
9 9
}
10 10
11 11
/// Returns a subslice with an open start bound.
12 -
fn sliceRangeOpenStart(s: *[i32], end: u32) -> *[i32] {
12 +
unsafe fn sliceRangeOpenStart(s: &[i32], end: u32) -> *unsafe [i32] {
13 13
    return &s[..end];
14 14
}
15 15
16 16
/// Returns a full reslice of a slice.
17 -
fn sliceRangeFull(s: *[i32]) -> *[i32] {
17 +
unsafe fn sliceRangeFull(s: &[i32]) -> *unsafe [i32] {
18 18
    return &s[..];
19 19
}
20 20
21 21
/// Returns a subslice from an array parameter.
22 -
fn sliceArray(a: [i32; 4]) -> *[i32] {
22 +
unsafe fn sliceArray(a: [i32; 4]) -> *unsafe [i32] {
23 23
    return &a[1..3];
24 24
}
test/tests/slice.runtime.i32.rad +3 -3
1 1
// Test runtime slice literal with i32 elements
2 2
3 -
fn sliceWithVar(c: i32) -> *[i32] {
3 +
unsafe fn sliceWithVar(c: i32) -> *unsafe [i32] {
4 4
    return &[c];
5 5
}
6 6
7 -
fn sliceWithVars(a: i32, b: i32) -> *[i32] {
7 +
unsafe fn sliceWithVars(a: i32, b: i32) -> *unsafe [i32] {
8 8
    return &[a, b];
9 9
}
10 10
11 -
fn sliceWithMixed(a: i32) -> *[i32] {
11 +
unsafe fn sliceWithMixed(a: i32) -> *unsafe [i32] {
12 12
    return &[a, 42, a];
13 13
}
test/tests/slice.runtime.literal.rad +2 -2
1 1
/// Create a slice literal with a runtime (non-constant) element.
2 -
fn sliceWithVar(c: u8) -> *[u8] {
2 +
unsafe fn sliceWithVar(c: u8) -> *unsafe [u8] {
3 3
    return &[c];
4 4
}
5 5
6 6
/// Create a slice literal with multiple runtime elements.
7 -
fn sliceWithVars(a: u8, b: u8) -> *[u8] {
7 +
unsafe fn sliceWithVars(a: u8, b: u8) -> *unsafe [u8] {
8 8
    return &[a, b];
9 9
}
test/tests/slice.subslice.rad +15 -15
1 1
//! returns: 42
2 2
3 -
fn testBasicSubslice() -> bool {
3 +
unsafe fn testBasicSubslice() -> bool {
4 4
    let arr: [i32; 5] = [10, 20, 30, 40, 50];
5 -
    let slice: *[i32] = &arr[..];        // Full slice [10, 20, 30, 40, 50]
6 -
    let subslice: *[i32] = &slice[1..4]; // Sub-slice [20, 30, 40]
5 +
    let slice: *unsafe [i32] = &arr[..];        // Full slice [10, 20, 30, 40, 50]
6 +
    let subslice: *unsafe [i32] = &slice[1..4]; // Sub-slice [20, 30, 40]
7 7
8 8
    return subslice[0] == 20 and subslice[1] == 30 and subslice[2] == 40;
9 9
}
10 10
11 -
fn testNestedSubslice() -> bool {
11 +
unsafe fn testNestedSubslice() -> bool {
12 12
    let arr: [i32; 6] = [100, 200, 300, 400, 500, 600];
13 -
    let slice1: *[i32] = &arr[1..5];     // [200, 300, 400, 500]
14 -
    let slice2: *[i32] = &slice1[1..3];  // [300, 400]
15 -
    let slice3: *[i32] = &slice2[0..1];  // [300]
13 +
    let slice1: *unsafe [i32] = &arr[1..5];     // [200, 300, 400, 500]
14 +
    let slice2: *unsafe [i32] = &slice1[1..3];  // [300, 400]
15 +
    let slice3: *unsafe [i32] = &slice2[0..1];  // [300]
16 16
17 17
    return slice3[0] == 300;
18 18
}
19 19
20 -
fn testSubsliceLength() -> bool {
20 +
unsafe fn testSubsliceLength() -> bool {
21 21
    let arr: [i32; 4] = [1, 2, 3, 4];
22 -
    let slice: *[i32] = &arr[..];
23 -
    let subslice: *[i32] = &slice[1..3];
22 +
    let slice: *unsafe [i32] = &arr[..];
23 +
    let subslice: *unsafe [i32] = &slice[1..3];
24 24
25 25
    return subslice.len == 2;
26 26
}
27 27
28 -
fn testEdgeCases() -> bool {
28 +
unsafe fn testEdgeCases() -> bool {
29 29
    let arr: [i32; 3] = [7, 8, 9];
30 -
    let slice: *[i32] = &arr[..];
30 +
    let slice: *unsafe [i32] = &arr[..];
31 31
32 32
    // Test single element subslice
33 -
    let single: *[i32] = &slice[1..2];
33 +
    let single: *unsafe [i32] = &slice[1..2];
34 34
    if (single[0] <> 8 or single.len <> 1) {
35 35
        return false;
36 36
    }
37 37
38 38
    // Test empty subslice
39 -
    let empty: *[i32] = &slice[2..2];
39 +
    let empty: *unsafe [i32] = &slice[2..2];
40 40
    if (empty.len <> 0) {
41 41
        return false;
42 42
    }
43 43
    return true;
44 44
}
45 45
46 -
@default fn main() -> i32 {
46 +
@default unsafe fn main() -> i32 {
47 47
    if (
48 48
        testBasicSubslice() and
49 49
        testNestedSubslice() and
50 50
        testSubsliceLength() and
51 51
        testEdgeCases()
test/tests/spill.blockarg.clobber.rad +2 -2
36 36
record State: Copy {
37 37
    h: [u32; 8],
38 38
    w: [u32; 64],
39 39
}
40 40
41 -
fn prepareSchedule(s: *mut State, block: *[u32]) {
41 +
fn prepareSchedule(s: &mut State, block: &[u32]) {
42 42
    let mut i: u32 = 0;
43 43
    while i < 16 {
44 44
        set s.w[i] = block[i];
45 45
        set i += 1;
46 46
    }
50 50
    }
51 51
}
52 52
53 53
/// SHA-256 compression: 64 rounds with 8 rotating working variables.
54 54
/// Exercises heavy register pressure and spilled block-argument shuffles.
55 -
fn compress(s: *mut State, k: *[u32]) {
55 +
fn compress(s: &mut State, k: &[u32]) {
56 56
    let mut a: u32 = s.h[0];
57 57
    let mut b: u32 = s.h[1];
58 58
    let mut c: u32 = s.h[2];
59 59
    let mut d: u32 = s.h[3];
60 60
    let mut e: u32 = s.h[4];
test/tests/spill.loop.rad +1 -1
29 29
        return n;
30 30
    }
31 31
    return (n + a - 1) & (0 - a);
32 32
}
33 33
34 -
fn computeRecordLayout(tags: *[u8]) -> Layout {
34 +
fn computeRecordLayout(tags: &[u8]) -> Layout {
35 35
    let mut currentOffset: u32 = 0;
36 36
    let mut maxAlignment: u32 = 1;
37 37
    for i in 0..tags.len {
38 38
        let layout = getLayout(tags[i]);
39 39
        set currentOffset = alignUp(currentOffset, layout.alignment);
test/tests/trait.aggregate.ret.rad +9 -9
14 14
    y: i32,
15 15
    z: i32,
16 16
}
17 17
18 18
trait Geometry {
19 -
    fn (*Geometry) origin() -> Point;
20 -
    fn (*Geometry) center() -> Vec3;
21 -
    fn (*Geometry) maybe() -> ?i32;
19 +
    unsafe fn (*unsafe Geometry) origin() -> Point;
20 +
    unsafe fn (*unsafe Geometry) center() -> Vec3;
21 +
    unsafe fn (*unsafe Geometry) maybe() -> ?i32;
22 22
}
23 23
24 24
record Circle: Copy {
25 25
    cx: i32,
26 26
    cy: i32,
27 27
    radius: i32,
28 28
}
29 29
30 30
instance Geometry for Circle {
31 -
    fn (c: *Circle) origin() -> Point {
31 +
    unsafe fn (c: *unsafe Circle) origin() -> Point {
32 32
        return Point { x: c.cx, y: c.cy };
33 33
    }
34 34
35 -
    fn (c: *Circle) center() -> Vec3 {
35 +
    unsafe fn (c: *unsafe Circle) center() -> Vec3 {
36 36
        return Vec3 { x: c.cx, y: c.cy, z: 0 };
37 37
    }
38 38
39 -
    fn (c: *Circle) maybe() -> ?i32 {
39 +
    unsafe fn (c: *unsafe Circle) maybe() -> ?i32 {
40 40
        if c.radius > 0 {
41 41
            return c.radius;
42 42
        }
43 43
        return nil;
44 44
    }
45 45
}
46 46
47 -
@default fn main() -> i32 {
47 +
@default unsafe fn main() -> i32 {
48 48
    let c = Circle { cx: 10, cy: 20, radius: 5 };
49 -
    let g: *opaque Geometry = &c;
49 +
    let g: *unsafe opaque Geometry = &c;
50 50
51 51
    // Small struct return (Point, 8 bytes = pointer size).
52 52
    let p = g.origin();
53 53
    assert p.x == 10;
54 54
    assert p.y == 20;
67 67
        return 7;
68 68
    }
69 69
70 70
    // Optional return - None case.
71 71
    let c2 = Circle { cx: 0, cy: 0, radius: 0 };
72 -
    let g2: *opaque Geometry = &c2;
72 +
    let g2: *unsafe opaque Geometry = &c2;
73 73
    let m2 = g2.maybe();
74 74
    if let _ = m2 {
75 75
        return 8;
76 76
    }
77 77
test/tests/trait.array.optional.rad +12 -12
11 11
record Multiplier: Copy {
12 12
    n: i32,
13 13
}
14 14
15 15
trait Transform {
16 -
    fn (*Transform) apply(x: i32) -> i32;
16 +
    unsafe fn (*unsafe Transform) apply(x: i32) -> i32;
17 17
}
18 18
19 19
instance Transform for Adder {
20 -
    fn (a: *Adder) apply(x: i32) -> i32 {
20 +
    unsafe fn (a: *unsafe Adder) apply(x: i32) -> i32 {
21 21
        return x + a.n;
22 22
    }
23 23
}
24 24
25 25
instance Transform for Multiplier {
26 -
    fn (m: *Multiplier) apply(x: i32) -> i32 {
26 +
    unsafe fn (m: *unsafe Multiplier) apply(x: i32) -> i32 {
27 27
        return x * m.n;
28 28
    }
29 29
}
30 30
31 31
/// Apply a chain of transforms to a value.
32 -
fn applyAll(transforms: *[*opaque Transform], value: i32) -> i32 {
32 +
unsafe fn applyAll(transforms: *unsafe [*unsafe opaque Transform], value: i32) -> i32 {
33 33
    let mut result = value;
34 34
    for i in 0..transforms.len {
35 35
        set result = transforms[i].apply(result);
36 36
    }
37 37
    return result;
38 38
}
39 39
40 40
/// Apply an optional transform, returning the original value if nil.
41 -
fn applyMaybe(t: ?*opaque Transform, value: i32) -> i32 {
41 +
unsafe fn applyMaybe(t: ?*unsafe opaque Transform, value: i32) -> i32 {
42 42
    if let tr = t {
43 43
        return tr.apply(value);
44 44
    }
45 45
    return value;
46 46
}
47 47
48 -
@default fn main() -> i32 {
48 +
@default unsafe fn main() -> i32 {
49 49
    let a1 = Adder { n: 10 };
50 50
    let m1 = Multiplier { n: 3 };
51 51
    let a2 = Adder { n: 5 };
52 52
53 -
    let t1: *opaque Transform = &a1;
54 -
    let t2: *opaque Transform = &m1;
55 -
    let t3: *opaque Transform = &a2;
53 +
    let t1: *unsafe opaque Transform = &a1;
54 +
    let t2: *unsafe opaque Transform = &m1;
55 +
    let t3: *unsafe opaque Transform = &a2;
56 56
57 57
    // Array of trait objects.
58 -
    let transforms: [*opaque Transform; 3] = [t1, t2, t3];
58 +
    let transforms: [*unsafe opaque Transform; 3] = [t1, t2, t3];
59 59
    // Chain: (1 + 10) * 3 + 5 = 38
60 60
    let result = applyAll(&transforms[..], 1);
61 61
    assert result == 38;
62 62
63 63
    // Optional trait object - Some case.
64 -
    let opt: ?*opaque Transform = t1;
64 +
    let opt: ?*unsafe opaque Transform = t1;
65 65
    let r2 = applyMaybe(opt, 5);
66 66
    assert r2 == 15;
67 67
68 68
    // Optional trait object - None case.
69 -
    let none: ?*opaque Transform = nil;
69 +
    let none: ?*unsafe opaque Transform = nil;
70 70
    let r3 = applyMaybe(none, 5);
71 71
    assert r3 == 5;
72 72
73 73
    return 0;
74 74
}
test/tests/trait.basic.rad +4 -4
3 3
record Counter: Copy {
4 4
    value: i32,
5 5
}
6 6
7 7
trait Adder {
8 -
    fn (*mut Adder) add(n: i32) -> i32;
8 +
    unsafe fn (*unsafe mut Adder) add(n: i32) -> i32;
9 9
}
10 10
11 11
instance Adder for Counter {
12 -
    fn (c: *mut Counter) add(n: i32) -> i32 {
12 +
    unsafe fn (c: *unsafe mut Counter) add(n: i32) -> i32 {
13 13
        set c.value = c.value + n;
14 14
        return c.value;
15 15
    }
16 16
}
17 17
18 -
@default fn main() -> i32 {
18 +
@default unsafe fn main() -> i32 {
19 19
    let mut c = Counter { value: 10 };
20 -
    let a: *mut opaque Adder = &mut c;
20 +
    let a: *unsafe mut opaque Adder = &mut c;
21 21
22 22
    let result = a.add(5);
23 23
    // c.value should be 15 now.
24 24
    assert result == 15;
25 25
    let result2 = a.add(3);
test/tests/trait.control.flow.rad +6 -6
8 8
record Counter: Copy {
9 9
    value: i32,
10 10
}
11 11
12 12
trait Stepper {
13 -
    fn (*mut Stepper) step() -> i32;
14 -
    fn (*Stepper) current() -> i32;
13 +
    unsafe fn (*unsafe mut Stepper) step() -> i32;
14 +
    unsafe fn (*unsafe Stepper) current() -> i32;
15 15
}
16 16
17 17
instance Stepper for Counter {
18 -
    fn (c: *mut Counter) step() -> i32 {
18 +
    unsafe fn (c: *unsafe mut Counter) step() -> i32 {
19 19
        set c.value = c.value + 1;
20 20
        return c.value;
21 21
    }
22 22
23 -
    fn (c: *Counter) current() -> i32 {
23 +
    unsafe fn (c: *unsafe Counter) current() -> i32 {
24 24
        return c.value;
25 25
    }
26 26
}
27 27
28 -
@default fn main() -> i32 {
28 +
@default unsafe fn main() -> i32 {
29 29
    let mut c = Counter { value: 0 };
30 -
    let s: *mut opaque Stepper = &mut c;
30 +
    let s: *unsafe mut opaque Stepper = &mut c;
31 31
32 32
    // Dispatch in a while loop.
33 33
    let mut i: i32 = 0;
34 34
    while i < 5 {
35 35
        s.step();
test/tests/trait.fn.param.rad +12 -12
11 11
record Square: Copy {
12 12
    side: i32,
13 13
}
14 14
15 15
trait Shape {
16 -
    fn (*Shape) area() -> i32;
16 +
    unsafe fn (*unsafe Shape) area() -> i32;
17 17
}
18 18
19 19
trait Scalable {
20 -
    fn (*mut Scalable) scale(factor: i32);
20 +
    unsafe fn (*unsafe mut Scalable) scale(factor: i32);
21 21
}
22 22
23 23
instance Shape for Circle {
24 -
    fn (c: *Circle) area() -> i32 {
24 +
    unsafe fn (c: *unsafe Circle) area() -> i32 {
25 25
        return c.radius * c.radius * 3;
26 26
    }
27 27
}
28 28
29 29
instance Shape for Square {
30 -
    fn (s: *Square) area() -> i32 {
30 +
    unsafe fn (s: *unsafe Square) area() -> i32 {
31 31
        return s.side * s.side;
32 32
    }
33 33
}
34 34
35 35
instance Scalable for Circle {
36 -
    fn (c: *mut Circle) scale(factor: i32) {
36 +
    unsafe fn (c: *unsafe mut Circle) scale(factor: i32) {
37 37
        set c.radius = c.radius * factor;
38 38
    }
39 39
}
40 40
41 41
/// Accept an immutable trait object parameter.
42 -
fn getArea(s: *opaque Shape) -> i32 {
42 +
unsafe fn getArea(s: *unsafe opaque Shape) -> i32 {
43 43
    return s.area();
44 44
}
45 45
46 46
/// Accept a mutable trait object parameter.
47 -
fn doubleSize(s: *mut opaque Scalable) {
47 +
unsafe fn doubleSize(s: *unsafe mut opaque Scalable) {
48 48
    s.scale(2);
49 49
}
50 50
51 51
/// Accept two trait object parameters.
52 -
fn totalArea(a: *opaque Shape, b: *opaque Shape) -> i32 {
52 +
unsafe fn totalArea(a: *unsafe opaque Shape, b: *unsafe opaque Shape) -> i32 {
53 53
    return a.area() + b.area();
54 54
}
55 55
56 -
@default fn main() -> i32 {
56 +
@default unsafe fn main() -> i32 {
57 57
    let c = Circle { radius: 5 };
58 58
    let s = Square { side: 4 };
59 59
60 60
    // Pass immutable trait objects to function.
61 -
    let cs: *opaque Shape = &c;
61 +
    let cs: *unsafe opaque Shape = &c;
62 62
    let ca = getArea(cs);
63 63
    assert ca == 75;
64 64
65 -
    let ss: *opaque Shape = &s;
65 +
    let ss: *unsafe opaque Shape = &s;
66 66
    let sa = getArea(ss);
67 67
    assert sa == 16;
68 68
69 69
    // Pass two different trait objects to same function.
70 70
    let total = totalArea(cs, ss);
71 71
    assert total == 91;
72 72
73 73
    // Pass mutable trait object to function.
74 74
    let mut c2 = Circle { radius: 3 };
75 -
    let sc: *mut opaque Scalable = &mut c2;
75 +
    let sc: *unsafe mut opaque Scalable = &mut c2;
76 76
    doubleSize(sc);
77 77
    assert c2.radius == 6;
78 78
    return 0;
79 79
}
test/tests/trait.multiple.methods.rad +8 -8
8 8
record Accumulator: Copy {
9 9
    total: i32,
10 10
}
11 11
12 12
trait Collector {
13 -
    fn (*mut Collector) add(n: i32) -> i32;
14 -
    fn (*mut Collector) clear();
15 -
    fn (*mut Collector) isEmpty() -> bool;
13 +
    unsafe fn (*unsafe mut Collector) add(n: i32) -> i32;
14 +
    unsafe fn (*unsafe mut Collector) clear();
15 +
    unsafe fn (*unsafe mut Collector) isEmpty() -> bool;
16 16
}
17 17
18 18
instance Collector for Accumulator {
19 -
    fn (a: *mut Accumulator) add(n: i32) -> i32 {
19 +
    unsafe fn (a: *unsafe mut Accumulator) add(n: i32) -> i32 {
20 20
        set a.total = a.total + n;
21 21
        return a.total;
22 22
    }
23 23
24 -
    fn (a: *mut Accumulator) clear() {
24 +
    unsafe fn (a: *unsafe mut Accumulator) clear() {
25 25
        set a.total = 0;
26 26
    }
27 27
28 -
    fn (a: *mut Accumulator) isEmpty() -> bool {
28 +
    unsafe fn (a: *unsafe mut Accumulator) isEmpty() -> bool {
29 29
        return a.total == 0;
30 30
    }
31 31
}
32 32
33 -
@default fn main() -> i32 {
33 +
@default unsafe fn main() -> i32 {
34 34
    let mut acc = Accumulator { total: 0 };
35 -
    let c: *mut opaque Collector = &mut acc;
35 +
    let c: *unsafe mut opaque Collector = &mut acc;
36 36
37 37
    // Initially empty.
38 38
    assert c.isEmpty();
39 39
40 40
    // Add returns running total.
test/tests/trait.multiple.traits.rad +9 -9
8 8
record Counter: Copy {
9 9
    value: i32,
10 10
}
11 11
12 12
trait Incrementable {
13 -
    fn (*mut Incrementable) inc() -> i32;
13 +
    unsafe fn (*unsafe mut Incrementable) inc() -> i32;
14 14
}
15 15
16 16
trait Resettable {
17 -
    fn (*mut Resettable) reset();
18 -
    fn (*mut Resettable) isZero() -> bool;
17 +
    unsafe fn (*unsafe mut Resettable) reset();
18 +
    unsafe fn (*unsafe mut Resettable) isZero() -> bool;
19 19
}
20 20
21 21
instance Incrementable for Counter {
22 -
    fn (c: *mut Counter) inc() -> i32 {
22 +
    unsafe fn (c: *unsafe mut Counter) inc() -> i32 {
23 23
        set c.value = c.value + 1;
24 24
        return c.value;
25 25
    }
26 26
}
27 27
28 28
instance Resettable for Counter {
29 -
    fn (c: *mut Counter) reset() {
29 +
    unsafe fn (c: *unsafe mut Counter) reset() {
30 30
        set c.value = 0;
31 31
    }
32 32
33 -
    fn (c: *mut Counter) isZero() -> bool {
33 +
    unsafe fn (c: *unsafe mut Counter) isZero() -> bool {
34 34
        return c.value == 0;
35 35
    }
36 36
}
37 37
38 -
@default fn main() -> i32 {
38 +
@default unsafe fn main() -> i32 {
39 39
    let mut c = Counter { value: 10 };
40 40
41 41
    // Dispatch through Incrementable.
42 -
    let i: *mut opaque Incrementable = &mut c;
42 +
    let i: *unsafe mut opaque Incrementable = &mut c;
43 43
    let v1 = i.inc();
44 44
    assert v1 == 11;
45 45
46 46
    // Dispatch through Resettable.
47 -
    let r: *mut opaque Resettable = &mut c;
47 +
    let r: *unsafe mut opaque Resettable = &mut c;
48 48
    if r.isZero() {
49 49
        return 2;
50 50
    }
51 51
    r.reset();
52 52
    assert r.isZero();
test/tests/trait.multiple.types.rad +10 -10
12 12
record Cat: Copy {
13 13
    lives: i32,
14 14
}
15 15
16 16
trait Speaker {
17 -
    fn (*Speaker) speak() -> i32;
18 -
    fn (*Speaker) isOld() -> bool;
17 +
    unsafe fn (*unsafe Speaker) speak() -> i32;
18 +
    unsafe fn (*unsafe Speaker) isOld() -> bool;
19 19
}
20 20
21 21
instance Speaker for Dog {
22 -
    fn (d: *Dog) speak() -> i32 {
22 +
    unsafe fn (d: *unsafe Dog) speak() -> i32 {
23 23
        return d.age;
24 24
    }
25 25
26 -
    fn (d: *Dog) isOld() -> bool {
26 +
    unsafe fn (d: *unsafe Dog) isOld() -> bool {
27 27
        return d.age > 10;
28 28
    }
29 29
}
30 30
31 31
instance Speaker for Cat {
32 -
    fn (c: *Cat) speak() -> i32 {
32 +
    unsafe fn (c: *unsafe Cat) speak() -> i32 {
33 33
        return c.lives * 10;
34 34
    }
35 35
36 -
    fn (c: *Cat) isOld() -> bool {
36 +
    unsafe fn (c: *unsafe Cat) isOld() -> bool {
37 37
        return c.lives < 5;
38 38
    }
39 39
}
40 40
41 -
@default fn main() -> i32 {
41 +
@default unsafe fn main() -> i32 {
42 42
    let d = Dog { age: 5 };
43 43
    let c = Cat { lives: 9 };
44 44
45 45
    // Immutable trait object from Dog.
46 -
    let sd: *opaque Speaker = &d;
46 +
    let sd: *unsafe opaque Speaker = &d;
47 47
    let v1 = sd.speak();
48 48
    assert v1 == 5;
49 49
    if sd.isOld() {
50 50
        return 2;
51 51
    }
52 52
53 53
    // Immutable trait object from Cat.
54 -
    let sc: *opaque Speaker = &c;
54 +
    let sc: *unsafe opaque Speaker = &c;
55 55
    let v2 = sc.speak();
56 56
    assert v2 == 90;
57 57
    if sc.isOld() {
58 58
        return 4;
59 59
    }
60 60
61 61
    // Old dog.
62 62
    let oldDog = Dog { age: 15 };
63 -
    let so: *opaque Speaker = &oldDog;
63 +
    let so: *unsafe opaque Speaker = &oldDog;
64 64
    assert so.isOld();
65 65
    return 0;
66 66
}
test/tests/trait.object.rad +4 -4
1 1
record Counter: Copy {
2 2
    value: i32,
3 3
}
4 4
5 5
trait Adder {
6 -
    fn (*mut Adder) add(n: i32) -> i32;
6 +
    unsafe fn (*unsafe mut Adder) add(n: i32) -> i32;
7 7
}
8 8
9 9
instance Adder for Counter {
10 -
    fn (c: *mut Counter) add(n: i32) -> i32 {
10 +
    unsafe fn (c: *unsafe mut Counter) add(n: i32) -> i32 {
11 11
        set c.value = c.value + n;
12 12
        return c.value;
13 13
    }
14 14
}
15 15
16 -
fn use_adder() -> i32 {
16 +
unsafe fn use_adder() -> i32 {
17 17
    let mut c = Counter { value: 0 };
18 -
    let a: *mut opaque Adder = &mut c;
18 +
    let a: *unsafe mut opaque Adder = &mut c;
19 19
    return a.add(1);
20 20
}
test/tests/trait.supertrait.forward.rad +6 -6
1 1
//! returns: 0
2 2
//! A supertrait declared later must contribute methods to its child trait.
3 3
4 4
trait Child: Parent {
5 -
    fn (*Child) child() -> i32;
5 +
    unsafe fn (*unsafe Child) child() -> i32;
6 6
}
7 7
8 8
trait Parent {
9 -
    fn (*Parent) parent() -> i32;
9 +
    unsafe fn (*unsafe Parent) parent() -> i32;
10 10
}
11 11
12 12
record Value: Copy {
13 13
    n: i32,
14 14
}
15 15
16 16
instance Parent for Value {
17 -
    fn (self: *Value) parent() -> i32 {
17 +
    unsafe fn (self: *unsafe Value) parent() -> i32 {
18 18
        return self.n;
19 19
    }
20 20
}
21 21
22 22
instance Child for Value {
23 -
    fn (self: *Value) child() -> i32 {
23 +
    unsafe fn (self: *unsafe Value) child() -> i32 {
24 24
        return self.n + 1;
25 25
    }
26 26
}
27 27
28 -
@default fn main() -> i32 {
28 +
@default unsafe fn main() -> i32 {
29 29
    let value = Value { n: 41 };
30 -
    let object: *opaque Child = &value;
30 +
    let object: *unsafe opaque Child = &value;
31 31
    assert object.parent() == 41;
32 32
    assert object.child() == 42;
33 33
    return 0;
34 34
}
test/tests/trait.supertrait.rad +8 -8
7 7
//! A Socket type implements all three. Coercing a `*opaque ReadWriter` to
8 8
//! `*opaque Reader` or `*opaque Writer` should work, and dispatching through
9 9
//! any of the three trait objects should call the correct methods.
10 10
11 11
trait Reader {
12 -
    fn (*mut Reader) read(buf: *mut [u8]) -> i32;
12 +
    unsafe fn (*unsafe mut Reader) read(buf: *unsafe mut [u8]) -> i32;
13 13
}
14 14
15 15
trait Writer {
16 -
    fn (*mut Writer) write(data: *[u8]) -> i32;
16 +
    unsafe fn (*unsafe mut Writer) write(data: *[u8]) -> i32;
17 17
}
18 18
19 19
trait ReadWriter: Reader + Writer {
20 -
    fn (*mut ReadWriter) flush() -> i32;
20 +
    unsafe fn (*unsafe mut ReadWriter) flush() -> i32;
21 21
}
22 22
23 23
record Socket: Copy {
24 24
    rbuf: [u8; 32],
25 25
    rpos: i32,
27 27
    wbuf: [u8; 32],
28 28
    wpos: i32,
29 29
}
30 30
31 31
instance Reader for Socket {
32 -
    fn (s: *mut Socket) read(buf: *mut [u8]) -> i32 {
32 +
    unsafe fn (s: *unsafe mut Socket) read(buf: *unsafe mut [u8]) -> i32 {
33 33
        let mut i: u32 = 0;
34 34
        while i < buf.len and s.rpos < s.rlen {
35 35
            set buf[i] = s.rbuf[s.rpos as u32];
36 36
            set s.rpos = s.rpos + 1;
37 37
            set i = i + 1;
39 39
        return i as i32;
40 40
    }
41 41
}
42 42
43 43
instance Writer for Socket {
44 -
    fn (s: *mut Socket) write(data: *[u8]) -> i32 {
44 +
    unsafe fn (s: *unsafe mut Socket) write(data: *[u8]) -> i32 {
45 45
        let mut i: u32 = 0;
46 46
        while i < data.len {
47 47
            if s.wpos >= 32 {
48 48
                return s.wpos;
49 49
            }
54 54
        return s.wpos;
55 55
    }
56 56
}
57 57
58 58
instance ReadWriter for Socket {
59 -
    fn (s: *mut Socket) flush() -> i32 {
59 +
    unsafe fn (s: *unsafe mut Socket) flush() -> i32 {
60 60
        let pos = s.wpos;
61 61
        set s.wpos = 0;
62 62
        return pos;
63 63
    }
64 64
}
65 65
66 -
@default fn main() -> i32 {
66 +
@default unsafe fn main() -> i32 {
67 67
    let mut sock = Socket {
68 68
        rbuf: undefined,
69 69
        rpos: 0,
70 70
        rlen: 5,
71 71
        wbuf: undefined,
77 77
    set sock.rbuf[2] = 'l' as u8;
78 78
    set sock.rbuf[3] = 'l' as u8;
79 79
    set sock.rbuf[4] = 'o' as u8;
80 80
81 81
    // Test 1: Use as ReadWriter trait object.
82 -
    let rw: *mut opaque ReadWriter = &mut sock;
82 +
    let rw: *unsafe mut opaque ReadWriter = &mut sock;
83 83
84 84
    // Test 2: Write through the ReadWriter (dispatches via Writer supertrait).
85 85
    let wn = rw.write("abc");
86 86
    assert wn == 3;
87 87
    assert sock.wpos == 3;
test/tests/trait.throws.rad +4 -4
9 9
record StrictParser: Copy {
10 10
    limit: i32,
11 11
}
12 12
13 13
trait Parser {
14 -
    fn (*Parser) parse(n: i32) -> i32 throws (ParseError);
14 +
    unsafe fn (*unsafe Parser) parse(n: i32) -> i32 throws (ParseError);
15 15
}
16 16
17 17
instance Parser for StrictParser {
18 -
    fn (p: *StrictParser) parse(n: i32) -> i32 throws (ParseError) {
18 +
    unsafe fn (p: *unsafe StrictParser) parse(n: i32) -> i32 throws (ParseError) {
19 19
        if n < 0 {
20 20
            throw ParseError::InvalidInput;
21 21
        }
22 22
        if n > p.limit {
23 23
            throw ParseError::Overflow;
24 24
        }
25 25
        return n * 2;
26 26
    }
27 27
}
28 28
29 -
@default fn main() -> i32 {
29 +
@default unsafe fn main() -> i32 {
30 30
    let sp = StrictParser { limit: 100 };
31 -
    let p: *opaque Parser = &sp;
31 +
    let p: *unsafe opaque Parser = &sp;
32 32
33 33
    // Success path.
34 34
    let r1 = try p.parse(5) catch {
35 35
        return 1;
36 36
    };
test/tests/trait.writer.rad +12 -12
5 5
//! A BufferWriter writes to an in-memory buffer; a CountingWriter wraps any
6 6
//! Writer and tracks how many bytes flow through it. This tests trait objects
7 7
//! as struct fields, dispatch chains, and a real-world composition pattern.
8 8
9 9
trait Writer {
10 -
    fn (*mut Writer) write(data: *[u8]) -> i32;
11 -
    fn (*Writer) total() -> i32;
10 +
    unsafe fn (*unsafe mut Writer) write(data: *[u8]) -> i32;
11 +
    unsafe fn (*unsafe Writer) total() -> i32;
12 12
}
13 13
14 14
/// Writes bytes into a fixed-size buffer.
15 15
record BufferWriter: Copy {
16 16
    buf: [u8; 64],
17 17
    pos: i32,
18 18
}
19 19
20 20
instance Writer for BufferWriter {
21 -
    fn (w: *mut BufferWriter) write(data: *[u8]) -> i32 {
21 +
    unsafe fn (w: *unsafe mut BufferWriter) write(data: *[u8]) -> i32 {
22 22
        let mut i: u32 = 0;
23 23
        while i < data.len {
24 24
            if w.pos >= 64 {
25 25
                return w.pos;
26 26
            }
29 29
            set i = i + 1;
30 30
        }
31 31
        return w.pos;
32 32
    }
33 33
34 -
    fn (w: *BufferWriter) total() -> i32 {
34 +
    unsafe fn (w: *unsafe BufferWriter) total() -> i32 {
35 35
        return w.pos;
36 36
    }
37 37
}
38 38
39 39
/// Counts bytes written through it, forwarding to an inner writer.
40 40
record CountingWriter: Copy {
41 -
    inner: *mut opaque Writer,
41 +
    inner: *unsafe mut opaque Writer,
42 42
    count: i32,
43 43
}
44 44
45 45
instance Writer for CountingWriter {
46 -
    fn (w: *mut CountingWriter) write(data: *[u8]) -> i32 {
46 +
    unsafe fn (w: *unsafe mut CountingWriter) write(data: *[u8]) -> i32 {
47 47
        set w.count = w.count + data.len as i32;
48 48
        return w.inner.write(data);
49 49
    }
50 50
51 -
    fn (w: *CountingWriter) total() -> i32 {
51 +
    unsafe fn (w: *unsafe CountingWriter) total() -> i32 {
52 52
        return w.count;
53 53
    }
54 54
}
55 55
56 56
/// Write a slice through any Writer.
57 -
fn emit(w: *mut opaque Writer, data: *[u8]) -> i32 {
57 +
unsafe fn emit(w: *unsafe mut opaque Writer, data: *[u8]) -> i32 {
58 58
    return w.write(data);
59 59
}
60 60
61 -
@default fn main() -> i32 {
61 +
@default unsafe fn main() -> i32 {
62 62
    // Direct BufferWriter usage through trait.
63 63
    let mut buf = BufferWriter { buf: undefined, pos: 0 };
64 -
    let w: *mut opaque Writer = &mut buf;
64 +
    let w: *unsafe mut opaque Writer = &mut buf;
65 65
    emit(w, "hello");
66 66
    assert w.total() == 5;
67 67
    emit(w, " world");
68 68
    assert w.total() == 11;
69 69
73 73
    assert buf.buf[5] == ' ' as u8;
74 74
    assert buf.buf[10] == 'd' as u8;
75 75
76 76
    // Counting writer wrapping a buffer writer.
77 77
    let mut buf2 = BufferWriter { buf: undefined, pos: 0 };
78 -
    let bw2: *mut opaque Writer = &mut buf2;
78 +
    let bw2: *unsafe mut opaque Writer = &mut buf2;
79 79
    let mut cw = CountingWriter { inner: bw2, count: 0 };
80 -
    let w2: *mut opaque Writer = &mut cw;
80 +
    let w2: *unsafe mut opaque Writer = &mut cw;
81 81
82 82
    emit(w2, "abc");
83 83
    assert cw.count == 3;
84 84
    // Underlying buffer also received the bytes.
85 85
    assert buf2.pos == 3;
test/tests/type.unify.rad +18 -18
45 45
46 46
    return true;
47 47
}
48 48
49 49
/// Verifies assignment compatibility for pointers with identical target types.
50 -
fn testPointerUnification() -> bool {
50 +
unsafe fn testPointerUnification() -> bool {
51 51
    let x: i32 = 42;
52 52
    let y: i32 = 24;
53 -
    let ptr1: *i32 = &x;
54 -
    let ptr2: *i32 = &y;
55 -
    let mut ptrResult: *i32 = ptr1;
53 +
    let ptr1: *unsafe i32 = &x;
54 +
    let ptr2: *unsafe i32 = &y;
55 +
    let mut ptrResult: *unsafe i32 = ptr1;
56 56
    set ptrResult = ptr2;
57 57
58 58
    return true;
59 59
}
60 60
61 61
/// Verifies conversion from array references to slices.
62 -
fn testArrayToSlice() -> bool {
62 +
unsafe fn testArrayToSlice() -> bool {
63 63
    let arr: [i32; 3] = [1, 2, 3];
64 -
    let slice: *[i32] = &arr[..];
64 +
    let slice: *unsafe [i32] = &arr[..];
65 65
66 66
    return true;
67 67
}
68 68
69 69
/// Verifies mixed signed and unsigned arithmetic using an explicit cast.
97 97
98 98
    return true;
99 99
}
100 100
101 101
/// Verifies coercion from pointer values to optional pointer values.
102 -
fn testPointerToOptional() -> bool {
102 +
unsafe fn testPointerToOptional() -> bool {
103 103
    let value: i32 = 42;
104 -
    let ptr: *i32 = &value;
105 -
    let optPtr: ?*i32 = ptr;
104 +
    let ptr: *unsafe i32 = &value;
105 +
    let optPtr: ?*unsafe i32 = ptr;
106 106
107 107
    return true;
108 108
}
109 109
110 110
/// Verifies creation of slices for different, but internally consistent, element types.
111 -
fn testSliceElementUnification() -> bool {
111 +
unsafe fn testSliceElementUnification() -> bool {
112 112
    let arrSmall: [i8; 3] = [1, 2, 3];
113 -
    let sliceSmall: *[i8] = &arrSmall[..];
113 +
    let sliceSmall: *unsafe [i8] = &arrSmall[..];
114 114
115 115
    let arrLarge: [i32; 3] = [10, 20, 30];
116 -
    let sliceLarge: *[i32] = &arrLarge[..];
116 +
    let sliceLarge: *unsafe [i32] = &arrLarge[..];
117 117
118 118
    return true;
119 119
}
120 120
121 121
/// Verifies optional assignments involving `nil` and concrete values.
136 136
137 137
    return true;
138 138
}
139 139
140 140
/// Verifies repeated pointer assignments across multiple values of the same type.
141 -
fn testMultiplePointerAssignments() -> bool {
141 +
unsafe fn testMultiplePointerAssignments() -> bool {
142 142
    let value1: i32 = 42;
143 143
    let value2: i32 = 24;
144 144
    let value3: i32 = 100;
145 145
146 -
    let ptr1: *i32 = &value1;
147 -
    let ptr2: *i32 = &value2;
148 -
    let ptr3: *i32 = &value3;
146 +
    let ptr1: *unsafe i32 = &value1;
147 +
    let ptr2: *unsafe i32 = &value2;
148 +
    let ptr3: *unsafe i32 = &value3;
149 149
150 -
    let mut result: *i32 = ptr1;
150 +
    let mut result: *unsafe i32 = ptr1;
151 151
    set result = ptr2;
152 152
    set result = ptr3;
153 153
154 154
    return true;
155 155
}
162 162
    set result = b2;
163 163
164 164
    return true;
165 165
}
166 166
167 -
@default fn main() -> i32 {
167 +
@default unsafe fn main() -> i32 {
168 168
    let testResult: bool =
169 169
        testNumericUnification() and
170 170
        testOptionalUnification() and
171 171
        testArrayUnification() and
172 172
        testPointerUnification() and
test/tests/union-tag.rad +3 -3
23 23
    V18,
24 24
    V19,
25 25
    V20,
26 26
}
27 27
28 -
fn tag(u: *BigUnion) -> u8 {
29 -
    let p = u as *opaque as *u8;
28 +
unsafe fn tag(u: *unsafe BigUnion) -> u8 {
29 +
    let p = u as *unsafe opaque as *unsafe u8;
30 30
    return *p;
31 31
}
32 32
33 -
@default fn main() -> i32 {
33 +
@default unsafe fn main() -> i32 {
34 34
    let v0 = BigUnion::V0;
35 35
    assert tag(&v0) == 0;
36 36
    let v7 = BigUnion::V7;
37 37
    assert tag(&v7) == 7;
38 38
    let v13 = BigUnion::V13;
test/tests/union.edge.case.3.rad +3 -3
9 9
10 10
record Parser: Copy {
11 11
    root: Node,
12 12
}
13 13
14 -
fn node(p: *mut Parser, value: Node) -> *Node {
14 +
unsafe fn node(p: &mut Parser, value: Node) -> *unsafe Node {
15 15
    set p.root = value;
16 16
    return &p.root;
17 17
}
18 18
19 -
fn nodeBool(p: *mut Parser, value: bool) -> *Node {
19 +
unsafe fn nodeBool(p: &mut Parser, value: bool) -> *unsafe Node {
20 20
    return node(p, Node::Bool(value));
21 21
}
22 22
23 -
@default fn main() -> u32 {
23 +
@default unsafe fn main() -> u32 {
24 24
    let mut parser = Parser { root: undefined };
25 25
26 26
    match *nodeBool(&mut parser, true) {
27 27
        case Node::Bool(v) => {
28 28
            assert v == true;
test/tests/union.mixed.assign.rad +2 -2
10 10
11 11
record Holder: Copy {
12 12
    value: Mixed,
13 13
}
14 14
15 -
fn storePayload(holder: *mut Holder, value: i32) {
15 +
fn storePayload(holder: &mut Holder, value: i32) {
16 16
    set holder.value = Mixed::Payload(value);
17 17
}
18 18
19 -
fn storeFinal(holder: *mut Holder) {
19 +
fn storeFinal(holder: &mut Holder) {
20 20
    set holder.value = Mixed::Final;
21 21
}
22 22
23 23
fn checkIfLet(value: Mixed) -> i32 {
24 24
    if let case Mixed::Payload(v) = value {
test/tests/union.payload.mutref.rad +3 -3
2 2
//! Test accessing and modifying union payload through mutable reference.
3 3
//! This mirrors the pattern in createBlockParam where we match on
4 4
//! &mut blk.sealState and push to incompleteVars.
5 5
6 6
record U32List: Copy {
7 -
    data: *mut [u32],
7 +
    data: *unsafe mut [u32],
8 8
    len: u32,
9 9
}
10 10
11 11
union Sealed: Copy {
12 12
    No { items: U32List },
17 17
    padding1: i32,
18 18
    padding2: i32,
19 19
    state: Sealed,
20 20
}
21 21
22 -
fn addItemViaMatchRef(blk: *mut Block, val: u32) -> bool {
22 +
unsafe fn addItemViaMatchRef(blk: &mut Block, val: u32) -> bool {
23 23
    match &mut blk.state {
24 24
        case Sealed::No { items } => {
25 25
            set items.data[items.len] = val;
26 26
            set items.len += 1;
27 27
            return true;
30 30
            return false;
31 31
        },
32 32
    }
33 33
}
34 34
35 -
@default fn main() -> i32 {
35 +
@default unsafe fn main() -> i32 {
36 36
    let mut buf: [u32; 8] = undefined;
37 37
    let mut blk = Block {
38 38
        padding1: 0,
39 39
        padding2: 0,
40 40
        state: Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } },
test/tests/wildcard.import.owner.rad +1 -0
1 1
//! A wildcard import keeps the defining module for imported functions.
2 2
//! returns: 42
3 3
4 4
mod wildcard_import_owner;
5 +
5 6
use wildcard_import_owner::*;
6 7
7 8
@default fn main() -> i32 {
8 9
    return answer();
9 10
}