Enforce move semantics for safe mutable pointers
27ce65c61e48bbadad9bf415f1c80ccedacd3220b63120c4d7e46d8a8176c407
Treat safe mutable pointers, slices, and trait objects as move-only values. Reject Copy implementations that contain them and preserve temporary call borrows. Use raw pointers for shared mutable compiler graphs and owned or immutable storage for tables, signatures, and diagnostic snapshots. Validate the slice.append allocator contract and use implicit raw borrows throughout compiler call sites. Assisted-by: Codex:gpt-6-astra
1 parent
3b15770b
compiler/radiance.rad
+50 -40
| 142 | 142 | /// Number of assembly source paths. |
|
| 143 | 143 | asmPathCount: u32, |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | /// Compilation context. |
|
| 147 | - | record CompileContext: Copy { |
|
| 147 | + | record CompileContext { |
|
| 148 | 148 | /// Array of packages to compile. |
|
| 149 | 149 | packages: [package::Package; MAX_PACKAGES], |
|
| 150 | 150 | /// Driver inputs for each package slot. |
|
| 151 | 151 | inputs: [PackageInput; MAX_PACKAGES], |
|
| 152 | 152 | /// Number of packages. |
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Root module info for a package. |
|
| 169 | 169 | record RootModule: Copy { |
|
| 170 | 170 | entry: *module::ModuleEntry, |
|
| 171 | - | ast: *mut ast::Node, |
|
| 171 | + | ast: *ast::Node, |
|
| 172 | 172 | } |
|
| 173 | 173 | ||
| 174 | 174 | /// Entry handling for streamed code generation. |
|
| 175 | 175 | union CodegenEntryMode: Copy { |
|
| 176 | 176 | /// Do not reserve an entry jump. |
| 245 | 245 | graph: &mut module::ModuleGraph, |
|
| 246 | 246 | path: *[u8], |
|
| 247 | 247 | nodeArena: &mut ast::NodeArena, |
|
| 248 | 248 | sourceArena: &mut alloc::Arena |
|
| 249 | 249 | ) throws (Error) { |
|
| 250 | - | pkgLog(&*pkg, &["parsing", "(", path, ")", ".."]); |
|
| 250 | + | pkgLog(pkg, &["parsing", "(", path, ")", ".."]); |
|
| 251 | 251 | ||
| 252 | - | let moduleId = try package::registerModule(&mut *pkg, graph, path) catch { |
|
| 252 | + | let moduleId = try package::registerModule(pkg, graph, &mut STRING_POOL, path) catch { |
|
| 253 | 253 | throw error(&["error registering module"]); |
|
| 254 | 254 | }; |
|
| 255 | 255 | // Read file into remaining arena space. |
|
| 256 | 256 | let buffer = alloc::remainingBuf(sourceArena); |
|
| 257 | 257 | if buffer.len == 0 { |
| 415 | 415 | for i in 0..pkgCount { |
|
| 416 | 416 | if i <> entryIdx and inputs[i].startupPath <> nil { |
|
| 417 | 417 | throw error(&["`-start` is only supported on the entry package"]); |
|
| 418 | 418 | } |
|
| 419 | 419 | } |
|
| 420 | - | let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut STRING_POOL, arena); |
|
| 420 | + | let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena); |
|
| 421 | 421 | let mut ctx = CompileContext { |
|
| 422 | 422 | packages: undefined, |
|
| 423 | 423 | inputs, |
|
| 424 | 424 | packageCount: pkgCount, |
|
| 425 | 425 | entryPkgIdx, |
| 442 | 442 | } |
|
| 443 | 443 | return ctx; |
|
| 444 | 444 | } |
|
| 445 | 445 | ||
| 446 | 446 | /// Get the entry package from the context. |
|
| 447 | - | unsafe fn getEntryPackage(ctx: *unsafe CompileContext) -> *unsafe package::Package throws (Error) { |
|
| 447 | + | fn getEntryPackage(ctx: &CompileContext) -> package::Package throws (Error) { |
|
| 448 | 448 | let entryIdx = ctx.entryPkgIdx else { |
|
| 449 | 449 | throw error(&["no entry package specified"]); |
|
| 450 | 450 | }; |
|
| 451 | - | return &ctx.packages[entryIdx]; |
|
| 451 | + | return ctx.packages[entryIdx]; |
|
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Return the startup assembly path for the entry package, if one was supplied. |
|
| 455 | 455 | fn getEntryStartupPath(ctx: &CompileContext) -> ?*[u8] { |
|
| 456 | 456 | let entryIdx = ctx.entryPkgIdx else { |
| 478 | 478 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 479 | 479 | module::printer::printGraph(&ctx.graph, &mut arena); |
|
| 480 | 480 | } |
|
| 481 | 481 | ||
| 482 | 482 | /// Dump the parsed AST. |
|
| 483 | - | unsafe fn dumpAst(ctx: *unsafe CompileContext) throws (Error) { |
|
| 483 | + | unsafe fn dumpAst(ctx: &CompileContext) throws (Error) { |
|
| 484 | 484 | let pkg = try getEntryPackage(ctx); |
|
| 485 | - | let root = try getRootModule(&*pkg, &ctx.graph); |
|
| 485 | + | let root = try getRootModule(&pkg, &ctx.graph); |
|
| 486 | 486 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 487 | 487 | ||
| 488 | 488 | ast::printer::printTree(root.ast, &mut arena); |
|
| 489 | 489 | } |
|
| 490 | 490 |
| 505 | 505 | res, &ctx.graph, entryPkg.name, &mut res.arena, &mut res.arena, options |
|
| 506 | 506 | ); |
|
| 507 | 507 | try lowerAllPackagesInto(ctx, res, &mut low); |
|
| 508 | 508 | ||
| 509 | 509 | // Finalize and return the unified program. |
|
| 510 | - | return lower::finalize(&low); |
|
| 510 | + | return lower::finalize(low); |
|
| 511 | 511 | } |
|
| 512 | 512 | ||
| 513 | 513 | /// Lower all packages into an existing lowerer. |
|
| 514 | 514 | unsafe fn lowerAllPackagesInto( |
|
| 515 | 515 | ctx: *unsafe mut CompileContext, |
| 520 | 520 | panic "lowerAllPackagesInto: no entry package"; |
|
| 521 | 521 | }; |
|
| 522 | 522 | // Lower all packages except entry. |
|
| 523 | 523 | for i in 0..ctx.packageCount { |
|
| 524 | 524 | if i <> entryIdx { |
|
| 525 | - | try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[i], false); |
|
| 525 | + | try lowerPackage(ctx, res, low, &ctx.packages[i], false); |
|
| 526 | 526 | } |
|
| 527 | 527 | } |
|
| 528 | 528 | // Lower entry package. |
|
| 529 | - | try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[entryIdx], true); |
|
| 529 | + | try lowerPackage(ctx, res, low, &ctx.packages[entryIdx], true); |
|
| 530 | 530 | } |
|
| 531 | 531 | ||
| 532 | 532 | /// Lower all modules in a package into the lowerer accumulator. |
|
| 533 | 533 | unsafe fn lowerPackage( |
|
| 534 | - | ctx: *unsafe CompileContext, |
|
| 534 | + | ctx: &CompileContext, |
|
| 535 | 535 | res: *unsafe mut resolver::Resolver, |
|
| 536 | 536 | low: &mut lower::Lowerer, |
|
| 537 | - | pkg: &mut package::Package, |
|
| 537 | + | pkg: &package::Package, |
|
| 538 | 538 | isEntry: bool |
|
| 539 | 539 | ) throws (Error) { |
|
| 540 | 540 | let rootId = pkg.rootModuleId else { |
|
| 541 | 541 | throw error(&["no root module found"]); |
|
| 542 | 542 | }; |
|
| 543 | 543 | // Set lowerer's package context for qualified name generation. |
|
| 544 | 544 | // TODO: We shouldn't have to call this manually. |
|
| 545 | - | lower::setPackage(&mut *low, &ctx.graph, pkg.name); |
|
| 545 | + | lower::setPackage(low, &ctx.graph, pkg.name); |
|
| 546 | 546 | ||
| 547 | - | try lowerModuleTreeInto(ctx, &mut *low, &ctx.graph, rootId, isEntry, pkg); |
|
| 547 | + | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg); |
|
| 548 | 548 | } |
|
| 549 | 549 | ||
| 550 | 550 | /// Recursively lower a module and all its children into the accumulator. |
|
| 551 | 551 | unsafe fn lowerModuleTreeInto( |
|
| 552 | - | ctx: *unsafe CompileContext, |
|
| 552 | + | ctx: &CompileContext, |
|
| 553 | 553 | low: &mut lower::Lowerer, |
|
| 554 | 554 | graph: &module::ModuleGraph, |
|
| 555 | 555 | modId: u16, |
|
| 556 | 556 | isRoot: bool, |
|
| 557 | 557 | pkg: &package::Package |
| 560 | 560 | throw error(&["module entry not found"]); |
|
| 561 | 561 | }; |
|
| 562 | 562 | let modAst = entry.ast else { |
|
| 563 | 563 | throw error(&["module has no AST"]); |
|
| 564 | 564 | }; |
|
| 565 | - | pkgLog(&*pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 565 | + | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 566 | 566 | ||
| 567 | - | try lower::lowerModule(&mut *low, modId, modAst, isRoot) catch err { |
|
| 567 | + | try lower::lowerModule(low, modId, modAst, isRoot) catch err { |
|
| 568 | 568 | io::printError("radiance: "); |
|
| 569 | 569 | io::printError("internal error during lowering: "); |
|
| 570 | 570 | lower::printError(err); |
|
| 571 | 571 | io::printError("\n"); |
|
| 572 | 572 | ||
| 573 | 573 | throw Error::Other; |
|
| 574 | 574 | }; |
|
| 575 | 575 | // Recurse into children. |
|
| 576 | 576 | for i in 0..entry.childrenLen { |
|
| 577 | 577 | let childId = module::childAt(entry, i); |
|
| 578 | - | try lowerModuleTreeInto(ctx, &mut *low, graph, childId, false, pkg); |
|
| 578 | + | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg); |
|
| 579 | 579 | } |
|
| 580 | 580 | } |
|
| 581 | 581 | ||
| 582 | 582 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
|
| 583 | 583 | unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node { |
| 683 | 683 | unsafe fn generateTestRunner( |
|
| 684 | 684 | ctx: *unsafe mut CompileContext, |
|
| 685 | 685 | arena: &mut ast::NodeArena |
|
| 686 | 686 | ) throws (Error) { |
|
| 687 | 687 | let entryPkg = try getEntryPackage(ctx); |
|
| 688 | - | let root = try getRootModule(&*entryPkg, &ctx.graph); |
|
| 688 | + | let root = try getRootModule(&entryPkg, &ctx.graph); |
|
| 689 | 689 | ||
| 690 | 690 | // Collect all test functions across all modules. |
|
| 691 | 691 | let mut tests: [TestDesc; MAX_TESTS] = undefined; |
|
| 692 | 692 | let mut testCount: u32 = 0; |
|
| 693 | 693 |
| 708 | 708 | io::printError(" test(s)\n"); |
|
| 709 | 709 | ||
| 710 | 710 | // Synthesize the `@default` function and append to the root module. |
|
| 711 | 711 | let fnDecl = synthTestMainFn(arena, &tests[..testCount]); |
|
| 712 | 712 | ||
| 713 | - | injectIntoBlock(root.ast, arena, fnDecl); |
|
| 713 | + | let updatedRoot = injectIntoBlock(root.ast, arena, fnDecl); |
|
| 714 | + | try module::setAst(&mut ctx.graph, root.entry.id, updatedRoot) catch { |
|
| 715 | + | throw error(&["failed to set test runner AST"]); |
|
| 716 | + | }; |
|
| 714 | 717 | } |
|
| 715 | 718 | ||
| 716 | 719 | /// Synthesize the test entry point. |
|
| 717 | 720 | unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[TestDesc]) -> *ast::Node { |
|
| 718 | 721 | // Build array literal: `[testing::test(...), ...]`. |
| 760 | 763 | return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl { |
|
| 761 | 764 | name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 762 | 765 | })); |
|
| 763 | 766 | } |
|
| 764 | 767 | ||
| 765 | - | /// Append a declaration to a block node's statement list. |
|
| 768 | + | /// Build a block node with a declaration appended to its statement list. |
|
| 766 | 769 | unsafe fn injectIntoBlock( |
|
| 767 | - | blockNode: *mut ast::Node, |
|
| 770 | + | blockNode: *ast::Node, |
|
| 768 | 771 | arena: &mut ast::NodeArena, |
|
| 769 | 772 | decl: *ast::Node |
|
| 770 | - | ) { |
|
| 773 | + | ) -> *ast::Node { |
|
| 771 | 774 | let case ast::NodeValue::Block(block) = blockNode.value else { |
|
| 772 | 775 | panic "injectIntoBlock: expected Block node"; |
|
| 773 | 776 | }; |
|
| 774 | - | let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena)); |
|
| 775 | - | set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts, isUnsafe: block.isUnsafe }); |
|
| 777 | + | let allocator = alloc::arenaAllocator(&mut arena.arena); |
|
| 778 | + | let mut stmts = ast::nodeSlice(arena, block.statements.len + 1); |
|
| 779 | + | for stmt in block.statements { |
|
| 780 | + | stmts.append(stmt, allocator); |
|
| 781 | + | } |
|
| 782 | + | stmts.append(decl, allocator); |
|
| 783 | + | return ast::allocNode(arena, blockNode.span, ast::NodeValue::Block( |
|
| 784 | + | ast::Block { statements: stmts, isUnsafe: block.isUnsafe } |
|
| 785 | + | )); |
|
| 776 | 786 | } |
|
| 777 | 787 | ||
| 778 | 788 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 779 | 789 | unsafe fn writeImage( |
|
| 780 | 790 | code: *[u32], |
| 786 | 796 | let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32); |
|
| 787 | 797 | let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32); |
|
| 788 | 798 | ||
| 789 | 799 | let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
|
| 790 | 800 | if fd < 0 { return false; } |
|
| 791 | - | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes) |
|
| 801 | + | let written = unix::writeAll(fd, headerBytes) and unix::writeAll(fd, codeBytes) |
|
| 792 | 802 | and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData); |
|
| 793 | 803 | let closed = unix::close(fd) == 0; |
|
| 794 | 804 | return written and closed; |
|
| 795 | 805 | } |
|
| 796 | 806 |
| 847 | 857 | /// Run the resolver on the parsed modules. |
|
| 848 | 858 | unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 849 | 859 | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 850 | 860 | let entryPkg = try getEntryPackage(ctx); |
|
| 851 | 861 | ||
| 852 | - | pkgLog(&*entryPkg, &["resolving", ".."]); |
|
| 862 | + | pkgLog(&entryPkg, &["resolving", ".."]); |
|
| 853 | 863 | ||
| 854 | 864 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
|
| 855 | 865 | let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData)); |
|
| 856 | 866 | let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount); |
|
| 857 | 867 | let storage = resolver::ResolverStorage { |
| 865 | 875 | // Build the semantic package list consumed by the resolver. |
|
| 866 | 876 | let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined; |
|
| 867 | 877 | let mut resolverPackageCount: u32 = 0; |
|
| 868 | 878 | for i in 0..ctx.packageCount { |
|
| 869 | 879 | let pkg = &ctx.packages[i]; |
|
| 870 | - | let root = try getRootModule(&*pkg, &ctx.graph); |
|
| 880 | + | let root = try getRootModule(pkg, &ctx.graph); |
|
| 871 | 881 | ||
| 872 | 882 | set resolverPkgs[resolverPackageCount] = resolver::Pkg { |
|
| 873 | 883 | rootEntry: root.entry, |
|
| 874 | 884 | rootAst: root.ast, |
|
| 875 | 885 | }; |
| 877 | 887 | } |
|
| 878 | 888 | ||
| 879 | 889 | // Resolve all packages. |
|
| 880 | 890 | // TODO: Fix this error printing dance. |
|
| 881 | 891 | let diags = try resolver::resolve(&mut res, &ctx.graph, &resolverPkgs[..resolverPackageCount]) catch { |
|
| 882 | - | let diags = resolver::Diagnostics { errors: res.errors }; |
|
| 892 | + | let diags = resolver::diagnostics(&mut res); |
|
| 883 | 893 | resolver::printer::printDiagnostics(&diags, &res); |
|
| 884 | 894 | throw Error::Other; |
|
| 885 | 895 | }; |
|
| 886 | 896 | if not resolver::success(&diags) { |
|
| 887 | 897 | resolver::printer::printDiagnostics(&diags, &res); |
| 905 | 915 | pkg: &package::Package, |
|
| 906 | 916 | path: *[u8], |
|
| 907 | 917 | asmDataLen: &mut u32, |
|
| 908 | 918 | arena: &mut alloc::Arena |
|
| 909 | 919 | ) throws (Error) { |
|
| 910 | - | pkgLog(&*pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
|
| 920 | + | pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
|
| 911 | 921 | ||
| 912 | 922 | let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else { |
|
| 913 | 923 | throw error(&["error reading assembly file"]); |
|
| 914 | 924 | }; |
|
| 915 | 925 | let source = &ASM_SOURCE_BUF[..sourceLen]; |
| 966 | 976 | ) -> rv64::Program throws (Error) { |
|
| 967 | 977 | let entryIdx = ctx.entryPkgIdx else { |
|
| 968 | 978 | panic "lowerAndGenerateAllPackages: no entry package"; |
|
| 969 | 979 | }; |
|
| 970 | 980 | let entryPkg = &ctx.packages[entryIdx]; |
|
| 971 | - | let startupPath = getEntryStartupPath(&*ctx); |
|
| 981 | + | let startupPath = getEntryStartupPath(ctx); |
|
| 972 | 982 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
|
| 973 | 983 | let storage = rv64::Storage { |
|
| 974 | 984 | dataSyms: &mut CODEGEN_DATA_SYMS[..], |
|
| 975 | 985 | dataSymEntries: &mut CODEGEN_DATA_SYM_ENTRIES[..], |
|
| 976 | 986 | }; |
| 985 | 995 | rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug }, |
|
| 986 | 996 | &mut res.arena |
|
| 987 | 997 | ); |
|
| 988 | 998 | let mut codegenCtx = codegen::Context { |
|
| 989 | 999 | generator: &mut generator, |
|
| 990 | - | fnArena: fnArena as *unsafe mut alloc::Arena, |
|
| 1000 | + | fnArena: (&mut *fnArena) as *unsafe mut alloc::Arena, |
|
| 991 | 1001 | }; |
|
| 992 | 1002 | let mut low = lower::lowerer( |
|
| 993 | 1003 | res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options |
|
| 994 | 1004 | ); |
|
| 995 | 1005 | set low.output = lower::FnOutput::Stream(lower::FnSink { |
|
| 996 | 1006 | ctx: &mut codegenCtx as *unsafe mut opaque, |
|
| 997 | 1007 | emitFn: codegen::emit, |
|
| 998 | 1008 | }); |
|
| 999 | 1009 | let mut asmDataLen: u32 = 0; |
|
| 1000 | 1010 | if let path = startupPath { |
|
| 1001 | - | try assembleAsmModule(&mut generator, &*entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1011 | + | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1002 | 1012 | } |
|
| 1003 | 1013 | try lowerAllPackagesInto(ctx, res, &mut low); |
|
| 1004 | - | let asmData = try assembleAsmInputs(&*ctx, &mut generator, &mut asmDataLen, &mut res.arena); |
|
| 1014 | + | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena); |
|
| 1005 | 1015 | ||
| 1006 | 1016 | match generator.entryPatch { |
|
| 1007 | 1017 | case rv64::EntryPatch::Reserved(targetName) => { |
|
| 1008 | 1018 | if targetName == nil { |
|
| 1009 | 1019 | throw error(&["fatal:", "no default function found"]); |
|
| 1010 | 1020 | } |
|
| 1011 | 1021 | } |
|
| 1012 | 1022 | else => {} |
|
| 1013 | 1023 | } |
|
| 1014 | 1024 | if let path = codegenOptions.logPath { |
|
| 1015 | - | pkgLog(&*entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1025 | + | pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1016 | 1026 | } |
|
| 1017 | - | return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]); |
|
| 1027 | + | return rv64::finishProgram(&mut generator, low.data, storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]); |
|
| 1018 | 1028 | } |
|
| 1019 | 1029 | ||
| 1020 | 1030 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1021 | 1031 | unsafe fn compile( |
|
| 1022 | 1032 | ctx: *unsafe mut CompileContext, |
| 1047 | 1057 | // Generate binary output if path specified. |
|
| 1048 | 1058 | let outPath = ctx.outputPath else { |
|
| 1049 | 1059 | try lowerAllPackages(ctx, res); |
|
| 1050 | 1060 | return; |
|
| 1051 | 1061 | }; |
|
| 1052 | - | let startupPath = getEntryStartupPath(&*ctx); |
|
| 1062 | + | let startupPath = getEntryStartupPath(ctx); |
|
| 1053 | 1063 | let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions { |
|
| 1054 | 1064 | logPath: outPath, |
|
| 1055 | 1065 | debug: ctx.debug, |
|
| 1056 | 1066 | entryMode: CodegenEntryMode::None |
|
| 1057 | 1067 | if startupPath <> nil |
| 1069 | 1079 | ||
| 1070 | 1080 | // Write debug info file if enabled. |
|
| 1071 | 1081 | if ctx.debug { |
|
| 1072 | 1082 | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena); |
|
| 1073 | 1083 | } |
|
| 1074 | - | pkgLog(&*entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1084 | + | pkgLog(&entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1075 | 1085 | } |
|
| 1076 | 1086 | ||
| 1077 | 1087 | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 1078 | 1088 | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
|
| 1079 | 1089 | let mut ctx = try processCommand(env.args, &mut arena) catch { |
compiler/radiance/codegen.rad
+4 -4
| 13 | 13 | fnArena: *unsafe mut alloc::Arena, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Emit one lowered function to machine code and reclaim its IL arena. |
|
| 17 | 17 | /// The context must point to a valid `Context` with live generator and arena storage. |
|
| 18 | - | export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *il::Fn, role: lower::FnRole) { |
|
| 18 | + | export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *unsafe il::Fn, role: lower::FnRole) { |
|
| 19 | 19 | let ctx = ctxPtr as *unsafe mut Context; |
|
| 20 | 20 | ||
| 21 | 21 | match role { |
|
| 22 | 22 | case lower::FnRole::Default => { |
|
| 23 | - | rv64::recordFunctionAlias(&mut *ctx.generator, super::DEFAULT_ENTRY_SYMBOL); |
|
| 23 | + | rv64::recordFunctionAlias(ctx.generator, super::DEFAULT_ENTRY_SYMBOL); |
|
| 24 | 24 | match ctx.generator.entryPatch { |
|
| 25 | 25 | case rv64::EntryPatch::Reserved(_) => { |
|
| 26 | 26 | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 27 | 27 | } |
|
| 28 | 28 | // Startup assembly calls the default function through its |
| 32 | 32 | } |
|
| 33 | 33 | else => {} |
|
| 34 | 34 | } |
|
| 35 | 35 | let generator = ctx.generator; |
|
| 36 | 36 | let arena = ctx.fnArena; |
|
| 37 | - | rv64::generateFunction(&mut *generator, func, &mut *arena); |
|
| 38 | - | alloc::reset(&mut *ctx.fnArena); |
|
| 37 | + | rv64::generateFunction(generator, func, arena); |
|
| 38 | + | alloc::reset(ctx.fnArena); |
|
| 39 | 39 | } |
lib/std/arch/rv64.rad
+13 -11
| 159 | 159 | export fn imageHeader(codeBytes: u32, roDataBytes: u32, rwDataBytes: u32) -> [u32; 5] { |
|
| 160 | 160 | return [IMAGE_MAGIC, IMAGE_VERSION, codeBytes, roDataBytes, rwDataBytes]; |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | 163 | /// Storage buffers passed from driver for code generation. |
|
| 164 | - | export record Storage: Copy { |
|
| 164 | + | export record Storage { |
|
| 165 | 165 | /// Buffer for data symbols. |
|
| 166 | 166 | dataSyms: *mut [data::DataSym], |
|
| 167 | 167 | /// Hash table entries for data symbol lookup. |
|
| 168 | 168 | dataSymEntries: *mut [dict::Entry], |
|
| 169 | 169 | } |
| 201 | 201 | /// State for incremental RV64 program generation. |
|
| 202 | 202 | /// |
|
| 203 | 203 | /// The generator owns global codegen state that must survive across function |
|
| 204 | 204 | /// emission. Function-local scratch stays outside this record so callers can |
|
| 205 | 205 | /// reclaim it after each function. |
|
| 206 | - | export record Generator: Copy { |
|
| 206 | + | export record Generator { |
|
| 207 | 207 | /// Binary emitter and relocation state. |
|
| 208 | 208 | e: emit::Emitter, |
|
| 209 | 209 | /// Entry jump patching state. |
|
| 210 | 210 | entryPatch: EntryPatch, |
|
| 211 | 211 | } |
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | /// Generate code for one IL function. |
|
| 237 | 237 | export unsafe fn generateFunction( |
|
| 238 | 238 | generator: &mut Generator, |
|
| 239 | - | func: *il::Fn, |
|
| 239 | + | func: *unsafe il::Fn, |
|
| 240 | 240 | arena: &mut alloc::Arena |
|
| 241 | 241 | ) { |
|
| 242 | 242 | if func.isExtern { |
|
| 243 | 243 | return; |
|
| 244 | 244 | } |
| 299 | 299 | } |
|
| 300 | 300 | ||
| 301 | 301 | /// Finish RV64 code generation and return the emitted program. |
|
| 302 | 302 | export unsafe fn finishProgram( |
|
| 303 | 303 | generator: &mut Generator, |
|
| 304 | - | globalData: *[il::Data], |
|
| 304 | + | globalData: &[il::Data], |
|
| 305 | 305 | storage: Storage, |
|
| 306 | 306 | roDataPrefix: *[u8], |
|
| 307 | - | roDataBuf: *mut [u8], |
|
| 308 | - | rwDataBuf: *mut [u8] |
|
| 307 | + | roDataBuf: &mut [u8], |
|
| 308 | + | rwDataBuf: &mut [u8] |
|
| 309 | 309 | ) -> Program { |
|
| 310 | 310 | // Build data map after function lowering. Function-local literals can add |
|
| 311 | 311 | // global data while functions are lowered, so final layout belongs here. |
|
| 312 | + | let case Storage { dataSyms: symbolBuf, dataSymEntries } = storage |
|
| 313 | + | else panic "expected code generation storage"; |
|
| 312 | 314 | let mut dataSymCount: u32 = 0; |
|
| 313 | 315 | let roLayoutSize = data::layoutSectionAtOffset( |
|
| 314 | - | globalData, storage.dataSyms, &mut dataSymCount, RO_DATA_BASE, roDataPrefix.len, true |
|
| 316 | + | globalData, symbolBuf, &mut dataSymCount, RO_DATA_BASE, roDataPrefix.len, true |
|
| 315 | 317 | ); |
|
| 316 | - | data::layoutSection(globalData, storage.dataSyms, &mut dataSymCount, RW_DATA_BASE, false); |
|
| 318 | + | data::layoutSection(globalData, symbolBuf, &mut dataSymCount, RW_DATA_BASE, false); |
|
| 317 | 319 | ||
| 318 | - | let dataSyms = &storage.dataSyms[..dataSymCount]; |
|
| 319 | - | let dataSymMap = data::buildMap(dataSyms, storage.dataSymEntries); |
|
| 320 | + | let dataSyms = &symbolBuf[..dataSymCount]; |
|
| 321 | + | let dataSymMap = data::buildMap(dataSyms, dataSymEntries); |
|
| 320 | 322 | let codeBase = mem::alignUp(RO_DATA_BASE + roLayoutSize, DWORD_SIZE as u32); |
|
| 321 | 323 | ||
| 322 | 324 | match generator.entryPatch { |
|
| 323 | 325 | case EntryPatch::Reserved(targetName) => { |
|
| 324 | 326 | let target = targetName else { |
| 347 | 349 | let rwDataSize = data::emitSection( |
|
| 348 | 350 | globalData, &dataSymMap, &generator.e.labels, codeBase, rwDataBuf, false |
|
| 349 | 351 | ); |
|
| 350 | 352 | return Program { |
|
| 351 | 353 | code: emit::getCode(&generator.e), |
|
| 352 | - | funcs: generator.e.funcs, |
|
| 354 | + | funcs: &generator.e.funcs[..], |
|
| 353 | 355 | roDataSize, |
|
| 354 | 356 | rwDataSize, |
|
| 355 | 357 | debugEntries: emit::getDebugEntries(&generator.e), |
|
| 356 | 358 | }; |
|
| 357 | 359 | } |
lib/std/arch/rv64/asm.rad
+13 -13
| 450 | 450 | /// Fixup payload. |
|
| 451 | 451 | info: FixupInfo, |
|
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Parser and emission state. |
|
| 455 | - | export record Assembler: Copy { |
|
| 455 | + | export record Assembler { |
|
| 456 | 456 | /// Allocation arena for temporary assembler state. |
|
| 457 | 457 | arena: *unsafe mut alloc::Arena, |
|
| 458 | 458 | /// Assembler lexical scanner. |
|
| 459 | 459 | scan: scanner::Scanner, |
|
| 460 | 460 | /// Output text buffer. |
| 484 | 484 | sourceKind: scanner::SourceKind, |
|
| 485 | 485 | source: *[u8], |
|
| 486 | 486 | textBuf: *mut [u32], |
|
| 487 | 487 | dataBuf: *mut [u8], |
|
| 488 | 488 | arena: &mut alloc::Arena, |
|
| 489 | - | pool: *mut strings::Pool, |
|
| 489 | + | pool: *unsafe mut strings::Pool, |
|
| 490 | 490 | dataBase: u32 |
|
| 491 | 491 | ) -> Program throws (Error) { |
|
| 492 | 492 | let slotCap = source.len + SOURCE_CAP_PADDING; |
|
| 493 | 493 | let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE); |
|
| 494 | 494 |
| 497 | 497 | let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap); |
|
| 498 | 498 | let entries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 499 | 499 | let constEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 500 | 500 | let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 501 | 501 | ||
| 502 | + | let symbolBuf = symbols as *mut [Symbol]; |
|
| 503 | + | let fixupBuf = fixups as *mut [Fixup]; |
|
| 504 | + | let externalBuf = externalFixups as *mut [Fixup]; |
|
| 502 | 505 | let mut a = Assembler { |
|
| 503 | 506 | arena: arena as *unsafe mut alloc::Arena, |
|
| 504 | 507 | scan: scanner::scanner(sourceKind, source, pool), |
|
| 505 | - | text: @sliceOf(textBuf.ptr, 0, textBuf.len), |
|
| 506 | - | data: @sliceOf(dataBuf.ptr, 0, dataBuf.len), |
|
| 508 | + | text: &mut textBuf[..0], |
|
| 509 | + | data: &mut dataBuf[..0], |
|
| 507 | 510 | section: Section::Text, |
|
| 508 | - | symbols: @sliceOf((symbols as *mut [Symbol]).ptr, 0, (symbols as *mut [Symbol]).len), |
|
| 511 | + | symbols: &mut symbolBuf[..0], |
|
| 509 | 512 | symbolMap: dict::init(entries as *mut [dict::Entry]), |
|
| 510 | 513 | constMap: dict::init(constEntries as *mut [dict::Entry]), |
|
| 511 | 514 | exportMap: dict::init(exportEntries as *mut [dict::Entry]), |
|
| 512 | - | fixups: @sliceOf((fixups as *mut [Fixup]).ptr, 0, (fixups as *mut [Fixup]).len), |
|
| 513 | - | externalFixups: @sliceOf((externalFixups as *mut [Fixup]).ptr, 0, (externalFixups as *mut [Fixup]).len), |
|
| 515 | + | fixups: &mut fixupBuf[..0], |
|
| 516 | + | externalFixups: &mut externalBuf[..0], |
|
| 514 | 517 | dataBase, |
|
| 515 | 518 | }; |
|
| 516 | 519 | // Parse assembly source and emit instructions. |
|
| 517 | 520 | try parser::parseProgram(&mut a); |
|
| 518 | 521 | // Resolve fixups and finalize program. |
|
| 519 | 522 | try emit::finishProgram(&mut a); |
|
| 520 | 523 | ||
| 521 | - | return Program { |
|
| 522 | - | text: a.text, |
|
| 523 | - | data: a.data, |
|
| 524 | - | symbols: a.symbols, |
|
| 525 | - | externalFixups: a.externalFixups, |
|
| 526 | - | }; |
|
| 524 | + | let case Assembler { text, data, symbols: definedSymbols, externalFixups: pendingFixups, .. } = a |
|
| 525 | + | else panic "expected assembler state"; |
|
| 526 | + | return Program { text, data, symbols: definedSymbols, externalFixups: pendingFixups }; |
|
| 527 | 527 | } |
|
| 528 | 528 | ||
| 529 | 529 | /// Return the next power of two at least as large as `value`. |
|
| 530 | 530 | fn nextPowerOfTwo(value: u32) -> u32 { |
|
| 531 | 531 | let mut n: u32 = MIN_TABLE_CAPACITY; |
lib/std/arch/rv64/asm/emit.rad
+5 -5
| 18 | 18 | a.symbols.append(super::Symbol { |
|
| 19 | 19 | name, |
|
| 20 | 20 | section: a.section, |
|
| 21 | 21 | offset, |
|
| 22 | 22 | isExported: dict::get(&a.exportMap, name) <> nil, |
|
| 23 | - | }, alloc::arenaAllocator(&mut *a.arena)); |
|
| 23 | + | }, alloc::arenaAllocator(a.arena)); |
|
| 24 | 24 | dict::insert(&mut a.symbolMap, name, idx as i32); |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Append one encoded instruction word to the text section. |
|
| 28 | 28 | export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) { |
|
| 29 | - | a.text.append(word, alloc::arenaAllocator(&mut *a.arena)); |
|
| 29 | + | a.text.append(word, alloc::arenaAllocator(a.arena)); |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// Append `words` no-op instructions to the text section. |
|
| 33 | 33 | export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) { |
|
| 34 | 34 | for _ in 0..words { |
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Append one byte to the data section. |
|
| 40 | 40 | export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) { |
|
| 41 | - | a.data.append(byte, alloc::arenaAllocator(&mut *a.arena)); |
|
| 41 | + | a.data.append(byte, alloc::arenaAllocator(a.arena)); |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Emit a little-endian integer with `bytes` bytes. |
|
| 45 | 45 | unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) { |
|
| 46 | 46 | for i in 0..bytes { |
| 78 | 78 | } |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Record a pending symbol fixup. |
|
| 82 | 82 | unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) { |
|
| 83 | - | a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(&mut *a.arena)); |
|
| 83 | + | a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena)); |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Record a text fixup that must be resolved after all program text is known. |
|
| 87 | 87 | unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) { |
|
| 88 | - | a.externalFixups.append(fixup, alloc::arenaAllocator(&mut *a.arena)); |
|
| 88 | + | a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena)); |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | 91 | /// Record a text-section symbol fixup and reserve its instruction words. |
|
| 92 | 92 | export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) { |
|
| 93 | 93 | recordFixup(a, symbol, info); |
lib/std/arch/rv64/asm/parser.rad
+25 -24
| 37 | 37 | } |
|
| 38 | 38 | return mem::alignUp(value, alignment); |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Advance the parser by one token, preserving the previous token. |
|
| 42 | - | fn advance(a: &mut super::Assembler) { |
|
| 42 | + | unsafe fn advance(a: &mut super::Assembler) { |
|
| 43 | 43 | set a.scan.previous = a.scan.current; |
|
| 44 | 44 | set a.scan.current = scanner::next(&mut a.scan); |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | 47 | /// Consume the current token when it has `kind`. |
|
| 48 | - | fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool { |
|
| 48 | + | unsafe fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool { |
|
| 49 | 49 | if a.scan.current.kind == kind { |
|
| 50 | 50 | advance(a); |
|
| 51 | 51 | return true; |
|
| 52 | 52 | } |
|
| 53 | 53 | return false; |
| 69 | 69 | throw failOnToken(tok, "data directive is only valid in the data section"); |
|
| 70 | 70 | } |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | 73 | /// Consume `kind` or throw `message` at the current token. |
|
| 74 | - | fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) { |
|
| 74 | + | unsafe fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) { |
|
| 75 | 75 | if not consume(a, kind) { |
|
| 76 | 76 | throw fail(a, message); |
|
| 77 | 77 | } |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Consume `kind` and return the consumed token. |
|
| 81 | - | fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) { |
|
| 81 | + | unsafe fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) { |
|
| 82 | 82 | try expect(a, kind, message); |
|
| 83 | 83 | return a.scan.previous; |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Require that the current item has reached its semicolon terminator. |
| 123 | 123 | try emit::emitDataValue(a, value, width), |
|
| 124 | 124 | } |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | 127 | /// Parse a possibly scoped name from one or more `::`-separated segments. |
|
| 128 | - | fn parseScopedName( |
|
| 128 | + | unsafe fn parseScopedName( |
|
| 129 | 129 | a: &mut super::Assembler, |
|
| 130 | 130 | kind: scanner::TokenKind, |
|
| 131 | 131 | message: *[u8], |
|
| 132 | 132 | trimPrefix: u32 |
|
| 133 | 133 | ) -> *[u8] throws (super::Error) { |
| 137 | 137 | ||
| 138 | 138 | while consume(a, scanner::TokenKind::ColonColon) { |
|
| 139 | 139 | let segment = try expectToken(a, scanner::TokenKind::Ident, "expected identifier after `::`"); |
|
| 140 | 140 | set end = segment.offset + segment.source.len; |
|
| 141 | 141 | } |
|
| 142 | - | return strings::intern(a.scan.pool, &a.scan.source[start..end]); |
|
| 142 | + | let source = &a.scan.source[start..end]; |
|
| 143 | + | return strings::intern(a.scan.pool, source); |
|
| 143 | 144 | } |
|
| 144 | 145 | ||
| 145 | 146 | /// Parse a bare symbol name. |
|
| 146 | - | fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 147 | + | unsafe fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 147 | 148 | return try parseScopedName(a, scanner::TokenKind::Ident, "expected symbol name", 0); |
|
| 148 | 149 | } |
|
| 149 | 150 | ||
| 150 | 151 | /// Return `true` when [`tok`] is any label token form. |
|
| 151 | 152 | fn isLabel(tok: scanner::TokenKind) -> bool { |
| 155 | 156 | /// Parse the contents of a quoted label token, decoding escapes as needed. |
|
| 156 | 157 | unsafe fn parseQuotedLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 157 | 158 | let tok = try expectToken(a, scanner::TokenKind::QuotedLabel, "expected label name"); |
|
| 158 | 159 | let rawStart = super::LABEL_SIGIL_LEN + super::QUOTE_DELIM_LEN; |
|
| 159 | 160 | let raw = &tok.source[rawStart..tok.source.len - super::QUOTE_DELIM_LEN]; |
|
| 160 | - | let storage = try alloc::allocSlice(&mut *a.arena, 1, 1, raw.len) catch { |
|
| 161 | + | let storage = try alloc::allocSlice(a.arena, 1, 1, raw.len) catch { |
|
| 161 | 162 | panic "asm: out of memory allocating quoted label"; |
|
| 162 | 163 | } as *mut [u8]; |
|
| 163 | 164 | let len = fmt::unescapeString(raw, storage); |
|
| 164 | 165 | ||
| 165 | 166 | return strings::intern(a.scan.pool, &storage[..len]); |
| 172 | 173 | } |
|
| 173 | 174 | return try parseScopedName(a, scanner::TokenKind::Label, "expected label name", super::LABEL_SIGIL_LEN); |
|
| 174 | 175 | } |
|
| 175 | 176 | ||
| 176 | 177 | /// Parse a directive name without its leading `.`. |
|
| 177 | - | fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 178 | + | unsafe fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 178 | 179 | let name = try expectToken(a, scanner::TokenKind::Directive, "expected directive name"); |
|
| 179 | 180 | return &name.source[super::DIRECTIVE_SIGIL_LEN..]; |
|
| 180 | 181 | } |
|
| 181 | 182 | ||
| 182 | 183 | /// Parse one top-level assembler item. |
| 544 | 545 | } |
|
| 545 | 546 | } |
|
| 546 | 547 | } |
|
| 547 | 548 | ||
| 548 | 549 | /// Parse a `.constant` directive. |
|
| 549 | - | fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 550 | + | unsafe fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 550 | 551 | let name = try parseSymbolName(a); |
|
| 551 | 552 | let value = try expectI32Value(a, try parseExpr(a), "constant out of range"); |
|
| 552 | 553 | ||
| 553 | 554 | dict::insert(&mut a.constMap, name, value); |
|
| 554 | 555 | } |
| 667 | 668 | } |
|
| 668 | 669 | } |
|
| 669 | 670 | } |
|
| 670 | 671 | ||
| 671 | 672 | /// Parse and resolve a register operand. |
|
| 672 | - | fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) { |
|
| 673 | + | unsafe fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) { |
|
| 673 | 674 | let tok = try expectToken(a, scanner::TokenKind::Register, "expected register"); |
|
| 674 | 675 | let reg = lookupRegister(&tok.source[1..]) else { |
|
| 675 | 676 | throw super::Error::Invalid { offset: tok.offset, message: "unknown register" }; |
|
| 676 | 677 | }; |
|
| 677 | 678 | return reg; |
|
| 678 | 679 | } |
|
| 679 | 680 | ||
| 680 | 681 | /// Parse a simple signed immediate or constant value. |
|
| 681 | - | fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 682 | + | unsafe fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 682 | 683 | if consume(a, scanner::TokenKind::Minus) { |
|
| 683 | 684 | return -(try parseValuePrimary(a)); |
|
| 684 | 685 | } |
|
| 685 | 686 | return try parseValuePrimary(a); |
|
| 686 | 687 | } |
|
| 687 | 688 | ||
| 688 | 689 | /// Parse the primary form used by simple immediate values. |
|
| 689 | - | fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 690 | + | unsafe fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 690 | 691 | if a.scan.current.kind == scanner::TokenKind::Number { |
|
| 691 | 692 | return try parseInteger(a); |
|
| 692 | 693 | } |
|
| 693 | 694 | if a.scan.current.kind == scanner::TokenKind::Ident { |
|
| 694 | 695 | return try parseConstantValue(a); |
|
| 695 | 696 | } |
|
| 696 | 697 | throw fail(a, "expected number or constant"); |
|
| 697 | 698 | } |
|
| 698 | 699 | ||
| 699 | 700 | /// Parse an additive constant expression. |
|
| 700 | - | fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 701 | + | unsafe fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 701 | 702 | let mut value = try parseExprMul(a); |
|
| 702 | 703 | ||
| 703 | 704 | while a.scan.current.kind == scanner::TokenKind::Plus or a.scan.current.kind == scanner::TokenKind::Minus { |
|
| 704 | 705 | let op = a.scan.current.kind; |
|
| 705 | 706 | advance(a); |
| 713 | 714 | } |
|
| 714 | 715 | return value; |
|
| 715 | 716 | } |
|
| 716 | 717 | ||
| 717 | 718 | /// Parse multiplicative expression operators. |
|
| 718 | - | fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 719 | + | unsafe fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 719 | 720 | let mut value = try parseExprUnary(a); |
|
| 720 | 721 | ||
| 721 | 722 | while a.scan.current.kind == scanner::TokenKind::Star or a.scan.current.kind == scanner::TokenKind::Slash { |
|
| 722 | 723 | let op = a.scan.current.kind; |
|
| 723 | 724 | advance(a); |
| 734 | 735 | } |
|
| 735 | 736 | return value; |
|
| 736 | 737 | } |
|
| 737 | 738 | ||
| 738 | 739 | /// Parse unary expression operators. |
|
| 739 | - | fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 740 | + | unsafe fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 740 | 741 | if consume(a, scanner::TokenKind::Minus) { |
|
| 741 | 742 | return -(try parseExprUnary(a)); |
|
| 742 | 743 | } |
|
| 743 | 744 | if consume(a, scanner::TokenKind::Plus) { |
|
| 744 | 745 | return try parseExprUnary(a); |
|
| 745 | 746 | } |
|
| 746 | 747 | return try parseExprPrimary(a); |
|
| 747 | 748 | } |
|
| 748 | 749 | ||
| 749 | 750 | /// Parse expression atoms. |
|
| 750 | - | fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 751 | + | unsafe fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 751 | 752 | if consume(a, scanner::TokenKind::LParen) { |
|
| 752 | 753 | let value = try parseExpr(a); |
|
| 753 | 754 | try expect(a, scanner::TokenKind::RParen, "expected `)`"); |
|
| 754 | 755 | return value; |
|
| 755 | 756 | } |
| 761 | 762 | } |
|
| 762 | 763 | throw fail(a, "expected expression"); |
|
| 763 | 764 | } |
|
| 764 | 765 | ||
| 765 | 766 | /// Parse and resolve a named assembler constant. |
|
| 766 | - | fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 767 | + | unsafe fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 767 | 768 | let name = try parseSymbolName(a); |
|
| 768 | 769 | let value = dict::get(&a.constMap, name) else { |
|
| 769 | 770 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "undefined constant" }; |
|
| 770 | 771 | }; |
|
| 771 | 772 | return value as i64; |
|
| 772 | 773 | } |
|
| 773 | 774 | ||
| 774 | 775 | /// Parse and resolve a CSR operand. |
|
| 775 | - | fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) { |
|
| 776 | + | unsafe fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) { |
|
| 776 | 777 | let name = try parseSymbolName(a); |
|
| 777 | 778 | let csr = lookupCsr(name) else { |
|
| 778 | 779 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "unknown CSR" }; |
|
| 779 | 780 | }; |
|
| 780 | 781 | return csr; |
|
| 781 | 782 | } |
|
| 782 | 783 | ||
| 783 | 784 | /// Parse an offset(base) memory operand. |
|
| 784 | - | fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) { |
|
| 785 | + | unsafe fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) { |
|
| 785 | 786 | let mut offset: i32 = 0; |
|
| 786 | 787 | if a.scan.current.kind <> scanner::TokenKind::LParen { |
|
| 787 | 788 | set offset = try expectSmallImmValue(a, try parseValue(a)); |
|
| 788 | 789 | } |
|
| 789 | 790 | try expect(a, scanner::TokenKind::LParen, "expected `(`"); |
| 792 | 793 | ||
| 793 | 794 | return MemOperand { base, offset }; |
|
| 794 | 795 | } |
|
| 795 | 796 | ||
| 796 | 797 | /// Parse an immediate value that fits in a signed 12-bit field. |
|
| 797 | - | fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 798 | + | unsafe fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 798 | 799 | return try expectSmallImmValue(a, try parseValue(a)); |
|
| 799 | 800 | } |
|
| 800 | 801 | ||
| 801 | 802 | /// Parse and validate a branch immediate. |
|
| 802 | - | fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 803 | + | unsafe fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 803 | 804 | let value = try expectI32Value(a, try parseValue(a), "branch immediate out of range"); |
|
| 804 | 805 | if not encode::isBranchImm(value) { |
|
| 805 | 806 | throw fail(a, "branch immediate out of range"); |
|
| 806 | 807 | } |
|
| 807 | 808 | return value; |
|
| 808 | 809 | } |
|
| 809 | 810 | ||
| 810 | 811 | /// Parse and validate a jump immediate. |
|
| 811 | - | fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 812 | + | unsafe fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 812 | 813 | let value = try expectI32Value(a, try parseValue(a), "jump immediate out of range"); |
|
| 813 | 814 | if not encode::isJumpImm(value) { |
|
| 814 | 815 | throw fail(a, "jump immediate out of range"); |
|
| 815 | 816 | } |
|
| 816 | 817 | return value; |
|
| 817 | 818 | } |
|
| 818 | 819 | ||
| 819 | 820 | /// Parse an integer token as an i64. |
|
| 820 | - | fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 821 | + | unsafe fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 821 | 822 | let tok = try expectToken(a, scanner::TokenKind::Number, "expected number"); |
|
| 822 | 823 | let value = parseIntegerText(tok.source) else { |
|
| 823 | 824 | throw failOnToken(tok, "invalid integer literal"); |
|
| 824 | 825 | }; |
|
| 825 | 826 | return value; |
lib/std/arch/rv64/asm/scanner.rad
+12 -10
| 61 | 61 | cursor: u32, |
|
| 62 | 62 | /// Current token observed by the parser. |
|
| 63 | 63 | current: Token, |
|
| 64 | 64 | /// Previously consumed token observed by the parser. |
|
| 65 | 65 | previous: Token, |
|
| 66 | - | /// Intern pool for identifier-shaped token text. |
|
| 67 | - | pool: *mut strings::Pool, |
|
| 66 | + | /// Intern pool for identifier-shaped token text. It must remain valid while scanning. |
|
| 67 | + | pool: *unsafe mut strings::Pool, |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Individual token with kind, source text, and byte offset. |
|
| 71 | 71 | export record Token: Copy { |
|
| 72 | 72 | /// Token kind. |
| 76 | 76 | /// Byte offset of `source` in the input buffer. |
|
| 77 | 77 | offset: u32, |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Create a new assembler scanner. |
|
| 81 | - | export fn scanner(sourceKind: SourceKind, source: *[u8], pool: *mut strings::Pool) -> Scanner { |
|
| 81 | + | export fn scanner(sourceKind: SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> Scanner { |
|
| 82 | 82 | let invalidToken = invalid(0, ""); |
|
| 83 | 83 | return Scanner { |
|
| 84 | 84 | sourceKind, |
|
| 85 | 85 | source, |
|
| 86 | 86 | token: 0, |
| 149 | 149 | else => return, |
|
| 150 | 150 | } |
|
| 151 | 151 | } |
|
| 152 | 152 | } |
|
| 153 | 153 | ||
| 154 | - | /// Return the next assembler token. |
|
| 155 | - | export fn next(s: &mut Scanner) -> Token { |
|
| 154 | + | /// Return the next assembler token. The retained pool must be valid and writable. |
|
| 155 | + | export unsafe fn next(s: &mut Scanner) -> Token { |
|
| 156 | 156 | skipWhitespace(s); |
|
| 157 | 157 | set s.token = s.cursor; |
|
| 158 | 158 | ||
| 159 | 159 | if isEof(s) { |
|
| 160 | 160 | return tok(s, TokenKind::Eof); |
| 270 | 270 | } |
|
| 271 | 271 | return invalid(s.token, "unterminated character"); |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Scan an identifier-shaped token of the given kind. |
|
| 275 | - | fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token { |
|
| 275 | + | unsafe fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token { |
|
| 276 | 276 | scanIdentifierBody(s); |
|
| 277 | + | let source = &s.source[s.token..s.cursor]; |
|
| 277 | 278 | ||
| 278 | 279 | return Token { |
|
| 279 | 280 | kind, |
|
| 280 | - | source: strings::intern(s.pool, &s.source[s.token..s.cursor]), |
|
| 281 | + | source: strings::intern(s.pool, source), |
|
| 281 | 282 | offset: s.token, |
|
| 282 | 283 | }; |
|
| 283 | 284 | } |
|
| 284 | 285 | ||
| 285 | 286 | /// Scan a sigil-prefixed identifier-shaped token. |
|
| 286 | - | fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token { |
|
| 287 | + | unsafe fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token { |
|
| 287 | 288 | let ch = current(s) else { |
|
| 288 | 289 | return invalid(s.token, message); |
|
| 289 | 290 | }; |
|
| 290 | 291 | if not char::isAlpha(ch) and ch <> '_' { |
|
| 291 | 292 | return invalid(s.token, message); |
|
| 292 | 293 | } |
|
| 293 | 294 | scanIdentifierBody(s); |
|
| 295 | + | let source = &s.source[s.token..s.cursor]; |
|
| 294 | 296 | ||
| 295 | 297 | return Token { |
|
| 296 | 298 | kind, |
|
| 297 | - | source: strings::intern(s.pool, &s.source[s.token..s.cursor]), |
|
| 299 | + | source: strings::intern(s.pool, source), |
|
| 298 | 300 | offset: s.token, |
|
| 299 | 301 | }; |
|
| 300 | 302 | } |
|
| 301 | 303 | ||
| 302 | 304 | /// Scan an assembler label token, accepting either `@name` or `@"quoted"` syntax. |
|
| 303 | - | fn scanLabelToken(s: &mut Scanner) -> Token { |
|
| 305 | + | unsafe fn scanLabelToken(s: &mut Scanner) -> Token { |
|
| 304 | 306 | let ch = current(s) else { |
|
| 305 | 307 | return invalid(s.token, "expected label after `@`"); |
|
| 306 | 308 | }; |
|
| 307 | 309 | if ch == '"' { |
|
| 308 | 310 | advance(s); |
lib/std/arch/rv64/asm/scanner/tests.rad
+7 -7
| 11 | 11 | return super::scanner(super::SourceKind::String, source, &mut TEST_STRING_POOL); |
|
| 12 | 12 | } |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Scanner recognizes assembler-specific sigils and scoped names. |
|
| 16 | - | @test fn testScanRegisterDirectiveAndLabelTokens() throws (testing::TestError) { |
|
| 16 | + | @test unsafe fn testScanRegisterDirectiveAndLabelTokens() throws (testing::TestError) { |
|
| 17 | 17 | let mut s = testScanner( |
|
| 18 | 18 | ".text %sp @entry name::tail 42" |
|
| 19 | 19 | ); |
|
| 20 | 20 | let directive = super::next(&mut s); |
|
| 21 | 21 | try testing::expect(directive.kind == super::TokenKind::Directive); |
| 35 | 35 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Number); |
|
| 36 | 36 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Keyword-shaped text remains plain assembler identifiers. |
|
| 40 | - | @test fn testScanKeywordShapedAsmNamesRemainAsmTokens() throws (testing::TestError) { |
|
| 40 | + | @test unsafe fn testScanKeywordShapedAsmNamesRemainAsmTokens() throws (testing::TestError) { |
|
| 41 | 41 | let mut s = testScanner( |
|
| 42 | 42 | "and or not align addi .text @label" |
|
| 43 | 43 | ); |
|
| 44 | 44 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 45 | 45 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
| 50 | 50 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Label); |
|
| 51 | 51 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Quoted labels can spell symbol names that are not identifier-shaped. |
|
| 55 | - | @test fn testScanQuotedLabelToken() throws (testing::TestError) { |
|
| 55 | + | @test unsafe fn testScanQuotedLabelToken() throws (testing::TestError) { |
|
| 56 | 56 | let mut s = testScanner( |
|
| 57 | 57 | "@\"foo.bar.baz\"" |
|
| 58 | 58 | ); |
|
| 59 | 59 | let label = super::next(&mut s); |
|
| 60 | 60 | try testing::expect(label.kind == super::TokenKind::QuotedLabel); |
|
| 61 | 61 | try testing::expect(mem::eq(label.source, "@\"foo.bar.baz\"")); |
|
| 62 | 62 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 63 | 63 | } |
|
| 64 | 64 | ||
| 65 | 65 | /// Sigil-prefixed tokens require the name to start immediately after the sigil. |
|
| 66 | - | @test fn testScanSigilsRequireAdjacency() throws (testing::TestError) { |
|
| 66 | + | @test unsafe fn testScanSigilsRequireAdjacency() throws (testing::TestError) { |
|
| 67 | 67 | let mut regScan = testScanner("% a0"); |
|
| 68 | 68 | try testing::expect(super::next(&mut regScan).kind == super::TokenKind::Invalid); |
|
| 69 | 69 | ||
| 70 | 70 | let mut labelScan = testScanner("@ entry"); |
|
| 71 | 71 | try testing::expect(super::next(&mut labelScan).kind == super::TokenKind::Invalid); |
| 73 | 73 | let mut directiveScan = testScanner(". text"); |
|
| 74 | 74 | try testing::expect(super::next(&mut directiveScan).kind == super::TokenKind::Invalid); |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | /// Scanner reaches EOF after trailing whitespace and comments. |
|
| 78 | - | @test fn testScanProgramEndingWithNewline() throws (testing::TestError) { |
|
| 78 | + | @test unsafe fn testScanProgramEndingWithNewline() throws (testing::TestError) { |
|
| 79 | 79 | let mut s = testScanner( |
|
| 80 | 80 | ".text;\n@start\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n// comment\nbeq %a0 %zero @done;\n@done\nret;\n" |
|
| 81 | 81 | ); |
|
| 82 | 82 | loop { |
|
| 83 | 83 | let tok = super::next(&mut s); |
| 87 | 87 | } |
|
| 88 | 88 | } |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | 91 | /// Signed numbers scan only the numeric formats supported by the assembler scanner. |
|
| 92 | - | @test fn testScanSignedHexAndUnsupportedNumericForms() throws (testing::TestError) { |
|
| 92 | + | @test unsafe fn testScanSignedHexAndUnsupportedNumericForms() throws (testing::TestError) { |
|
| 93 | 93 | let mut s = testScanner( |
|
| 94 | 94 | "+0x2a -0b10 45.5" |
|
| 95 | 95 | ); |
|
| 96 | 96 | let mut tok = super::next(&mut s); |
|
| 97 | 97 | try testing::expect(tok.kind == super::TokenKind::Number); |
| 117 | 117 | try testing::expect(tok.kind == super::TokenKind::Number); |
|
| 118 | 118 | try testing::expect(mem::eq(tok.source, "5")); |
|
| 119 | 119 | } |
|
| 120 | 120 | ||
| 121 | 121 | /// Unterminated string and character literals report invalid tokens. |
|
| 122 | - | @test fn testScanUnterminatedDelimitedLiterals() throws (testing::TestError) { |
|
| 122 | + | @test unsafe fn testScanUnterminatedDelimitedLiterals() throws (testing::TestError) { |
|
| 123 | 123 | let mut stringScan = testScanner("\"unterminated"); |
|
| 124 | 124 | let stringTok = super::next(&mut stringScan); |
|
| 125 | 125 | try testing::expect(stringTok.kind == super::TokenKind::Invalid); |
|
| 126 | 126 | try testing::expect(mem::eq(stringTok.source, "unterminated string")); |
|
| 127 | 127 |
lib/std/arch/rv64/emit.rad
+12 -7
| 95 | 95 | /// Offset from SP. |
|
| 96 | 96 | offset: i32, |
|
| 97 | 97 | } |
|
| 98 | 98 | ||
| 99 | 99 | /// Emission context. Tracks state during code generation. |
|
| 100 | - | export record Emitter: Copy { |
|
| 100 | + | export record Emitter { |
|
| 101 | 101 | /// Allocator for growing append-backed emitter lists. |
|
| 102 | 102 | allocator: alloc::Allocator, |
|
| 103 | 103 | /// Emitted instructions storage. |
|
| 104 | 104 | code: *mut [u32], |
|
| 105 | 105 | /// Current number of emitted instructions. |
| 196 | 196 | if debug { |
|
| 197 | 197 | set debugEntries = try alloc::allocSlice( |
|
| 198 | 198 | arena, @sizeOf(types::DebugEntry), @alignOf(types::DebugEntry), MAX_DEBUG_ENTRIES |
|
| 199 | 199 | ) as *mut [types::DebugEntry]; |
|
| 200 | 200 | } |
|
| 201 | + | let pendingBranchesBuf = pendingBranches as *mut [PendingBranch]; |
|
| 202 | + | let pendingCallsBuf = pendingCalls as *mut [PendingCall]; |
|
| 203 | + | let pendingJumpsBuf = pendingJumps as *mut [PendingJump]; |
|
| 204 | + | let pendingAddrLoadsBuf = pendingAddrLoads as *mut [PendingAddrLoad]; |
|
| 205 | + | let funcsBuf = funcs as *mut [types::FuncAddr]; |
|
| 201 | 206 | return Emitter { |
|
| 202 | 207 | allocator: alloc::arenaAllocator(arena), |
|
| 203 | 208 | code: code as *mut [u32], |
|
| 204 | 209 | codeLen: 0, |
|
| 205 | - | pendingBranches: @sliceOf((pendingBranches as *mut [PendingBranch]).ptr, 0, MAX_PENDING), |
|
| 206 | - | pendingCalls: @sliceOf((pendingCalls as *mut [PendingCall]).ptr, 0, MAX_PENDING), |
|
| 207 | - | pendingJumps: @sliceOf((pendingJumps as *mut [PendingJump]).ptr, 0, MAX_PENDING), |
|
| 208 | - | pendingAddrLoads: @sliceOf((pendingAddrLoads as *mut [PendingAddrLoad]).ptr, 0, MAX_PENDING), |
|
| 210 | + | pendingBranches: &mut pendingBranchesBuf[..0], |
|
| 211 | + | pendingCalls: &mut pendingCallsBuf[..0], |
|
| 212 | + | pendingJumps: &mut pendingJumpsBuf[..0], |
|
| 213 | + | pendingAddrLoads: &mut pendingAddrLoadsBuf[..0], |
|
| 209 | 214 | labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]), |
|
| 210 | - | funcs: @sliceOf((funcs as *mut [types::FuncAddr]).ptr, 0, MAX_FUNCS), |
|
| 215 | + | funcs: &mut funcsBuf[..0], |
|
| 211 | 216 | debugEntries, |
|
| 212 | 217 | debugEntriesLen: 0, |
|
| 213 | 218 | }; |
|
| 214 | 219 | } |
|
| 215 | 220 |
| 375 | 380 | assert encode::isJumpImm(offset), "patchLocalBranches: jump offset too large"; |
|
| 376 | 381 | patch(e, p.index, encode::jal(super::ZERO, offset)); |
|
| 377 | 382 | }, |
|
| 378 | 383 | } |
|
| 379 | 384 | } |
|
| 380 | - | set e.pendingBranches = @sliceOf(e.pendingBranches.ptr, 0, e.pendingBranches.cap); |
|
| 385 | + | set e.pendingBranches = &mut e.pendingBranches[..0]; |
|
| 381 | 386 | } |
|
| 382 | 387 | ||
| 383 | 388 | /// Encode a conditional branch instruction. |
|
| 384 | 389 | fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 { |
|
| 385 | 390 | match op { |
lib/std/arch/rv64/isel.rad
+129 -129
| 147 | 147 | /// Get the source register for an SSA register. |
|
| 148 | 148 | /// If the register is spilled, loads the value from the spill slot into the |
|
| 149 | 149 | /// scratch register and returns it. Otherwise returns the physical register. |
|
| 150 | 150 | unsafe fn getSrcReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 151 | 151 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) { |
|
| 152 | - | emit::emitLd(&mut *s.e, scratch, spillBase(s), spillOffset(s, slot)); |
|
| 152 | + | emit::emitLd(s.e, scratch, spillBase(s), spillOffset(s, slot)); |
|
| 153 | 153 | return scratch; |
|
| 154 | 154 | } |
|
| 155 | 155 | return getReg(s, ssa); |
|
| 156 | 156 | } |
|
| 157 | 157 |
| 165 | 165 | }, |
|
| 166 | 166 | case il::Val::Imm(imm) => { |
|
| 167 | 167 | if imm == 0 { |
|
| 168 | 168 | return super::ZERO; |
|
| 169 | 169 | } |
|
| 170 | - | emit::loadImm(&mut *s.e, scratch, imm); |
|
| 170 | + | emit::loadImm(s.e, scratch, imm); |
|
| 171 | 171 | return scratch; |
|
| 172 | 172 | }, |
|
| 173 | 173 | case il::Val::DataSym(name) => { |
|
| 174 | - | emit::recordDataAddrLoad(&mut *s.e, name, scratch); |
|
| 174 | + | emit::recordDataAddrLoad(s.e, name, scratch); |
|
| 175 | 175 | return scratch; |
|
| 176 | 176 | }, |
|
| 177 | 177 | case il::Val::FnAddr(name) => { |
|
| 178 | - | emit::recordAddrLoad(&mut *s.e, name, scratch); |
|
| 178 | + | emit::recordAddrLoad(s.e, name, scratch); |
|
| 179 | 179 | return scratch; |
|
| 180 | 180 | }, |
|
| 181 | 181 | case il::Val::Undef => { |
|
| 182 | 182 | return scratch; |
|
| 183 | 183 | } |
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | 195 | /// Emit a move instruction if source and destination differ. |
|
| 196 | 196 | unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) { |
|
| 197 | 197 | if *rd <> *rs { |
|
| 198 | - | emit::emit(&mut *s.e, encode::mv(rd, rs)); |
|
| 198 | + | emit::emit(s.e, encode::mv(rd, rs)); |
|
| 199 | 199 | } |
|
| 200 | 200 | } |
|
| 201 | 201 | ||
| 202 | 202 | /// Emit zero-extension from a sub-word type to the full register width. |
|
| 203 | 203 | fn emitZext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) { |
| 245 | 245 | if let case il::Val::Imm(imm) = b { |
|
| 246 | 246 | set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed)); |
|
| 247 | 247 | } |
|
| 248 | 248 | let rs2 = resolveVal(s, super::SCRATCH2, divisor); |
|
| 249 | 249 | if not isExtendedImm(divisor, typ, signed) { |
|
| 250 | - | emitCmpExt(&mut *s.e, rs2, rs2, typ, signed); |
|
| 250 | + | emitCmpExt(s.e, rs2, rs2, typ, signed); |
|
| 251 | 251 | } |
|
| 252 | 252 | let mut knownNonZero = false; |
|
| 253 | 253 | if let case il::Val::Imm(imm) = divisor { |
|
| 254 | 254 | set knownNonZero = imm <> 0; |
|
| 255 | 255 | } |
|
| 256 | 256 | if not knownNonZero { |
|
| 257 | - | emit::emit(&mut *s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2)); |
|
| 258 | - | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 257 | + | emit::emit(s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2)); |
|
| 258 | + | emit::emit(s.e, encode::ebreak()); |
|
| 259 | 259 | } |
|
| 260 | 260 | return rs2; |
|
| 261 | 261 | } |
|
| 262 | 262 | ||
| 263 | 263 | //////////////////////// |
| 272 | 272 | isDynamic: bool, |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Pre-scan all blocks for constant-sized reserve instructions. |
|
| 276 | 276 | /// Returns the total size needed for all static reserves, respecting alignment. |
|
| 277 | - | fn computeReserveInfo(func: *il::Fn) -> ReserveInfo { |
|
| 277 | + | unsafe fn computeReserveInfo(func: *unsafe il::Fn) -> ReserveInfo { |
|
| 278 | 278 | let mut offset: i32 = 0; |
|
| 279 | 279 | let mut isDynamic = false; |
|
| 280 | 280 | ||
| 281 | 281 | for b in 0..func.blocks.len { |
|
| 282 | 282 | let block = &func.blocks[b]; |
| 299 | 299 | ||
| 300 | 300 | /// Select instructions for a function. |
|
| 301 | 301 | export unsafe fn selectFn( |
|
| 302 | 302 | e: &mut emit::Emitter, |
|
| 303 | 303 | ralloc: ®alloc::AllocResult, |
|
| 304 | - | func: *il::Fn |
|
| 304 | + | func: *unsafe il::Fn |
|
| 305 | 305 | ) { |
|
| 306 | 306 | // Reset block offsets for this function. |
|
| 307 | 307 | labels::resetBlocks(&mut e.labels); |
|
| 308 | 308 | // Pre-scan for constant-sized reserves to promote to fixed frame slots. |
|
| 309 | 309 | let reserveInfo = computeReserveInfo(func); |
| 324 | 324 | reserveOffset: 0, pendingSpill: nil, |
|
| 325 | 325 | nextSynthBlock: func.blocks.len + 1, |
|
| 326 | 326 | isDynamic: frame.isDynamic, |
|
| 327 | 327 | }; |
|
| 328 | 328 | // Record function name for printing. |
|
| 329 | - | emit::recordFunc(&mut *s.e, func.name); |
|
| 329 | + | emit::recordFunc(s.e, func.name); |
|
| 330 | 330 | // Record function code offset for call patching. |
|
| 331 | - | emit::recordFuncOffset(&mut *s.e, func.name); |
|
| 331 | + | emit::recordFuncOffset(s.e, func.name); |
|
| 332 | 332 | // Emit prologue. |
|
| 333 | - | emit::emitPrologue(&mut *s.e, &frame); |
|
| 333 | + | emit::emitPrologue(s.e, &frame); |
|
| 334 | 334 | ||
| 335 | 335 | // Move function params from arg registers to assigned registers. |
|
| 336 | 336 | // Cross-call params may have been assigned to callee-saved registers |
|
| 337 | 337 | // instead of their natural arg registers. Spilled params are stored |
|
| 338 | 338 | // directly to their spill slots. |
| 341 | 341 | let param = funcParam.value; |
|
| 342 | 342 | let argReg = super::ARG_REGS[i]; |
|
| 343 | 343 | ||
| 344 | 344 | if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) { |
|
| 345 | 345 | // Spilled parameter: store arg register to spill slot. |
|
| 346 | - | emit::emitSd(&mut *s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 346 | + | emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 347 | 347 | } else if let assigned = ralloc.assignments[param.n] { |
|
| 348 | 348 | emitMv(&mut s, assigned, argReg); |
|
| 349 | 349 | } |
|
| 350 | 350 | } |
|
| 351 | 351 | } |
| 353 | 353 | // Emit each block. |
|
| 354 | 354 | for i in 0..func.blocks.len { |
|
| 355 | 355 | selectBlock(&mut s, i, &func.blocks[i], &frame, func); |
|
| 356 | 356 | } |
|
| 357 | 357 | // Emit epilogue. |
|
| 358 | - | emit::emitEpilogue(&mut *s.e, &frame); |
|
| 358 | + | emit::emitEpilogue(s.e, &frame); |
|
| 359 | 359 | // Patch local branches now that all blocks are emitted. |
|
| 360 | - | emit::patchLocalBranches(&mut *s.e); |
|
| 360 | + | emit::patchLocalBranches(s.e); |
|
| 361 | 361 | } |
|
| 362 | 362 | ||
| 363 | 363 | /// Select instructions for a block. |
|
| 364 | - | unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *il::Block, frame: &emit::Frame, func: *il::Fn) { |
|
| 364 | + | unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *unsafe il::Block, frame: &emit::Frame, func: *unsafe il::Fn) { |
|
| 365 | 365 | // Record block address for branch patching. |
|
| 366 | - | emit::recordBlock(&mut *s.e, blockIdx); |
|
| 366 | + | emit::recordBlock(s.e, blockIdx); |
|
| 367 | 367 | ||
| 368 | 368 | // Block parameters are handled at jump sites (in `Jmp`/`Br`). |
|
| 369 | 369 | // By the time we enter the block, the arguments have already been |
|
| 370 | 370 | // moved to the parameter registers by the predecessor's terminator. |
|
| 371 | 371 | ||
| 372 | 372 | // Process each instruction, auto-committing any pending spill after each. |
|
| 373 | 373 | let hasLocs = block.locs.len > 0; |
|
| 374 | 374 | for instr, i in block.instrs { |
|
| 375 | 375 | // Record debug location before emitting machine instructions. |
|
| 376 | 376 | if hasLocs { |
|
| 377 | - | emit::recordSrcLoc(&mut *s.e, block.locs[i]); |
|
| 377 | + | emit::recordSrcLoc(s.e, block.locs[i]); |
|
| 378 | 378 | } |
|
| 379 | 379 | set s.pendingSpill = nil; |
|
| 380 | 380 | selectInstr(s, blockIdx, instr, frame, func); |
|
| 381 | 381 | ||
| 382 | 382 | // Flush the pending spill store, if any. |
|
| 383 | 383 | if let p = s.pendingSpill { |
|
| 384 | 384 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) { |
|
| 385 | - | emit::emitSd(&mut *s.e, p.rd, spillBase(s), spillOffset(s, slot)); |
|
| 385 | + | emit::emitSd(s.e, p.rd, spillBase(s), spillOffset(s, slot)); |
|
| 386 | 386 | } |
|
| 387 | 387 | set s.pendingSpill = nil; |
|
| 388 | 388 | } |
|
| 389 | 389 | } |
|
| 390 | 390 | } |
|
| 391 | 391 | ||
| 392 | 392 | /// Select instructions for a single IL instruction. |
|
| 393 | - | unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *il::Fn) { |
|
| 393 | + | unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *unsafe il::Fn) { |
|
| 394 | 394 | match instr { |
|
| 395 | 395 | case il::Instr::BinOp { op, typ, dst, a, b } => { |
|
| 396 | 396 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 397 | 397 | let rs1 = resolveVal(s, super::SCRATCH1, a); |
|
| 398 | 398 | selectAluBinOp(s, op, typ, rd, rs1, b); |
| 403 | 403 | selectAluUnOp(s, op, typ, rd, rs); |
|
| 404 | 404 | }, |
|
| 405 | 405 | case il::Instr::Load { typ, dst, src, offset } => { |
|
| 406 | 406 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 407 | 407 | let base = getSrcReg(s, src, super::SCRATCH2); |
|
| 408 | - | emit::emitLoad(&mut *s.e, rd, base, offset, typ); |
|
| 408 | + | emit::emitLoad(s.e, rd, base, offset, typ); |
|
| 409 | 409 | }, |
|
| 410 | 410 | case il::Instr::Sload { typ, dst, src, offset } => { |
|
| 411 | 411 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 412 | 412 | let base = getSrcReg(s, src, super::SCRATCH2); |
|
| 413 | - | emit::emitSload(&mut *s.e, rd, base, offset, typ); |
|
| 413 | + | emit::emitSload(s.e, rd, base, offset, typ); |
|
| 414 | 414 | }, |
|
| 415 | 415 | case il::Instr::Store { typ, src, dst, offset } => { |
|
| 416 | 416 | let base = getSrcReg(s, dst, super::SCRATCH2); |
|
| 417 | 417 | let rs = resolveVal(s, super::SCRATCH1, src); |
|
| 418 | - | emit::emitStore(&mut *s.e, rs, base, offset, typ); |
|
| 418 | + | emit::emitStore(s.e, rs, base, offset, typ); |
|
| 419 | 419 | }, |
|
| 420 | 420 | case il::Instr::Copy { dst, val } => { |
|
| 421 | 421 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 422 | 422 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 423 | 423 | emitMv(s, rd, rs); |
| 430 | 430 | let aligned: i32 = mem::alignUpI32(s.reserveOffset, alignment as i32); |
|
| 431 | 431 | let base = spillBase(s); |
|
| 432 | 432 | let offset = s.ralloc.spill.frameSize + aligned |
|
| 433 | 433 | - (s.frameSize if s.isDynamic else 0); |
|
| 434 | 434 | ||
| 435 | - | emit::emitAddImm(&mut *s.e, rd, base, offset); |
|
| 435 | + | emit::emitAddImm(s.e, rd, base, offset); |
|
| 436 | 436 | set s.reserveOffset = aligned + (sz as i32); |
|
| 437 | 437 | }, |
|
| 438 | 438 | case il::Val::Reg(r) => { |
|
| 439 | 439 | // Dynamic-sized reserve: runtime SP adjustment. |
|
| 440 | 440 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 441 | 441 | let rs = getSrcReg(s, r, super::SCRATCH2); |
|
| 442 | 442 | ||
| 443 | - | emit::emit(&mut *s.e, encode::sub(super::SP, super::SP, rs)); |
|
| 443 | + | emit::emit(s.e, encode::sub(super::SP, super::SP, rs)); |
|
| 444 | 444 | ||
| 445 | 445 | if alignment > 1 { |
|
| 446 | 446 | let mask = 0 - alignment as i32; |
|
| 447 | 447 | assert encode::isSmallImm(mask); |
|
| 448 | 448 | ||
| 449 | - | emit::emit(&mut *s.e, encode::andi(super::SP, super::SP, mask)); |
|
| 449 | + | emit::emit(s.e, encode::andi(super::SP, super::SP, mask)); |
|
| 450 | 450 | } |
|
| 451 | - | emit::emit(&mut *s.e, encode::mv(rd, super::SP)); |
|
| 451 | + | emit::emit(s.e, encode::mv(rd, super::SP)); |
|
| 452 | 452 | }, |
|
| 453 | 453 | else => |
|
| 454 | 454 | panic "selectInstr: invalid reserve operand", |
|
| 455 | 455 | } |
|
| 456 | 456 | }, |
| 476 | 476 | panic "selectInstr: blit dst not spilled"; |
|
| 477 | 477 | }; |
|
| 478 | 478 | let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else { |
|
| 479 | 479 | panic "selectInstr: blit src not spilled"; |
|
| 480 | 480 | }; |
|
| 481 | - | emit::emitLd(&mut *s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot)); |
|
| 481 | + | emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot)); |
|
| 482 | 482 | set srcReload = spillOffset(s, srcSlot); |
|
| 483 | 483 | } else { |
|
| 484 | 484 | set rdst = getSrcReg(s, dst, super::SCRATCH2); |
|
| 485 | 485 | set rsrc = getSrcReg(s, src, super::SCRATCH2); |
|
| 486 | 486 | } |
| 493 | 493 | let canLoop = not bothSpilled |
|
| 494 | 494 | and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2 |
|
| 495 | 495 | and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2; |
|
| 496 | 496 | ||
| 497 | 497 | if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD { |
|
| 498 | - | emit::emitAddImm(&mut *s.e, super::SCRATCH1, rsrc, dwordBytes); |
|
| 498 | + | emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes); |
|
| 499 | 499 | ||
| 500 | 500 | let loopStart = s.e.codeLen; |
|
| 501 | 501 | ||
| 502 | - | emit::emitLd(&mut *s.e, super::SCRATCH2, rsrc, 0); |
|
| 503 | - | emit::emitSd(&mut *s.e, super::SCRATCH2, rdst, 0); |
|
| 504 | - | emit::emit(&mut *s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE)); |
|
| 502 | + | emit::emitLd(s.e, super::SCRATCH2, rsrc, 0); |
|
| 503 | + | emit::emitSd(s.e, super::SCRATCH2, rdst, 0); |
|
| 504 | + | emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE)); |
|
| 505 | 505 | ||
| 506 | 506 | if *rdst <> *rsrc { |
|
| 507 | - | emit::emit(&mut *s.e, encode::addi(rdst, rdst, super::DWORD_SIZE)); |
|
| 507 | + | emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE)); |
|
| 508 | 508 | } |
|
| 509 | 509 | let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE; |
|
| 510 | 510 | ||
| 511 | - | emit::emit(&mut *s.e, encode::bne(rsrc, super::SCRATCH1, brOff)); |
|
| 511 | + | emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff)); |
|
| 512 | 512 | set remaining -= dwordBytes; |
|
| 513 | 513 | } |
|
| 514 | 514 | ||
| 515 | 515 | // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time. |
|
| 516 | 516 | // Before each load/store pair, check whether the offset is |
|
| 517 | 517 | // about to exceed the 12-bit signed immediate range. When |
|
| 518 | 518 | // it does, advance the base registers by the accumulated |
|
| 519 | 519 | // offset and reset to zero. |
|
| 520 | 520 | while remaining >= super::DWORD_SIZE { |
|
| 521 | 521 | if offset > super::MAX_IMM - super::DWORD_SIZE { |
|
| 522 | - | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 522 | + | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 523 | 523 | if *rdst <> *rsrc { |
|
| 524 | - | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 524 | + | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 525 | 525 | } |
|
| 526 | 526 | set offset = 0; |
|
| 527 | 527 | } |
|
| 528 | 528 | if let off = srcReload { |
|
| 529 | - | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 530 | - | emit::emitLd(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 529 | + | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 530 | + | emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 531 | 531 | } else { |
|
| 532 | - | emit::emitLd(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 532 | + | emit::emitLd(s.e, super::SCRATCH1, rsrc, offset); |
|
| 533 | 533 | } |
|
| 534 | - | emit::emitSd(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 534 | + | emit::emitSd(s.e, super::SCRATCH1, rdst, offset); |
|
| 535 | 535 | set offset += super::DWORD_SIZE; |
|
| 536 | 536 | set remaining -= super::DWORD_SIZE; |
|
| 537 | 537 | } |
|
| 538 | 538 | if remaining >= super::WORD_SIZE { |
|
| 539 | 539 | if offset > super::MAX_IMM - super::WORD_SIZE { |
|
| 540 | - | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 540 | + | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 541 | 541 | if *rdst <> *rsrc { |
|
| 542 | - | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 542 | + | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 543 | 543 | } |
|
| 544 | 544 | set offset = 0; |
|
| 545 | 545 | } |
|
| 546 | 546 | if let off = srcReload { |
|
| 547 | - | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 548 | - | emit::emitLw(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 547 | + | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 548 | + | emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 549 | 549 | } else { |
|
| 550 | - | emit::emitLw(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 550 | + | emit::emitLw(s.e, super::SCRATCH1, rsrc, offset); |
|
| 551 | 551 | } |
|
| 552 | - | emit::emitSw(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 552 | + | emit::emitSw(s.e, super::SCRATCH1, rdst, offset); |
|
| 553 | 553 | set offset += super::WORD_SIZE; |
|
| 554 | 554 | set remaining -= super::WORD_SIZE; |
|
| 555 | 555 | } |
|
| 556 | 556 | while remaining > 0 { |
|
| 557 | 557 | if offset > super::MAX_IMM - 1 { |
|
| 558 | - | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 558 | + | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 559 | 559 | if *rdst <> *rsrc { |
|
| 560 | - | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 560 | + | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 561 | 561 | } |
|
| 562 | 562 | set offset = 0; |
|
| 563 | 563 | } |
|
| 564 | 564 | if let off = srcReload { |
|
| 565 | - | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 566 | - | emit::emitLb(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 565 | + | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 566 | + | emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 567 | 567 | } else { |
|
| 568 | - | emit::emitLb(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 568 | + | emit::emitLb(s.e, super::SCRATCH1, rsrc, offset); |
|
| 569 | 569 | } |
|
| 570 | - | emit::emitSb(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 570 | + | emit::emitSb(s.e, super::SCRATCH1, rdst, offset); |
|
| 571 | 571 | set offset += 1; |
|
| 572 | 572 | set remaining -= 1; |
|
| 573 | 573 | } |
|
| 574 | 574 | // Restore base registers if they were advanced (never happens |
|
| 575 | 575 | // in the both-spilled case since size <= MAX_IMM). |
|
| 576 | 576 | if not bothSpilled { |
|
| 577 | 577 | let advanced = staticSize as i32 - offset; |
|
| 578 | 578 | if advanced <> 0 { |
|
| 579 | - | emit::emitAddImm(&mut *s.e, rsrc, rsrc, 0 - advanced); |
|
| 579 | + | emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced); |
|
| 580 | 580 | if *rdst <> *rsrc { |
|
| 581 | - | emit::emitAddImm(&mut *s.e, rdst, rdst, 0 - advanced); |
|
| 581 | + | emit::emitAddImm(s.e, rdst, rdst, 0 - advanced); |
|
| 582 | 582 | } |
|
| 583 | 583 | } |
|
| 584 | 584 | } |
|
| 585 | 585 | }, |
|
| 586 | 586 | case il::Instr::Zext { typ, dst, val } => { |
|
| 587 | 587 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 588 | 588 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 589 | - | emitZext(&mut *s.e, rd, rs, typ); |
|
| 589 | + | emitZext(s.e, rd, rs, typ); |
|
| 590 | 590 | }, |
|
| 591 | 591 | case il::Instr::Sext { typ, dst, val } => { |
|
| 592 | 592 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 593 | 593 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 594 | - | emitSext(&mut *s.e, rd, rs, typ); |
|
| 594 | + | emitSext(s.e, rd, rs, typ); |
|
| 595 | 595 | }, |
|
| 596 | 596 | case il::Instr::Ret { val } => { |
|
| 597 | 597 | if let v = val { |
|
| 598 | 598 | let rs = resolveVal(s, super::SCRATCH1, v); |
|
| 599 | 599 | emitMv(s, super::A0, rs); |
| 601 | 601 | // Skip the jump to epilogue if this RET is in the last block, |
|
| 602 | 602 | // since the epilogue immediately follows. |
|
| 603 | 603 | if frame.totalSize <> 0 and blockIdx + 1 == frame.epilogueBlock { |
|
| 604 | 604 | // Epilogue is the next block; fallthrough is sufficient. |
|
| 605 | 605 | } else { |
|
| 606 | - | emit::emitReturn(&mut *s.e, frame); |
|
| 606 | + | emit::emitReturn(s.e, frame); |
|
| 607 | 607 | } |
|
| 608 | 608 | }, |
|
| 609 | 609 | case il::Instr::Jmp { target, args } => { |
|
| 610 | 610 | // Move arguments to target block's parameter registers. |
|
| 611 | 611 | emitBlockArgs(s, func, target, args); |
|
| 612 | 612 | // Skip branch if target is the next block (fallthrough). |
|
| 613 | 613 | if target <> blockIdx + 1 { |
|
| 614 | - | emit::recordBranch(&mut *s.e, target, emit::BranchKind::Jump); |
|
| 614 | + | emit::recordBranch(s.e, target, emit::BranchKind::Jump); |
|
| 615 | 615 | } |
|
| 616 | 616 | }, |
|
| 617 | 617 | case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => { |
|
| 618 | 618 | // Use zero register directly for immediate `0` operands. |
|
| 619 | 619 | let aIsZero = isZeroImm(a); |
| 636 | 636 | if let case il::CmpOp::Slt = op { |
|
| 637 | 637 | set signed = true; |
|
| 638 | 638 | } |
|
| 639 | 639 | let useSext = cmpUsesSext(typ, signed); |
|
| 640 | 640 | if not aIsZero and not isExtendedImm(a, typ, useSext) { |
|
| 641 | - | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 641 | + | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 642 | 642 | } |
|
| 643 | 643 | if not bIsZero and not isExtendedImm(b, typ, useSext) { |
|
| 644 | - | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 644 | + | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 645 | 645 | } |
|
| 646 | 646 | // Block-argument moves must only execute on the taken path. |
|
| 647 | 647 | // When `thenArgs` is non-empty, invert the branch so that the |
|
| 648 | 648 | // then-moves land on the fall-through (taken) side. |
|
| 649 | 649 | // |
| 652 | 652 | // conditional branch to skip to the *other* target and letting |
|
| 653 | 653 | // execution fall through. |
|
| 654 | 654 | if thenArgs.len > 0 and elseArgs.len > 0 { |
|
| 655 | 655 | panic "selectInstr: both `then` and `else` have block arguments"; |
|
| 656 | 656 | } else if thenArgs.len > 0 { |
|
| 657 | - | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 657 | + | emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 658 | 658 | emitBlockArgs(s, func, thenTarget, thenArgs); |
|
| 659 | 659 | // Skip trailing jump if then is the next block (fallthrough). |
|
| 660 | 660 | if thenTarget <> blockIdx + 1 { |
|
| 661 | - | emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Jump); |
|
| 661 | + | emit::recordBranch(s.e, thenTarget, emit::BranchKind::Jump); |
|
| 662 | 662 | } |
|
| 663 | 663 | } else if thenTarget == blockIdx + 1 and elseArgs.len == 0 { |
|
| 664 | 664 | // Then is the next block and no else args: invert the |
|
| 665 | 665 | // condition to branch to else and fall through to then. |
|
| 666 | - | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 666 | + | emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 667 | 667 | } else { |
|
| 668 | - | emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 }); |
|
| 668 | + | emit::recordBranch(s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 }); |
|
| 669 | 669 | emitBlockArgs(s, func, elseTarget, elseArgs); |
|
| 670 | 670 | // Skip trailing jump if else is the next block (fallthrough). |
|
| 671 | 671 | if elseTarget <> blockIdx + 1 { |
|
| 672 | - | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::Jump); |
|
| 672 | + | emit::recordBranch(s.e, elseTarget, emit::BranchKind::Jump); |
|
| 673 | 673 | } |
|
| 674 | 674 | } |
|
| 675 | 675 | }, |
|
| 676 | 676 | case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
|
| 677 | 677 | let rs1 = resolveVal(s, super::SCRATCH1, val); |
|
| 678 | 678 | // When a case has block args, invert the branch to skip past |
|
| 679 | 679 | // the arg moves. |
|
| 680 | 680 | for c in cases { |
|
| 681 | - | emit::loadImm(&mut *s.e, super::SCRATCH2, c.value); |
|
| 681 | + | emit::loadImm(s.e, super::SCRATCH2, c.value); |
|
| 682 | 682 | ||
| 683 | 683 | if c.args.len > 0 { |
|
| 684 | 684 | let skip = s.nextSynthBlock; |
|
| 685 | 685 | set s.nextSynthBlock = skip + 1; |
|
| 686 | 686 | ||
| 687 | - | emit::recordBranch(&mut *s.e, skip, emit::BranchKind::InvertedCond { |
|
| 687 | + | emit::recordBranch(s.e, skip, emit::BranchKind::InvertedCond { |
|
| 688 | 688 | op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2, |
|
| 689 | 689 | }); |
|
| 690 | 690 | emitBlockArgs(s, func, c.target, c.args); |
|
| 691 | - | emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Jump); |
|
| 692 | - | emit::recordBlock(&mut *s.e, skip); |
|
| 691 | + | emit::recordBranch(s.e, c.target, emit::BranchKind::Jump); |
|
| 692 | + | emit::recordBlock(s.e, skip); |
|
| 693 | 693 | } else { |
|
| 694 | - | emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Cond { |
|
| 694 | + | emit::recordBranch(s.e, c.target, emit::BranchKind::Cond { |
|
| 695 | 695 | op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2, |
|
| 696 | 696 | }); |
|
| 697 | 697 | } |
|
| 698 | 698 | } |
|
| 699 | 699 | // Fall through to default. |
|
| 700 | 700 | emitBlockArgs(s, func, defaultTarget, defaultArgs); |
|
| 701 | - | emit::recordBranch(&mut *s.e, defaultTarget, emit::BranchKind::Jump); |
|
| 701 | + | emit::recordBranch(s.e, defaultTarget, emit::BranchKind::Jump); |
|
| 702 | 702 | }, |
|
| 703 | 703 | case il::Instr::Unreachable => { |
|
| 704 | - | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 704 | + | emit::emit(s.e, encode::ebreak()); |
|
| 705 | 705 | }, |
|
| 706 | 706 | case il::Instr::Call { retTy, dst, func, args } => { |
|
| 707 | 707 | // For indirect calls, save target to scratch register before arg |
|
| 708 | 708 | // setup can clobber it. |
|
| 709 | 709 | if let case il::Val::Reg(r) = func { |
| 715 | 715 | emitParallelMoves(s, &super::ARG_REGS[..], args); |
|
| 716 | 716 | ||
| 717 | 717 | // Emit call. |
|
| 718 | 718 | match func { |
|
| 719 | 719 | case il::Val::FnAddr(name) => { |
|
| 720 | - | emit::recordCall(&mut *s.e, name); |
|
| 720 | + | emit::recordCall(s.e, name); |
|
| 721 | 721 | }, |
|
| 722 | 722 | case il::Val::Reg(_) => { |
|
| 723 | - | emit::emit(&mut *s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 723 | + | emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 724 | 724 | }, |
|
| 725 | 725 | else => { |
|
| 726 | 726 | panic "selectInstr: invalid call target"; |
|
| 727 | 727 | } |
|
| 728 | 728 | } |
| 738 | 738 | // support constant-evaluating struct/union values in them. |
|
| 739 | 739 | let ecallDsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3]; |
|
| 740 | 740 | let ecallArgs: [il::Val; 5] = [num, a0, a1, a2, a3]; |
|
| 741 | 741 | ||
| 742 | 742 | emitParallelMoves(s, &ecallDsts[..], &ecallArgs[..]); |
|
| 743 | - | emit::emit(&mut *s.e, encode::ecall()); |
|
| 743 | + | emit::emit(s.e, encode::ecall()); |
|
| 744 | 744 | ||
| 745 | 745 | // Result in A0. |
|
| 746 | 746 | let ecallRd = getDstReg(s, dst, super::SCRATCH1); |
|
| 747 | 747 | emitMv(s, ecallRd, super::A0); |
|
| 748 | 748 | }, |
|
| 749 | 749 | case il::Instr::Ebreak => { |
|
| 750 | - | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 750 | + | emit::emit(s.e, encode::ebreak()); |
|
| 751 | 751 | }, |
|
| 752 | 752 | case il::Instr::MemoryFence => { |
|
| 753 | - | emit::emit(&mut *s.e, encode::fence()); |
|
| 753 | + | emit::emit(s.e, encode::fence()); |
|
| 754 | 754 | }, |
|
| 755 | 755 | } |
|
| 756 | 756 | } |
|
| 757 | 757 | ||
| 758 | 758 | /// Choose the cheapest canonical representation that preserves the comparison. |
| 839 | 839 | case il::BinOp::Add => { |
|
| 840 | 840 | if typ == il::Type::W32 { |
|
| 841 | 841 | // Inline W32 ADD with immediate optimization. |
|
| 842 | 842 | if let case il::Val::Imm(imm) = b { |
|
| 843 | 843 | if encode::isSmallImm64(imm) { |
|
| 844 | - | emit::emit(&mut *s.e, encode::addiw(rd, rs1, imm as i32)); |
|
| 844 | + | emit::emit(s.e, encode::addiw(rd, rs1, imm as i32)); |
|
| 845 | 845 | return; |
|
| 846 | 846 | } |
|
| 847 | 847 | } |
|
| 848 | 848 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 849 | - | emit::emit(&mut *s.e, encode::addw(rd, rs1, rs2)); |
|
| 849 | + | emit::emit(s.e, encode::addw(rd, rs1, rs2)); |
|
| 850 | 850 | } else { |
|
| 851 | 851 | selectBinOp(s, rd, rs1, b, BinOp::Add, super::SCRATCH2); |
|
| 852 | 852 | } |
|
| 853 | 853 | } |
|
| 854 | 854 | case il::BinOp::Sub => { |
|
| 855 | 855 | // Optimize subtraction by small immediate: use ADDI with negated value. |
|
| 856 | 856 | if let case il::Val::Imm(imm) = b { |
|
| 857 | 857 | let neg = -imm; |
|
| 858 | 858 | if neg >= super::MIN_IMM as i64 and neg <= super::MAX_IMM as i64 { |
|
| 859 | - | emit::emit(&mut *s.e, |
|
| 859 | + | emit::emit(s.e, |
|
| 860 | 860 | encode::addiw(rd, rs1, neg as i32) |
|
| 861 | 861 | if typ == il::Type::W32 else |
|
| 862 | 862 | encode::addi(rd, rs1, neg as i32)); |
|
| 863 | 863 | return; |
|
| 864 | 864 | } |
|
| 865 | 865 | } |
|
| 866 | 866 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 867 | 867 | ||
| 868 | - | emit::emit(&mut *s.e, |
|
| 868 | + | emit::emit(s.e, |
|
| 869 | 869 | encode::subw(rd, rs1, rs2) |
|
| 870 | 870 | if typ == il::Type::W32 else |
|
| 871 | 871 | encode::sub(rd, rs1, rs2)); |
|
| 872 | 872 | } |
|
| 873 | 873 | case il::BinOp::Mul => { |
|
| 874 | 874 | // Strength-reduce multiplication by known constants. |
|
| 875 | 875 | if let case il::Val::Imm(imm) = b { |
|
| 876 | 876 | if imm == 0 { |
|
| 877 | - | emit::emit(&mut *s.e, encode::mv(rd, super::ZERO)); |
|
| 877 | + | emit::emit(s.e, encode::mv(rd, super::ZERO)); |
|
| 878 | 878 | return; |
|
| 879 | 879 | } else if imm == 1 { |
|
| 880 | 880 | emitMv(s, rd, rs1); |
|
| 881 | 881 | return; |
|
| 882 | 882 | } else if imm == 2 { |
|
| 883 | - | emit::emit(&mut *s.e, encode::slli(rd, rs1, 1)); |
|
| 883 | + | emit::emit(s.e, encode::slli(rd, rs1, 1)); |
|
| 884 | 884 | return; |
|
| 885 | 885 | } else if imm == 4 { |
|
| 886 | - | emit::emit(&mut *s.e, encode::slli(rd, rs1, 2)); |
|
| 886 | + | emit::emit(s.e, encode::slli(rd, rs1, 2)); |
|
| 887 | 887 | return; |
|
| 888 | 888 | } else if imm == 8 { |
|
| 889 | - | emit::emit(&mut *s.e, encode::slli(rd, rs1, 3)); |
|
| 889 | + | emit::emit(s.e, encode::slli(rd, rs1, 3)); |
|
| 890 | 890 | return; |
|
| 891 | 891 | } |
|
| 892 | 892 | } |
|
| 893 | 893 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 894 | - | emit::emit(&mut *s.e, |
|
| 894 | + | emit::emit(s.e, |
|
| 895 | 895 | encode::mulw(rd, rs1, rs2) |
|
| 896 | 896 | if typ == il::Type::W32 else |
|
| 897 | 897 | encode::mul(rd, rs1, rs2)); |
|
| 898 | 898 | } |
|
| 899 | 899 | case il::BinOp::Sdiv => { |
|
| 900 | 900 | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 901 | - | emit::emit(&mut *s.e, |
|
| 901 | + | emit::emit(s.e, |
|
| 902 | 902 | encode::divw(rd, rs1, rs2) |
|
| 903 | 903 | if typ == il::Type::W32 else |
|
| 904 | 904 | encode::div(rd, rs1, rs2)); |
|
| 905 | 905 | } |
|
| 906 | 906 | case il::BinOp::Udiv => { |
|
| 907 | 907 | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 908 | - | emit::emit(&mut *s.e, |
|
| 908 | + | emit::emit(s.e, |
|
| 909 | 909 | encode::divuw(rd, rs1, rs2) |
|
| 910 | 910 | if typ == il::Type::W32 else |
|
| 911 | 911 | encode::divu(rd, rs1, rs2)); |
|
| 912 | 912 | } |
|
| 913 | 913 | case il::BinOp::Srem => { |
|
| 914 | 914 | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 915 | - | emit::emit(&mut *s.e, |
|
| 915 | + | emit::emit(s.e, |
|
| 916 | 916 | encode::remw(rd, rs1, rs2) |
|
| 917 | 917 | if typ == il::Type::W32 else |
|
| 918 | 918 | encode::rem(rd, rs1, rs2)); |
|
| 919 | 919 | } |
|
| 920 | 920 | case il::BinOp::Urem => { |
|
| 921 | 921 | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 922 | - | emit::emit(&mut *s.e, |
|
| 922 | + | emit::emit(s.e, |
|
| 923 | 923 | encode::remuw(rd, rs1, rs2) |
|
| 924 | 924 | if typ == il::Type::W32 else |
|
| 925 | 925 | encode::remu(rd, rs1, rs2)); |
|
| 926 | 926 | } |
|
| 927 | 927 | case il::BinOp::And => |
| 937 | 937 | case il::BinOp::Ushr => |
|
| 938 | 938 | selectShift(s, rd, rs1, b, ShiftOp::Srl, typ, super::SCRATCH2), |
|
| 939 | 939 | case il::BinOp::Eq, il::BinOp::Ne => { |
|
| 940 | 940 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 941 | 941 | let useSext = cmpUsesSext(typ, false); |
|
| 942 | - | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 942 | + | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 943 | 943 | if not isExtendedImm(b, typ, useSext) { |
|
| 944 | - | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 944 | + | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 945 | 945 | } |
|
| 946 | - | emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2)); |
|
| 946 | + | emit::emit(s.e, encode::xor(rd, rs1, rs2)); |
|
| 947 | 947 | if let case il::BinOp::Eq = op { |
|
| 948 | - | emit::emit(&mut *s.e, encode::sltiu(rd, rd, 1)); |
|
| 948 | + | emit::emit(s.e, encode::sltiu(rd, rd, 1)); |
|
| 949 | 949 | } else { |
|
| 950 | - | emit::emit(&mut *s.e, encode::sltu(rd, super::ZERO, rd)); |
|
| 950 | + | emit::emit(s.e, encode::sltu(rd, super::ZERO, rd)); |
|
| 951 | 951 | } |
|
| 952 | 952 | } |
|
| 953 | 953 | case il::BinOp::Slt => |
|
| 954 | 954 | selectCmp(s, typ, rd, rs1, b, CmpOp::Slt, false, super::SCRATCH2), |
|
| 955 | 955 | case il::BinOp::Ult => |
| 964 | 964 | /// Select a unary ALU operation. |
|
| 965 | 965 | unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) { |
|
| 966 | 966 | match op { |
|
| 967 | 967 | case il::UnOp::Neg => { |
|
| 968 | 968 | if typ == il::Type::W32 { |
|
| 969 | - | emit::emit(&mut *s.e, encode::subw(rd, super::ZERO, rs)); |
|
| 969 | + | emit::emit(s.e, encode::subw(rd, super::ZERO, rs)); |
|
| 970 | 970 | } else { |
|
| 971 | - | emit::emit(&mut *s.e, encode::neg(rd, rs)); |
|
| 971 | + | emit::emit(s.e, encode::neg(rd, rs)); |
|
| 972 | 972 | } |
|
| 973 | 973 | } |
|
| 974 | 974 | case il::UnOp::Not => |
|
| 975 | - | emit::emit(&mut *s.e, encode::not_(rd, rs)), |
|
| 975 | + | emit::emit(s.e, encode::not_(rd, rs)), |
|
| 976 | 976 | } |
|
| 977 | 977 | } |
|
| 978 | 978 | ||
| 979 | 979 | /// Select binary operation with immediate optimization. |
|
| 980 | 980 | unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) { |
|
| 981 | 981 | // Try immediate optimization first. |
|
| 982 | 982 | if let case il::Val::Imm(imm) = b { |
|
| 983 | 983 | if encode::isSmallImm64(imm) { |
|
| 984 | 984 | let simm = imm as i32; |
|
| 985 | 985 | match op { |
|
| 986 | - | case BinOp::Add => emit::emit(&mut *s.e, encode::addi(rd, rs1, simm)), |
|
| 987 | - | case BinOp::And => emit::emit(&mut *s.e, encode::andi(rd, rs1, simm)), |
|
| 988 | - | case BinOp::Or => emit::emit(&mut *s.e, encode::ori(rd, rs1, simm)), |
|
| 989 | - | case BinOp::Xor => emit::emit(&mut *s.e, encode::xori(rd, rs1, simm)), |
|
| 986 | + | case BinOp::Add => emit::emit(s.e, encode::addi(rd, rs1, simm)), |
|
| 987 | + | case BinOp::And => emit::emit(s.e, encode::andi(rd, rs1, simm)), |
|
| 988 | + | case BinOp::Or => emit::emit(s.e, encode::ori(rd, rs1, simm)), |
|
| 989 | + | case BinOp::Xor => emit::emit(s.e, encode::xori(rd, rs1, simm)), |
|
| 990 | 990 | } |
|
| 991 | 991 | return; |
|
| 992 | 992 | } |
|
| 993 | 993 | } |
|
| 994 | 994 | // Fallback: load into register. |
|
| 995 | 995 | let rs2 = resolveVal(s, scratch, b); |
|
| 996 | 996 | match op { |
|
| 997 | - | case BinOp::Add => emit::emit(&mut *s.e, encode::add(rd, rs1, rs2)), |
|
| 998 | - | case BinOp::And => emit::emit(&mut *s.e, encode::and_(rd, rs1, rs2)), |
|
| 999 | - | case BinOp::Or => emit::emit(&mut *s.e, encode::or_(rd, rs1, rs2)), |
|
| 1000 | - | case BinOp::Xor => emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2)), |
|
| 997 | + | case BinOp::Add => emit::emit(s.e, encode::add(rd, rs1, rs2)), |
|
| 998 | + | case BinOp::And => emit::emit(s.e, encode::and_(rd, rs1, rs2)), |
|
| 999 | + | case BinOp::Or => emit::emit(s.e, encode::or_(rd, rs1, rs2)), |
|
| 1000 | + | case BinOp::Xor => emit::emit(s.e, encode::xor(rd, rs1, rs2)), |
|
| 1001 | 1001 | } |
|
| 1002 | 1002 | } |
|
| 1003 | 1003 | ||
| 1004 | 1004 | /// Select shift operation with immediate optimization. |
|
| 1005 | 1005 | /// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits |
| 1013 | 1013 | // Otherwise fall back to register shifts, which naturally mask the count. |
|
| 1014 | 1014 | if shamt >= 0 and ((isW32 and shamt < 32) or (not isW32 and shamt < 64)) { |
|
| 1015 | 1015 | let sa = shamt as i32; |
|
| 1016 | 1016 | if isW32 { |
|
| 1017 | 1017 | match op { |
|
| 1018 | - | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slliw(rd, rs1, sa)), |
|
| 1019 | - | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srliw(rd, rs1, sa)), |
|
| 1020 | - | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraiw(rd, rs1, sa)), |
|
| 1018 | + | case ShiftOp::Sll => emit::emit(s.e, encode::slliw(rd, rs1, sa)), |
|
| 1019 | + | case ShiftOp::Srl => emit::emit(s.e, encode::srliw(rd, rs1, sa)), |
|
| 1020 | + | case ShiftOp::Sra => emit::emit(s.e, encode::sraiw(rd, rs1, sa)), |
|
| 1021 | 1021 | } |
|
| 1022 | 1022 | } else { |
|
| 1023 | 1023 | match op { |
|
| 1024 | - | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slli(rd, rs1, sa)), |
|
| 1025 | - | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srli(rd, rs1, sa)), |
|
| 1026 | - | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::srai(rd, rs1, sa)), |
|
| 1024 | + | case ShiftOp::Sll => emit::emit(s.e, encode::slli(rd, rs1, sa)), |
|
| 1025 | + | case ShiftOp::Srl => emit::emit(s.e, encode::srli(rd, rs1, sa)), |
|
| 1026 | + | case ShiftOp::Sra => emit::emit(s.e, encode::srai(rd, rs1, sa)), |
|
| 1027 | 1027 | } |
|
| 1028 | 1028 | } |
|
| 1029 | 1029 | return; |
|
| 1030 | 1030 | } |
|
| 1031 | 1031 | } |
|
| 1032 | 1032 | // Fallback: load into register. |
|
| 1033 | 1033 | let rs2 = resolveVal(s, scratch, b); |
|
| 1034 | 1034 | if isW32 { |
|
| 1035 | 1035 | match op { |
|
| 1036 | - | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sllw(rd, rs1, rs2)), |
|
| 1037 | - | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srlw(rd, rs1, rs2)), |
|
| 1038 | - | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraw(rd, rs1, rs2)), |
|
| 1036 | + | case ShiftOp::Sll => emit::emit(s.e, encode::sllw(rd, rs1, rs2)), |
|
| 1037 | + | case ShiftOp::Srl => emit::emit(s.e, encode::srlw(rd, rs1, rs2)), |
|
| 1038 | + | case ShiftOp::Sra => emit::emit(s.e, encode::sraw(rd, rs1, rs2)), |
|
| 1039 | 1039 | } |
|
| 1040 | 1040 | } else { |
|
| 1041 | 1041 | match op { |
|
| 1042 | - | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sll(rd, rs1, rs2)), |
|
| 1043 | - | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srl(rd, rs1, rs2)), |
|
| 1044 | - | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sra(rd, rs1, rs2)), |
|
| 1042 | + | case ShiftOp::Sll => emit::emit(s.e, encode::sll(rd, rs1, rs2)), |
|
| 1043 | + | case ShiftOp::Srl => emit::emit(s.e, encode::srl(rd, rs1, rs2)), |
|
| 1044 | + | case ShiftOp::Sra => emit::emit(s.e, encode::sra(rd, rs1, rs2)), |
|
| 1045 | 1045 | } |
|
| 1046 | 1046 | } |
|
| 1047 | 1047 | } |
|
| 1048 | 1048 | ||
| 1049 | 1049 | /// Resolve parallel moves from IL values to physical destination registers. |
| 1166 | 1166 | /// Emit moves from block arguments to target block's parameter registers. |
|
| 1167 | 1167 | /// |
|
| 1168 | 1168 | /// Handles spilled destinations directly, then delegates to [`emitParallelMoves`] |
|
| 1169 | 1169 | /// for the remaining register-to-register parallel move resolution. Edges that |
|
| 1170 | 1170 | /// would overwrite an unconsumed spill source are unsupported. |
|
| 1171 | - | unsafe fn emitBlockArgs(s: &mut Selector, func: *il::Fn, target: u32, args: &[il::Val]) { |
|
| 1171 | + | unsafe fn emitBlockArgs(s: &mut Selector, func: *unsafe il::Fn, target: u32, args: &[il::Val]) { |
|
| 1172 | 1172 | if args.len == 0 { |
|
| 1173 | 1173 | return; |
|
| 1174 | 1174 | } |
|
| 1175 | 1175 | let block = &func.blocks[target]; |
|
| 1176 | 1176 | assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch"; |
| 1222 | 1222 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, param) { |
|
| 1223 | 1223 | if let case il::Val::Undef = arg { |
|
| 1224 | 1224 | // Undefined values don't need any move. |
|
| 1225 | 1225 | } else { |
|
| 1226 | 1226 | let rs = resolveVal(s, super::SCRATCH1, arg); |
|
| 1227 | - | emit::emitSd(&mut *s.e, rs, spillBase(s), spillOffset(s, slot)); |
|
| 1227 | + | emit::emitSd(s.e, rs, spillBase(s), spillOffset(s, slot)); |
|
| 1228 | 1228 | } |
|
| 1229 | 1229 | } else { |
|
| 1230 | 1230 | set dsts[i] = getReg(s, param); |
|
| 1231 | 1231 | } |
|
| 1232 | 1232 | } |
| 1247 | 1247 | let mut signed = false; |
|
| 1248 | 1248 | if let case CmpOp::Slt = op { |
|
| 1249 | 1249 | set signed = true; |
|
| 1250 | 1250 | } |
|
| 1251 | 1251 | let useSext = cmpUsesSext(typ, signed); |
|
| 1252 | - | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 1252 | + | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 1253 | 1253 | ||
| 1254 | 1254 | // Canonicalizing the immediate can expose an immediate instruction even |
|
| 1255 | 1255 | // when the IL value used a different representation for the same width. |
|
| 1256 | 1256 | let mut rhs = b; |
|
| 1257 | 1257 | if let case il::Val::Imm(imm) = b { |
|
| 1258 | 1258 | let canonical = canonicalCmpImm(imm, typ, useSext); |
|
| 1259 | 1259 | set rhs = il::Val::Imm(canonical); |
|
| 1260 | 1260 | if encode::isSmallImm64(canonical) { |
|
| 1261 | 1261 | let simm = canonical as i32; |
|
| 1262 | 1262 | match op { |
|
| 1263 | - | case CmpOp::Slt => emit::emit(&mut *s.e, encode::slti(rd, rs1, simm)), |
|
| 1264 | - | case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltiu(rd, rs1, simm)), |
|
| 1263 | + | case CmpOp::Slt => emit::emit(s.e, encode::slti(rd, rs1, simm)), |
|
| 1264 | + | case CmpOp::Ult => emit::emit(s.e, encode::sltiu(rd, rs1, simm)), |
|
| 1265 | 1265 | } |
|
| 1266 | 1266 | if invert { |
|
| 1267 | - | emit::emit(&mut *s.e, encode::xori(rd, rd, 1)); |
|
| 1267 | + | emit::emit(s.e, encode::xori(rd, rd, 1)); |
|
| 1268 | 1268 | } |
|
| 1269 | 1269 | return; |
|
| 1270 | 1270 | } |
|
| 1271 | 1271 | } |
|
| 1272 | 1272 | ||
| 1273 | 1273 | let rs2 = resolveVal(s, scratch, rhs); |
|
| 1274 | 1274 | if not isExtendedImm(rhs, typ, useSext) { |
|
| 1275 | - | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 1275 | + | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 1276 | 1276 | } |
|
| 1277 | 1277 | match op { |
|
| 1278 | - | case CmpOp::Slt => emit::emit(&mut *s.e, encode::slt(rd, rs1, rs2)), |
|
| 1279 | - | case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltu(rd, rs1, rs2)), |
|
| 1278 | + | case CmpOp::Slt => emit::emit(s.e, encode::slt(rd, rs1, rs2)), |
|
| 1279 | + | case CmpOp::Ult => emit::emit(s.e, encode::sltu(rd, rs1, rs2)), |
|
| 1280 | 1280 | } |
|
| 1281 | 1281 | if invert { |
|
| 1282 | - | emit::emit(&mut *s.e, encode::xori(rd, rd, 1)); |
|
| 1282 | + | emit::emit(s.e, encode::xori(rd, rd, 1)); |
|
| 1283 | 1283 | } |
|
| 1284 | 1284 | } |
lib/std/arch/rv64/tests.rad
+1 -1
| 21 | 21 | @test unsafe fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) { |
|
| 22 | 22 | let mut arena = alloc::new(&mut ASSEMBLY_ARENA_STORAGE[..]); |
|
| 23 | 23 | let symbols = try alloc::allocSlice(&mut arena, @sizeOf(asm::Symbol), @alignOf(asm::Symbol), 2) catch { |
|
| 24 | 24 | throw testing::TestError::Failed; |
|
| 25 | 25 | }; |
|
| 26 | - | let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2, 2); |
|
| 26 | + | let symbolSlice = symbols as *mut [asm::Symbol]; |
|
| 27 | 27 | set symbolSlice[0] = asm::Symbol { |
|
| 28 | 28 | name: "local", |
|
| 29 | 29 | section: asm::Section::Text, |
|
| 30 | 30 | offset: 0, |
|
| 31 | 31 | isExported: false, |
lib/std/collections/dict.rad
+1 -1
| 12 | 12 | /// Associated value. |
|
| 13 | 13 | value: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Open-addressed hash map with caller-provided storage. |
|
| 17 | - | export record Dict: Copy { |
|
| 17 | + | export record Dict { |
|
| 18 | 18 | /// Hash table entries. |
|
| 19 | 19 | entries: *mut [Entry], |
|
| 20 | 20 | /// Number of occupied entries. |
|
| 21 | 21 | count: u32, |
|
| 22 | 22 | } |
lib/std/lang/alloc.rad
+19 -2
| 16 | 16 | ||
| 17 | 17 | /// Bump allocator backed by a byte slice. |
|
| 18 | 18 | /// |
|
| 19 | 19 | /// Allocations are made by advancing an offset pointer. Individual allocations |
|
| 20 | 20 | /// cannot be freed; instead, the entire arena is reset at once via [`reset`]. |
|
| 21 | - | export record Arena: Copy { |
|
| 21 | + | export record Arena { |
|
| 22 | 22 | /// Backing storage. |
|
| 23 | 23 | data: *mut [u8], |
|
| 24 | 24 | /// Current allocation offset in bytes. |
|
| 25 | 25 | offset: u32, |
|
| 26 | 26 | } |
| 128 | 128 | } |
|
| 129 | 129 | ||
| 130 | 130 | /// Arena allocation function conforming to the `Allocator` interface. |
|
| 131 | 131 | unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque { |
|
| 132 | 132 | let arena = ctx as *unsafe mut Arena; |
|
| 133 | - | return try! alloc(&mut *arena, size, alignment); |
|
| 133 | + | return try! alloc(arena, size, alignment); |
|
| 134 | + | } |
|
| 135 | + | ||
| 136 | + | /// Allocate raw storage that remains valid until the arena is reset. |
|
| 137 | + | export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { |
|
| 138 | + | let owner = try alloc(arena, size, alignment); |
|
| 139 | + | let byte = owner as *mut u8; |
|
| 140 | + | let raw: *unsafe mut u8 = &mut *byte; |
|
| 141 | + | return raw as *unsafe mut opaque; |
|
| 142 | + | } |
|
| 143 | + | ||
| 144 | + | /// Allocate a raw slice that remains valid until the arena is reset. |
|
| 145 | + | export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { |
|
| 146 | + | if count == 0 { |
|
| 147 | + | return &mut []; |
|
| 148 | + | } |
|
| 149 | + | let raw = try allocRaw(arena, size * count, alignment); |
|
| 150 | + | return @sliceOf(raw, count); |
|
| 134 | 151 | } |
lib/std/lang/ast.rad
+27 -25
| 10 | 10 | export constant MAX_TRAIT_METHODS: u32 = 8; |
|
| 11 | 11 | ||
| 12 | 12 | /// Arena for all parser allocations. |
|
| 13 | 13 | /// |
|
| 14 | 14 | /// Uses a bump allocator for both AST nodes and node pointer arrays. |
|
| 15 | - | export record NodeArena: Copy { |
|
| 15 | + | export record NodeArena { |
|
| 16 | 16 | /// Bump allocator for all allocations. |
|
| 17 | 17 | arena: alloc::Arena, |
|
| 18 | 18 | /// Next node ID to assign. Incremented on each node allocation. |
|
| 19 | 19 | nextId: u32, |
|
| 20 | 20 | } |
| 32 | 32 | if capacity == 0 { |
|
| 33 | 33 | return &mut []; |
|
| 34 | 34 | } |
|
| 35 | 35 | let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity); |
|
| 36 | 36 | ||
| 37 | - | return @sliceOf(ptr.ptr as *mut *Node, 0, capacity); |
|
| 37 | + | let nodes = ptr as *mut [*Node]; |
|
| 38 | + | return &mut nodes[..0]; |
|
| 38 | 39 | } |
|
| 39 | 40 | ||
| 40 | 41 | /// Attribute bit set applied to declarations or fields. |
|
| 41 | 42 | export union Attribute: Copy { |
|
| 42 | 43 | /// Public visibility attribute. |
| 53 | 54 | Unsafe = 0b100000, |
|
| 54 | 55 | } |
|
| 55 | 56 | ||
| 56 | 57 | /// Ordered collection of attribute nodes applied to a declaration. |
|
| 57 | 58 | export record Attributes: Copy { |
|
| 58 | - | list: *mut [*Node], |
|
| 59 | + | /// Attribute nodes in declaration order. |
|
| 60 | + | list: *[*Node], |
|
| 59 | 61 | } |
|
| 60 | 62 | ||
| 61 | 63 | /// Check if an attributes list contains an attribute. |
|
| 62 | 64 | export fn attributesContains(self: &Attributes, attr: Attribute) -> bool { |
|
| 63 | 65 | for node in self.list { |
| 201 | 203 | /// Nominal type, points to identifier node. |
|
| 202 | 204 | Nominal(*Node), |
|
| 203 | 205 | /// Inline record type for union variant payloads. |
|
| 204 | 206 | Record { |
|
| 205 | 207 | /// Field declaration nodes. |
|
| 206 | - | fields: *mut [*Node], |
|
| 208 | + | fields: *[*Node], |
|
| 207 | 209 | /// Whether this record has labeled fields. |
|
| 208 | 210 | labeled: bool, |
|
| 209 | 211 | }, |
|
| 210 | 212 | /// Anonymous function type. |
|
| 211 | 213 | Fn { |
| 227 | 229 | } |
|
| 228 | 230 | ||
| 229 | 231 | /// Function signature. |
|
| 230 | 232 | export record FnSig: Copy { |
|
| 231 | 233 | /// Parameter type nodes in declaration order. |
|
| 232 | - | params: *mut [*Node], |
|
| 234 | + | params: *[*Node], |
|
| 233 | 235 | /// Optional return type node. |
|
| 234 | 236 | returnType: ?*Node, |
|
| 235 | 237 | /// Throwable type nodes declared in the signature. |
|
| 236 | - | throwList: *mut [*Node], |
|
| 238 | + | throwList: *[*Node], |
|
| 237 | 239 | } |
|
| 238 | 240 | ||
| 239 | 241 | /// Address-of expression metadata. |
|
| 240 | 242 | export record AddressOf: Copy { |
|
| 241 | 243 | /// Target expression being referenced. |
| 245 | 247 | } |
|
| 246 | 248 | ||
| 247 | 249 | /// Compound statement block with optional dedicated scope. |
|
| 248 | 250 | export record Block: Copy { |
|
| 249 | 251 | /// Statements that belong to this block. |
|
| 250 | - | statements: *mut [*Node], |
|
| 252 | + | statements: *[*Node], |
|
| 251 | 253 | /// Whether this block permits unsafe operations. |
|
| 252 | 254 | isUnsafe: bool, |
|
| 253 | 255 | } |
|
| 254 | 256 | ||
| 255 | 257 | /// Function call expression. |
|
| 256 | 258 | export record Call: Copy { |
|
| 257 | 259 | /// Callee expression. |
|
| 258 | 260 | callee: *Node, |
|
| 259 | 261 | /// Argument expressions in source order. |
|
| 260 | - | args: *mut [*Node], |
|
| 262 | + | args: *[*Node], |
|
| 261 | 263 | } |
|
| 262 | 264 | ||
| 263 | 265 | /// Single argument to a function or record literal, optionally labeled. |
|
| 264 | 266 | export record Arg: Copy { |
|
| 265 | 267 | /// Optional label applied to the argument. |
| 299 | 301 | /// Try expression metadata. |
|
| 300 | 302 | export record Try: Copy { |
|
| 301 | 303 | /// Expression evaluated with implicit error propagation. |
|
| 302 | 304 | expr: *Node, |
|
| 303 | 305 | /// Catch clauses. Empty for propagation (`try`), `try!`, or `try?`. |
|
| 304 | - | catches: *mut [*Node], |
|
| 306 | + | catches: *[*Node], |
|
| 305 | 307 | /// Whether the try should panic instead of returning an error. |
|
| 306 | 308 | shouldPanic: bool, |
|
| 307 | 309 | /// Whether the try should return an optional instead of propagating error. |
|
| 308 | 310 | returnsOptional: bool, |
|
| 309 | 311 | } |
| 361 | 363 | } |
|
| 362 | 364 | ||
| 363 | 365 | /// Prong arm. |
|
| 364 | 366 | export union ProngArm: Copy { |
|
| 365 | 367 | /// Case arm with pattern list. |
|
| 366 | - | Case(*mut [*Node]), |
|
| 368 | + | Case(*[*Node]), |
|
| 367 | 369 | /// Binding arm with single identifier or placeholder. |
|
| 368 | 370 | Binding(*Node), |
|
| 369 | 371 | /// Else arm. |
|
| 370 | 372 | Else, |
|
| 371 | 373 | } |
| 405 | 407 | /// `match` statement metadata. |
|
| 406 | 408 | export record Match: Copy { |
|
| 407 | 409 | /// Expression whose value controls the match. |
|
| 408 | 410 | subject: *Node, |
|
| 409 | 411 | /// Prong nodes evaluated in order. |
|
| 410 | - | prongs: *mut [*Node], |
|
| 412 | + | prongs: *[*Node], |
|
| 411 | 413 | } |
|
| 412 | 414 | ||
| 413 | 415 | /// `match` prong metadata. |
|
| 414 | 416 | export record MatchProng: Copy { |
|
| 415 | 417 | /// Prong arm. |
| 470 | 472 | export record RecordLit: Copy { |
|
| 471 | 473 | /// Type name associated with the literal. |
|
| 472 | 474 | /// If `nil`, it's an anonymous record literal. |
|
| 473 | 475 | typeName: ?*Node, |
|
| 474 | 476 | /// Field initializer nodes. |
|
| 475 | - | fields: *mut [*Node], |
|
| 477 | + | fields: *[*Node], |
|
| 476 | 478 | /// When true, remaining fields are discarded (`{ x, .. }`). |
|
| 477 | 479 | ignoreRest: bool, |
|
| 478 | 480 | } |
|
| 479 | 481 | ||
| 480 | 482 | /// Record declaration. |
|
| 481 | 483 | export record RecordDecl: Copy { |
|
| 482 | 484 | /// Identifier naming the record. |
|
| 483 | 485 | name: *Node, |
|
| 484 | 486 | /// Field declaration nodes. |
|
| 485 | - | fields: *mut [*Node], |
|
| 487 | + | fields: *[*Node], |
|
| 486 | 488 | /// Optional attribute list applied to the record. |
|
| 487 | 489 | attrs: ?Attributes, |
|
| 488 | 490 | /// Trait derivations attached to the record. |
|
| 489 | - | derives: *mut [*Node], |
|
| 491 | + | derives: *[*Node], |
|
| 490 | 492 | /// Whether this record has labeled fields. |
|
| 491 | 493 | labeled: bool, |
|
| 492 | 494 | } |
|
| 493 | 495 | ||
| 494 | 496 | /// Union declarations. |
|
| 495 | 497 | export record UnionDecl: Copy { |
|
| 496 | 498 | /// Identifier naming the union. |
|
| 497 | 499 | name: *Node, |
|
| 498 | 500 | /// Variant nodes making up the union. |
|
| 499 | - | variants: *mut [*Node], |
|
| 501 | + | variants: *[*Node], |
|
| 500 | 502 | /// Optional attribute list applied to the union. |
|
| 501 | 503 | attrs: ?Attributes, |
|
| 502 | 504 | /// Trait derivations attached to the union. |
|
| 503 | - | derives: *mut [*Node], |
|
| 505 | + | derives: *[*Node], |
|
| 504 | 506 | } |
|
| 505 | 507 | ||
| 506 | 508 | /// Union variant declaration. |
|
| 507 | 509 | export record UnionDeclVariant: Copy { |
|
| 508 | 510 | /// Identifier naming the variant. |
| 614 | 616 | /// Numeric literal such as `42` or `0xFF`. |
|
| 615 | 617 | Number(fmt::IntLiteral), |
|
| 616 | 618 | /// Range expression such as `0..10` or `..`. |
|
| 617 | 619 | Range(Range), |
|
| 618 | 620 | /// Array literal expression. |
|
| 619 | - | ArrayLit(*mut [*Node]), |
|
| 621 | + | ArrayLit(*[*Node]), |
|
| 620 | 622 | /// Array repeat literal expression. |
|
| 621 | 623 | ArrayRepeatLit(ArrayRepeatLit), |
|
| 622 | 624 | /// Array subscript expression. |
|
| 623 | 625 | Subscript { |
|
| 624 | 626 | /// Array or slice. |
| 633 | 635 | /// Builtin function call (e.g. `@sizeOf(T)`). |
|
| 634 | 636 | BuiltinCall { |
|
| 635 | 637 | /// Builtin function kind. |
|
| 636 | 638 | kind: Builtin, |
|
| 637 | 639 | /// Argument list. |
|
| 638 | - | args: *mut [*Node], |
|
| 640 | + | args: *[*Node], |
|
| 639 | 641 | }, |
|
| 640 | 642 | /// Block expression or statement body. |
|
| 641 | 643 | Block(Block), |
|
| 642 | 644 | /// Call expression, eg. `f(x)`. |
|
| 643 | 645 | Call(Call), |
| 755 | 757 | /// Trait declaration. |
|
| 756 | 758 | TraitDecl { |
|
| 757 | 759 | /// Trait name identifier. |
|
| 758 | 760 | name: *Node, |
|
| 759 | 761 | /// Supertrait name nodes. |
|
| 760 | - | supertraits: *mut [*Node], |
|
| 762 | + | supertraits: *[*Node], |
|
| 761 | 763 | /// Method signature nodes ([`TraitMethodSig`]). |
|
| 762 | - | methods: *mut [*Node], |
|
| 764 | + | methods: *[*Node], |
|
| 763 | 765 | /// Optional attributes. |
|
| 764 | 766 | attrs: ?Attributes, |
|
| 765 | 767 | }, |
|
| 766 | 768 | /// Method signature inside a trait declaration. |
|
| 767 | 769 | TraitMethodSig { |
| 779 | 781 | /// Trait name identifier. |
|
| 780 | 782 | traitName: *Node, |
|
| 781 | 783 | /// Target type identifier. |
|
| 782 | 784 | targetType: *Node, |
|
| 783 | 785 | /// Method definition nodes ([`MethodDecl`]). |
|
| 784 | - | methods: *mut [*Node], |
|
| 786 | + | methods: *[*Node], |
|
| 785 | 787 | }, |
|
| 786 | 788 | /// Method definition with a receiver. |
|
| 787 | 789 | /// Used both inside `instance` blocks and as standalone methods. |
|
| 788 | 790 | MethodDecl { |
|
| 789 | 791 | /// Method name identifier. |
| 851 | 853 | fnBody: *Node |
|
| 852 | 854 | } |
|
| 853 | 855 | ||
| 854 | 856 | /// Synthesize a module with a function in it with the given name and statements. |
|
| 855 | 857 | export unsafe fn synthFnModule( |
|
| 856 | - | arena: &mut NodeArena, name: *[u8], bodyStmts: *mut [*Node] |
|
| 858 | + | arena: &mut NodeArena, name: *[u8], bodyStmts: *[*Node] |
|
| 857 | 859 | ) -> SynthFnMod { |
|
| 858 | 860 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 859 | 861 | let fnName = synthNode(arena, NodeValue::Ident(name)); |
|
| 860 | - | let params: *mut [*Node] = &mut []; |
|
| 861 | - | let throwList: *mut [*Node] = &mut []; |
|
| 862 | + | let params: *[*Node] = &[]; |
|
| 863 | + | let throwList: *[*Node] = &[]; |
|
| 862 | 864 | let fnSig = FnSig { params, returnType: nil, throwList }; |
|
| 863 | - | let fnBody = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false })); |
|
| 865 | + | let fnBody: *Node = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false })); |
|
| 864 | 866 | let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl { |
|
| 865 | 867 | name: fnName, sig: fnSig, body: fnBody, attrs: nil, |
|
| 866 | 868 | })); |
|
| 867 | 869 | let mut rootStmts: *mut [*Node] = &mut []; |
|
| 868 | 870 | rootStmts.append(fnDecl, a); |
lib/std/lang/ast/printer.rad
+21 -21
| 104 | 104 | } |
|
| 105 | 105 | case super::TypeSig::Optional { valueType } => |
|
| 106 | 106 | return sexpr::list(a, "?", &[toExpr(a, valueType)]), |
|
| 107 | 107 | case super::TypeSig::Nominal(name) => return toExpr(a, name), |
|
| 108 | 108 | case super::TypeSig::Record { fields, .. } => |
|
| 109 | - | return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])), |
|
| 109 | + | return sexpr::list(a, "record", nodeListToExprs(a, fields)), |
|
| 110 | 110 | case super::TypeSig::Fn { sig, isUnsafe } => { |
|
| 111 | 111 | let mut ret = sexpr::sym("void"); |
|
| 112 | 112 | if let rt = sig.returnType { |
|
| 113 | 113 | set ret = toExpr(a, rt); |
|
| 114 | 114 | } |
|
| 115 | 115 | let head = "unsafe-fn" if isUnsafe else "fn"; |
|
| 116 | - | return sexpr::list(a, head, &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]); |
|
| 116 | + | return sexpr::list(a, head, &[sexpr::list(a, "params", nodeListToExprs(a, sig.params)), ret]); |
|
| 117 | 117 | } |
|
| 118 | 118 | case super::TypeSig::TraitObject { class, traitName, mutable } => { |
|
| 119 | 119 | let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj"); |
|
| 120 | 120 | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable |
|
| 121 | 121 | else sexpr::list(a, head, &[toExpr(a, traitName)]); |
|
| 122 | 122 | } |
|
| 123 | 123 | } |
|
| 124 | 124 | } |
|
| 125 | 125 | ||
| 126 | 126 | /// Convert a node slice to a slice of expressions. |
|
| 127 | - | unsafe fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 127 | + | unsafe fn nodeListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] { |
|
| 128 | 128 | if nodes.len == 0 { |
|
| 129 | 129 | return &[]; |
|
| 130 | 130 | } |
|
| 131 | 131 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 132 | 132 | for node, i in nodes { |
| 137 | 137 | ||
| 138 | 138 | /// Convert optional attributes to an attribute list expression. |
|
| 139 | 139 | unsafe fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr { |
|
| 140 | 140 | let mut exprs: *[sexpr::Expr] = &[]; |
|
| 141 | 141 | if let list = attrs { |
|
| 142 | - | set exprs = nodeListToExprs(a, &list.list[..]); |
|
| 142 | + | set exprs = nodeListToExprs(a, list.list); |
|
| 143 | 143 | } |
|
| 144 | 144 | return sexpr::list(a, "attrs", exprs); |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Convert an optional node to an expression, or return placeholder. |
| 167 | 167 | } |
|
| 168 | 168 | return sexpr::Expr::Null; |
|
| 169 | 169 | } |
|
| 170 | 170 | ||
| 171 | 171 | /// Convert a list of match prongs to expressions. |
|
| 172 | - | unsafe fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 172 | + | unsafe fn prongListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] { |
|
| 173 | 173 | if nodes.len == 0 { |
|
| 174 | 174 | return &[]; |
|
| 175 | 175 | } |
|
| 176 | 176 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 177 | 177 | for prong, i in nodes { |
| 190 | 190 | /// Convert a match prong to an S-expression. |
|
| 191 | 191 | unsafe fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr { |
|
| 192 | 192 | match p.arm { |
|
| 193 | 193 | case super::ProngArm::Case(patterns) => { |
|
| 194 | 194 | return sexpr::block(a, "case", &[ |
|
| 195 | - | sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])), |
|
| 195 | + | sexpr::list(a, "patterns", nodeListToExprs(a, patterns)), |
|
| 196 | 196 | guardExpr(a, p.guard) |
|
| 197 | 197 | ], &[toExpr(a, p.body)]); |
|
| 198 | 198 | } |
|
| 199 | 199 | case super::ProngArm::Else => { |
|
| 200 | 200 | return sexpr::block(a, "else", &[guardExpr(a, p.guard)], &[toExpr(a, p.body)]); |
| 217 | 217 | ) -> sexpr::Expr { |
|
| 218 | 218 | return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]); |
|
| 219 | 219 | } |
|
| 220 | 220 | ||
| 221 | 221 | /// Convert a list of record fields to expressions. |
|
| 222 | - | unsafe fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 222 | + | unsafe fn fieldListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] { |
|
| 223 | 223 | if nodes.len == 0 { |
|
| 224 | 224 | return &[]; |
|
| 225 | 225 | } |
|
| 226 | 226 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 227 | 227 | for node, i in nodes { |
| 241 | 241 | unsafe fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr { |
|
| 242 | 242 | return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]); |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | 245 | /// Convert a list of union variants to expressions. |
|
| 246 | - | unsafe fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 246 | + | unsafe fn variantListToExprs(a: &mut alloc::Arena, nodes: &[*super::Node]) -> *[sexpr::Expr] { |
|
| 247 | 247 | if nodes.len == 0 { |
|
| 248 | 248 | return &[]; |
|
| 249 | 249 | } |
|
| 250 | 250 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 251 | 251 | for node, i in nodes { |
| 291 | 291 | set buf[0] = toExpr(a, c.callee); |
|
| 292 | 292 | for arg, i in c.args { set buf[i + 1] = toExpr(a, arg); } |
|
| 293 | 293 | return sexpr::Expr::List { head: "call", tail: buf, multiline: false }; |
|
| 294 | 294 | } |
|
| 295 | 295 | case super::NodeValue::BuiltinCall { kind, args } => |
|
| 296 | - | return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args[..])), |
|
| 296 | + | return sexpr::list(a, builtinName(kind), nodeListToExprs(a, args)), |
|
| 297 | 297 | case super::NodeValue::Subscript { container, index } => |
|
| 298 | 298 | return sexpr::list(a, "[]", &[toExpr(a, container), toExpr(a, index)]), |
|
| 299 | 299 | case super::NodeValue::FieldAccess(acc) => |
|
| 300 | 300 | return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]), |
|
| 301 | 301 | case super::NodeValue::ScopeAccess(acc) => |
| 306 | 306 | case super::NodeValue::Deref(target) => |
|
| 307 | 307 | return sexpr::list(a, "deref", &[toExpr(a, target)]), |
|
| 308 | 308 | case super::NodeValue::As(cast) => |
|
| 309 | 309 | return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]), |
|
| 310 | 310 | case super::NodeValue::ArrayLit(elems) => |
|
| 311 | - | return sexpr::list(a, "array", nodeListToExprs(a, &elems[..])), |
|
| 311 | + | return sexpr::list(a, "array", nodeListToExprs(a, elems)), |
|
| 312 | 312 | case super::NodeValue::ArrayRepeatLit(rep) => |
|
| 313 | 313 | return sexpr::list(a, "array-repeat", &[toExpr(a, rep.item), toExpr(a, rep.count)]), |
|
| 314 | 314 | case super::NodeValue::RecordLit(lit) => { |
|
| 315 | 315 | let mut total: u32 = lit.fields.len as u32; |
|
| 316 | 316 | if let _ = lit.typeName { |
| 343 | 343 | } |
|
| 344 | 344 | case super::NodeValue::Try(t) => { |
|
| 345 | 345 | let mut head = "try"; |
|
| 346 | 346 | if t.shouldPanic { set head = "try!"; } |
|
| 347 | 347 | if t.catches.len > 0 { |
|
| 348 | - | let catches = nodeListToExprs(a, &t.catches[..]); |
|
| 348 | + | let catches = nodeListToExprs(a, t.catches); |
|
| 349 | 349 | return sexpr::list(a, head, &[toExpr(a, t.expr), sexpr::block(a, "catches", &[], catches)]); |
|
| 350 | 350 | } |
|
| 351 | 351 | return sexpr::list(a, head, &[toExpr(a, t.expr)]); |
|
| 352 | 352 | } |
|
| 353 | 353 | case super::NodeValue::CatchClause(clause) => { |
| 365 | 365 | set children[len] = toExpr(a, clause.body); |
|
| 366 | 366 | set len += 1; |
|
| 367 | 367 | return sexpr::list(a, head, &children[..len]); |
|
| 368 | 368 | } |
|
| 369 | 369 | case super::NodeValue::Block(blk) => { |
|
| 370 | - | let children = nodeListToExprs(a, &blk.statements[..]); |
|
| 370 | + | let children = nodeListToExprs(a, blk.statements); |
|
| 371 | 371 | let name = "unsafe" if blk.isUnsafe else "block"; |
|
| 372 | 372 | return sexpr::block(a, name, &[], children); |
|
| 373 | 373 | } |
|
| 374 | 374 | case super::NodeValue::Let(decl) => { |
|
| 375 | 375 | let mut head = "let"; |
| 441 | 441 | toExpr(a, f.iterable) |
|
| 442 | 442 | ], &[toExpr(a, f.body), toExprOrNull(a, f.elseBranch)]), |
|
| 443 | 443 | case super::NodeValue::Loop { body } => |
|
| 444 | 444 | return sexpr::block(a, "loop", &[], &[toExpr(a, body)]), |
|
| 445 | 445 | case super::NodeValue::Match(m) => { |
|
| 446 | - | let children = prongListToExprs(a, &m.prongs[..]); |
|
| 446 | + | let children = prongListToExprs(a, m.prongs); |
|
| 447 | 447 | return sexpr::block(a, "match", &[toExpr(a, m.subject)], children); |
|
| 448 | 448 | } |
|
| 449 | 449 | case super::NodeValue::MatchProng(p) => { |
|
| 450 | 450 | return prongToExpr(a, p); |
|
| 451 | 451 | } |
|
| 452 | 452 | case super::NodeValue::FnDecl(f) => { |
|
| 453 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &f.sig.params[..])); |
|
| 453 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, f.sig.params)); |
|
| 454 | 454 | let ret = toExprOrNull(a, f.sig.returnType); |
|
| 455 | 455 | if let body = f.body { |
|
| 456 | 456 | return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]); |
|
| 457 | 457 | } |
|
| 458 | 458 | return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]); |
|
| 459 | 459 | } |
|
| 460 | 460 | case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]), |
|
| 461 | 461 | case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]), |
|
| 462 | 462 | case super::NodeValue::RecordDecl(r) => { |
|
| 463 | - | let children = fieldListToExprs(a, &r.fields[..]); |
|
| 463 | + | let children = fieldListToExprs(a, r.fields); |
|
| 464 | 464 | return sexpr::block(a, "record", &[toExpr(a, r.name)], children); |
|
| 465 | 465 | } |
|
| 466 | 466 | case super::NodeValue::RecordField { field, type, value } => { |
|
| 467 | 467 | return fieldToExpr(a, field, type, value); |
|
| 468 | 468 | } |
|
| 469 | 469 | case super::NodeValue::UnionDecl(u_) => { |
|
| 470 | - | let children = variantListToExprs(a, &u_.variants[..]); |
|
| 470 | + | let children = variantListToExprs(a, u_.variants); |
|
| 471 | 471 | return sexpr::block(a, "union", &[toExpr(a, u_.name)], children); |
|
| 472 | 472 | } |
|
| 473 | 473 | case super::NodeValue::UnionDeclVariant(v) => { |
|
| 474 | 474 | return variantToExpr(a, v.name, v.type); |
|
| 475 | 475 | } |
|
| 476 | 476 | case super::NodeValue::ExprStmt(e) => return toExpr(a, e), |
|
| 477 | 477 | case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => { |
|
| 478 | - | let children = nodeListToExprs(a, &methods[..]); |
|
| 479 | - | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits[..])); |
|
| 478 | + | let children = nodeListToExprs(a, methods); |
|
| 479 | + | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, supertraits)); |
|
| 480 | 480 | return sexpr::block(a, "trait", &[toExpr(a, name), supers], children); |
|
| 481 | 481 | } |
|
| 482 | 482 | case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => { |
|
| 483 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 483 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params)); |
|
| 484 | 484 | let ret = toExprOrNull(a, sig.returnType); |
|
| 485 | 485 | let attributes = attributesToExpr(a, attrs); |
|
| 486 | 486 | return sexpr::list( |
|
| 487 | 487 | a, |
|
| 488 | 488 | "methodSig", |
|
| 489 | 489 | &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret], |
|
| 490 | 490 | ); |
|
| 491 | 491 | } |
|
| 492 | 492 | case super::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 493 | - | let children = nodeListToExprs(a, &methods[..]); |
|
| 493 | + | let children = nodeListToExprs(a, methods); |
|
| 494 | 494 | return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children); |
|
| 495 | 495 | } |
|
| 496 | 496 | case super::NodeValue::MethodDecl { |
|
| 497 | 497 | name, receiverName, receiverType, sig, body, attrs, |
|
| 498 | 498 | } => { |
|
| 499 | - | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 499 | + | let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params)); |
|
| 500 | 500 | let ret = toExprOrNull(a, sig.returnType); |
|
| 501 | 501 | let attributes = attributesToExpr(a, attrs); |
|
| 502 | 502 | return sexpr::block( |
|
| 503 | 503 | a, |
|
| 504 | 504 | "method", |
lib/std/lang/gen/bitset.rad
+3 -2
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Create a new bitset backed by the given zero-initialized storage. |
|
| 31 | 31 | /// The storage must outlive the bitset and its iterators. |
|
| 32 | 32 | export unsafe fn new(bits: &mut [u32]) -> Bitset { |
|
| 33 | - | return Bitset { bits: bits as *unsafe mut [u32], len: bits.len * 32 }; |
|
| 33 | + | let len = bits.len * 32; |
|
| 34 | + | return Bitset { bits: bits as *unsafe mut [u32], len }; |
|
| 34 | 35 | } |
|
| 35 | 36 | ||
| 36 | 37 | /// Create a new bitset backed by the given storage, zeroing it first. |
|
| 37 | 38 | /// The storage must outlive the bitset and its iterators. |
|
| 38 | 39 | export unsafe fn init(bits: &mut [u32]) -> Bitset { |
| 43 | 44 | } |
|
| 44 | 45 | ||
| 45 | 46 | /// Create a bitset from arena allocation. |
|
| 46 | 47 | export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 47 | 48 | let numWords = wordsFor(len); |
|
| 48 | - | let bits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *mut [u32]; |
|
| 49 | + | let bits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *unsafe mut [u32]; |
|
| 49 | 50 | ||
| 50 | 51 | return init(bits); |
|
| 51 | 52 | } |
|
| 52 | 53 | ||
| 53 | 54 | /// Set bit `n` in the bitset. |
lib/std/lang/gen/data.rad
+12 -12
| 22 | 22 | /// Absolute address, including data base address. |
|
| 23 | 23 | addr: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Hash-indexed data symbol map. |
|
| 27 | - | export record DataSymMap: Copy { |
|
| 27 | + | export record DataSymMap { |
|
| 28 | 28 | /// Underlying hash table. |
|
| 29 | 29 | dict: dict::Dict, |
|
| 30 | 30 | /// Fallback linear array for edge cases. |
|
| 31 | 31 | syms: *[DataSym], |
|
| 32 | 32 | } |
| 34 | 34 | /// Lay out data symbols for a single section. |
|
| 35 | 35 | /// Data with sidecar image bytes is placed first, then zero-initialized data, |
|
| 36 | 36 | /// so that only meaningful bytes need to be written to the output file. |
|
| 37 | 37 | /// Returns the updated offset past all placed symbols. |
|
| 38 | 38 | export fn layoutSection( |
|
| 39 | - | items: *[il::Data], |
|
| 40 | - | syms: *mut [DataSym], |
|
| 39 | + | items: &[il::Data], |
|
| 40 | + | syms: &mut [DataSym], |
|
| 41 | 41 | count: &mut u32, |
|
| 42 | 42 | base: u32, |
|
| 43 | 43 | readOnly: bool |
|
| 44 | 44 | ) -> u32 { |
|
| 45 | 45 | return layoutSectionAtOffset(items, syms, count, base, 0, readOnly); |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Lay out data symbols for a single section starting at [`startOffset`]. |
|
| 49 | 49 | export fn layoutSectionAtOffset( |
|
| 50 | - | items: *[il::Data], |
|
| 51 | - | syms: *mut [DataSym], |
|
| 50 | + | items: &[il::Data], |
|
| 51 | + | syms: &mut [DataSym], |
|
| 52 | 52 | count: &mut u32, |
|
| 53 | 53 | base: u32, |
|
| 54 | 54 | startOffset: u32, |
|
| 55 | 55 | readOnly: bool |
|
| 56 | 56 | ) -> u32 { |
|
| 57 | 57 | let mut offset: u32 = startOffset; |
|
| 58 | 58 | ||
| 59 | 59 | // Data requiring sidecar image bytes first. |
|
| 60 | 60 | for i in 0..items.len { |
|
| 61 | - | let data = &items[i]; |
|
| 61 | + | let data = items[i]; |
|
| 62 | 62 | if data.readOnly == readOnly and not data.isZeroInit { |
|
| 63 | 63 | set offset = mem::alignUp(offset, data.alignment); |
|
| 64 | 64 | set syms[*count] = DataSym { name: data.name, addr: base + offset }; |
|
| 65 | 65 | set *count += 1; |
|
| 66 | 66 | set offset += data.size; |
|
| 67 | 67 | } |
|
| 68 | 68 | } |
|
| 69 | 69 | // Zero-initialized data after. |
|
| 70 | 70 | for i in 0..items.len { |
|
| 71 | - | let data = &items[i]; |
|
| 71 | + | let data = items[i]; |
|
| 72 | 72 | if data.readOnly == readOnly and data.isZeroInit { |
|
| 73 | 73 | set offset = mem::alignUp(offset, data.alignment); |
|
| 74 | 74 | set syms[*count] = DataSym { name: data.name, addr: base + offset }; |
|
| 75 | 75 | set *count += 1; |
|
| 76 | 76 | set offset += data.size; |
| 81 | 81 | ||
| 82 | 82 | /// Emit data bytes for a single section (read-only or read-write) into `buf`. |
|
| 83 | 83 | /// Iterates data requiring sidecar image bytes, serializing each data item. |
|
| 84 | 84 | /// Returns the total number of bytes written. |
|
| 85 | 85 | export unsafe fn emitSection( |
|
| 86 | - | items: *[il::Data], |
|
| 86 | + | items: &[il::Data], |
|
| 87 | 87 | dataSymMap: &DataSymMap, |
|
| 88 | 88 | fnLabels: &labels::Labels, |
|
| 89 | 89 | codeBase: u32, |
|
| 90 | - | buf: *mut [u8], |
|
| 90 | + | buf: &mut [u8], |
|
| 91 | 91 | readOnly: bool |
|
| 92 | 92 | ) -> u32 { |
|
| 93 | 93 | return emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0); |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | 96 | /// Emit data bytes for a single section starting at `startOffset`. |
|
| 97 | 97 | export unsafe fn emitSectionAtOffset( |
|
| 98 | - | items: *[il::Data], |
|
| 98 | + | items: &[il::Data], |
|
| 99 | 99 | dataSymMap: &DataSymMap, |
|
| 100 | 100 | fnLabels: &labels::Labels, |
|
| 101 | 101 | codeBase: u32, |
|
| 102 | - | buf: *mut [u8], |
|
| 102 | + | buf: &mut [u8], |
|
| 103 | 103 | readOnly: bool, |
|
| 104 | 104 | startOffset: u32 |
|
| 105 | 105 | ) -> u32 { |
|
| 106 | 106 | let mut offset: u32 = startOffset; |
|
| 107 | 107 | ||
| 108 | 108 | for i in 0..items.len { |
|
| 109 | - | let data = &items[i]; |
|
| 109 | + | let data = items[i]; |
|
| 110 | 110 | if data.readOnly == readOnly and not data.isZeroInit { |
|
| 111 | 111 | set offset = mem::alignUp(offset, data.alignment); |
|
| 112 | 112 | assert offset + data.size <= buf.len, "emitSectionAtOffset: buffer overflow"; |
|
| 113 | 113 | for j in 0..data.values.len { |
|
| 114 | 114 | let v = &data.values[j]; |
lib/std/lang/gen/labels.rad
+1 -1
| 10 | 10 | export constant MAX_FUNCS: u32 = 8192; |
|
| 11 | 11 | /// Size of the function hash table. Must be a power of two. |
|
| 12 | 12 | export constant FUNC_TABLE_SIZE: u32 = MAX_FUNCS * 2; |
|
| 13 | 13 | ||
| 14 | 14 | /// Label tracking for code emission. |
|
| 15 | - | export record Labels: Copy { |
|
| 15 | + | export record Labels { |
|
| 16 | 16 | /// Block offsets indexed by block index. |
|
| 17 | 17 | /// Per-function, reset each function. |
|
| 18 | 18 | blockOffsets: *mut [i32], |
|
| 19 | 19 | /// Number of blocks recorded in current function. |
|
| 20 | 20 | blockCount: u32, |
lib/std/lang/gen/regalloc.rad
+2 -2
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Complete register allocation result. |
|
| 38 | 38 | export record AllocResult: Copy { |
|
| 39 | 39 | /// SSA register to physical register mapping. |
|
| 40 | - | assignments: *[?super::Reg], |
|
| 40 | + | assignments: *unsafe [?super::Reg], |
|
| 41 | 41 | /// Spill slot information. |
|
| 42 | 42 | spill: spill::SpillInfo, |
|
| 43 | 43 | /// Bitmask of used callee-saved registers. |
|
| 44 | 44 | usedCalleeSaved: u32, |
|
| 45 | 45 | } |
| 47 | 47 | /// Run register allocation on a function. |
|
| 48 | 48 | /// |
|
| 49 | 49 | /// Returns a mapping from SSA registers to physical registers, plus |
|
| 50 | 50 | /// spill information. |
|
| 51 | 51 | export unsafe fn allocate( |
|
| 52 | - | func: *il::Fn, |
|
| 52 | + | func: *unsafe il::Fn, |
|
| 53 | 53 | config: &TargetConfig, |
|
| 54 | 54 | arena: &mut alloc::Arena |
|
| 55 | 55 | ) -> AllocResult throws (alloc::AllocError) { |
|
| 56 | 56 | // Phase 1: Liveness analysis. |
|
| 57 | 57 | let live = try liveness::analyze(func, arena); |
lib/std/lang/gen/regalloc/assign.rad
+18 -17
| 16 | 16 | /// Maximum number of active register mappings. |
|
| 17 | 17 | constant MAX_ACTIVE: u32 = 64; |
|
| 18 | 18 | ||
| 19 | 19 | /// Register mapping at a program point. |
|
| 20 | 20 | /// Maps SSA registers to physical registers. |
|
| 21 | - | export record RegMap: Copy { |
|
| 21 | + | export record RegMap { |
|
| 22 | 22 | /// SSA (virtual) registers that have mappings. |
|
| 23 | 23 | virtRegs: *mut [u32], |
|
| 24 | 24 | /// Physical register for each virtual register. |
|
| 25 | 25 | physRegs: *mut [gen::Reg], |
|
| 26 | 26 | /// Number of active mappings. |
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Register assignment result, per function. |
|
| 31 | 31 | export record AssignInfo: Copy { |
|
| 32 | 32 | /// SSA register -> physical register mapping. |
|
| 33 | - | assignments: *mut [?gen::Reg], |
|
| 33 | + | assignments: *unsafe mut [?gen::Reg], |
|
| 34 | 34 | /// Bitmask of used callee-saved registers. |
|
| 35 | 35 | usedCalleeSaved: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Per-instruction context for freeing and allocating register uses. |
|
| 39 | 39 | record InstrCtx: Copy { |
|
| 40 | 40 | current: *unsafe mut RegMap, |
|
| 41 | 41 | usedRegs: *unsafe mut bitset::Bitset, |
|
| 42 | 42 | /// Last operand-use index for each register used in the current block. |
|
| 43 | - | lastUse: *[u32], |
|
| 43 | + | lastUse: *unsafe [u32], |
|
| 44 | 44 | live: *unsafe liveness::LiveInfo, |
|
| 45 | 45 | blockIdx: u32, |
|
| 46 | 46 | instrIdx: u32, |
|
| 47 | 47 | allocatable: *[gen::Reg], |
|
| 48 | 48 | calleeSaved: *[gen::Reg], |
|
| 49 | - | assignments: *mut [?gen::Reg], |
|
| 49 | + | assignments: *unsafe mut [?gen::Reg], |
|
| 50 | 50 | spillInfo: *unsafe spill::SpillInfo, |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// Context for recording the last operand-use index in a block. |
|
| 54 | 54 | record LastUseCtx: Copy { |
|
| 55 | 55 | /// Per-register indices, shared by all blocks in the function. |
|
| 56 | - | lastUse: *mut [u32], |
|
| 56 | + | lastUse: *unsafe mut [u32], |
|
| 57 | 57 | /// Index of the instruction whose operands are being recorded. |
|
| 58 | 58 | index: u32, |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Compute register assignment. |
|
| 62 | 62 | export unsafe fn assign( |
|
| 63 | - | func: *il::Fn, |
|
| 63 | + | func: *unsafe il::Fn, |
|
| 64 | 64 | live: &liveness::LiveInfo, |
|
| 65 | 65 | spillInfo: &spill::SpillInfo, |
|
| 66 | 66 | config: &super::TargetConfig, |
|
| 67 | 67 | arena: &mut alloc::Arena |
|
| 68 | 68 | ) -> AssignInfo throws (alloc::AllocError) { |
| 76 | 76 | usedCalleeSaved: 0, |
|
| 77 | 77 | }; |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | // Allocate output structures. |
|
| 81 | - | let assignments = try alloc::allocSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *mut [?gen::Reg]; |
|
| 81 | + | let assignments = try alloc::allocRawSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *unsafe mut [?gen::Reg]; |
|
| 82 | 82 | for i in 0..maxReg { |
|
| 83 | 83 | set assignments[i] = nil; |
|
| 84 | 84 | } |
|
| 85 | 85 | // Reuse one last-use table for all blocks in the function. |
|
| 86 | - | let lastUse = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *mut [u32]; |
|
| 86 | + | let lastUse = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *unsafe mut [u32]; |
|
| 87 | 87 | // Pre-assign function parameters to argument registers. |
|
| 88 | 88 | // Cross-call params are NOT pre-assigned here; they will be allocated |
|
| 89 | 89 | // to callee-saved registers by the normal path, and isel emits moves |
|
| 90 | 90 | // from the arg register to the assigned register at function entry. |
|
| 91 | 91 | for param, i in func.params { |
| 94 | 94 | set assignments[param.value.n] = config.argRegs[i]; |
|
| 95 | 95 | } |
|
| 96 | 96 | } |
|
| 97 | 97 | } |
|
| 98 | 98 | // Allocate used registers bitset (32 physical registers per 32-bit word). |
|
| 99 | - | let usedRegsBits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *mut [u32]; |
|
| 99 | + | let usedRegsBits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *unsafe mut [u32]; |
|
| 100 | 100 | let mut usedRegs = bitset::init(usedRegsBits); |
|
| 101 | 101 | ||
| 102 | 102 | // Current register mapping. |
|
| 103 | 103 | let mut current = try createRegMap(arena); |
|
| 104 | 104 |
| 271 | 271 | panic "rallocReg: no free register, spilling fault"; |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Record the current index; forward traversal leaves the last operand use. |
|
| 275 | 275 | unsafe fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 276 | - | recordLastUse(reg, ctxPtr as &mut LastUseCtx); |
|
| 276 | + | let index = (ctxPtr as &mut LastUseCtx).index; |
|
| 277 | + | recordLastUse(reg, &mut (ctxPtr as &mut LastUseCtx).lastUse[..], index); |
|
| 277 | 278 | } |
|
| 278 | 279 | ||
| 279 | 280 | /// Record the last instruction that uses the register. |
|
| 280 | - | fn recordLastUse(reg: il::Reg, ctx: &mut LastUseCtx) { |
|
| 281 | - | set ctx.lastUse[reg.n] = ctx.index; |
|
| 281 | + | fn recordLastUse(reg: il::Reg, lastUse: &mut [u32], index: u32) { |
|
| 282 | + | set lastUse[reg.n] = index; |
|
| 282 | 283 | } |
|
| 283 | 284 | ||
| 284 | 285 | /// Free operands with no later block use or live-out use, then allocate missing uses. |
|
| 285 | 286 | unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 286 | 287 | processInstrReg(reg, ctxPtr as &mut InstrCtx); |
|
| 287 | 288 | } |
|
| 288 | 289 | ||
| 289 | 290 | /// Release expired registers and assign a register for this operand. |
|
| 290 | 291 | unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) { |
|
| 291 | 292 | if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) { |
|
| 292 | - | if let phys = rmapRemove(&mut *ctx.current, reg.n) { |
|
| 293 | - | bitset::clear(&mut *ctx.usedRegs, *phys as u32); |
|
| 293 | + | if let phys = rmapRemove(ctx.current, reg.n) { |
|
| 294 | + | bitset::clear(ctx.usedRegs, *phys as u32); |
|
| 294 | 295 | } |
|
| 295 | 296 | } |
|
| 296 | 297 | assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds"; |
|
| 297 | - | if spill::isSpilled(&*ctx.spillInfo, reg) { |
|
| 298 | + | if spill::isSpilled(ctx.spillInfo, reg) { |
|
| 298 | 299 | return; // Spilled values don't get physical registers. |
|
| 299 | 300 | } |
|
| 300 | 301 | if ctx.assignments[reg.n] == nil { |
|
| 301 | 302 | let current = ctx.current; |
|
| 302 | 303 | let usedRegs = ctx.usedRegs; |
|
| 303 | 304 | set ctx.assignments[reg.n] = rallocReg( |
|
| 304 | - | &mut *current, &mut *usedRegs, reg.n, ctx.allocatable, |
|
| 305 | - | ctx.calleeSaved, &*ctx.spillInfo |
|
| 305 | + | current, usedRegs, reg.n, ctx.allocatable, |
|
| 306 | + | ctx.calleeSaved, ctx.spillInfo |
|
| 306 | 307 | ); |
|
| 307 | 308 | } |
|
| 308 | 309 | } |
lib/std/lang/gen/regalloc/liveness.rad
+19 -19
| 33 | 33 | export constant MAX_SSA_REGS: u32 = 8192; |
|
| 34 | 34 | ||
| 35 | 35 | /// Liveness information for a function. |
|
| 36 | 36 | export record LiveInfo: Copy { |
|
| 37 | 37 | /// Per-block live-in sets (indexed by block index). |
|
| 38 | - | liveIn: *mut [bitset::Bitset], |
|
| 38 | + | liveIn: *unsafe mut [bitset::Bitset], |
|
| 39 | 39 | /// Per-block live-out sets (indexed by block index). |
|
| 40 | - | liveOut: *mut [bitset::Bitset], |
|
| 40 | + | liveOut: *unsafe mut [bitset::Bitset], |
|
| 41 | 41 | /// Per-block defs sets (registers defined in block). |
|
| 42 | - | defs: *mut [bitset::Bitset], |
|
| 42 | + | defs: *unsafe mut [bitset::Bitset], |
|
| 43 | 43 | /// Per-block uses sets (registers used before defined in block). |
|
| 44 | - | uses: *mut [bitset::Bitset], |
|
| 44 | + | uses: *unsafe mut [bitset::Bitset], |
|
| 45 | 45 | /// Number of blocks. |
|
| 46 | 46 | blockCount: u32, |
|
| 47 | 47 | /// Maximum register number used. |
|
| 48 | 48 | maxReg: u32, |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Context for collecting defs and uses during block analysis. |
|
| 52 | 52 | record DefsUses: Copy { |
|
| 53 | - | defs: *bitset::Bitset, |
|
| 54 | - | uses: *mut bitset::Bitset, |
|
| 53 | + | defs: *unsafe bitset::Bitset, |
|
| 54 | + | uses: *unsafe mut bitset::Bitset, |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Context for searching for a specific register in an instruction. |
|
| 58 | 58 | record FindCtx: Copy { |
|
| 59 | 59 | target: u32, |
|
| 60 | 60 | found: bool, |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Compute liveness by growing live sets until no live-in set changes. |
|
| 64 | - | export unsafe fn analyze(func: *il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 64 | + | export unsafe fn analyze(func: *unsafe il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 65 | 65 | let blockCount = func.blocks.len; |
|
| 66 | 66 | if blockCount == 0 { |
|
| 67 | 67 | return LiveInfo { |
|
| 68 | 68 | liveIn: &mut [], |
|
| 69 | 69 | liveOut: &mut [], |
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | assert maxReg <= MAX_SSA_REGS, "analyze: maximum SSA registers exceeded"; |
|
| 95 | 95 | // Allocate per-block bitsets. |
|
| 96 | - | let liveIn = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset]; |
|
| 97 | - | let liveOut = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset]; |
|
| 98 | - | let defs = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset]; |
|
| 99 | - | let uses = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset]; |
|
| 96 | + | let liveIn = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 97 | + | let liveOut = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 98 | + | let defs = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 99 | + | let uses = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 100 | 100 | ||
| 101 | 101 | for b in 0..blockCount { |
|
| 102 | 102 | set liveIn[b] = try bitset::allocate(arena, maxReg); |
|
| 103 | 103 | set liveOut[b] = try bitset::allocate(arena, maxReg); |
|
| 104 | 104 | set defs[b] = try bitset::allocate(arena, maxReg); |
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Compute `liveIn = uses | (liveOut - defs)` and update `dst`. |
|
| 136 | 136 | /// Returns `true` if `dst` changed. Combined loop avoids multiple passes. |
|
| 137 | 137 | unsafe fn computeAndUpdateLiveIn( |
|
| 138 | - | dst: *mut bitset::Bitset, |
|
| 139 | - | liveOut: *bitset::Bitset, |
|
| 140 | - | defs: *bitset::Bitset, |
|
| 141 | - | uses: *bitset::Bitset |
|
| 138 | + | dst: *unsafe mut bitset::Bitset, |
|
| 139 | + | liveOut: *unsafe bitset::Bitset, |
|
| 140 | + | defs: *unsafe bitset::Bitset, |
|
| 141 | + | uses: *unsafe bitset::Bitset |
|
| 142 | 142 | ) -> bool { |
|
| 143 | 143 | let numWords = dst.bits.len; |
|
| 144 | 144 | let mut changed = false; |
|
| 145 | 145 | for i in 0..numWords { |
|
| 146 | 146 | let newWord = uses.bits[i] | (liveOut.bits[i] & ~defs.bits[i]); |
| 151 | 151 | } |
|
| 152 | 152 | return changed; |
|
| 153 | 153 | } |
|
| 154 | 154 | ||
| 155 | 155 | /// Compute local defs and uses for a single block. |
|
| 156 | - | unsafe fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) { |
|
| 156 | + | unsafe fn computeLocalDefsUses(block: *unsafe il::Block, defs: *unsafe mut bitset::Bitset, uses: *unsafe mut bitset::Bitset) { |
|
| 157 | 157 | for p in block.params { |
|
| 158 | 158 | bitset::put(defs, p.value.n); |
|
| 159 | 159 | } |
|
| 160 | 160 | for i in 0..block.instrs.len { |
|
| 161 | 161 | let instr = block.instrs[i]; |
| 197 | 197 | } |
|
| 198 | 198 | return current; |
|
| 199 | 199 | } |
|
| 200 | 200 | ||
| 201 | 201 | /// Add successor live-in sets to the block's live-out set. |
|
| 202 | - | unsafe fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 202 | + | unsafe fn addSuccessorLiveIn(func: *unsafe il::Fn, block: *unsafe il::Block, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) { |
|
| 203 | 203 | if block.instrs.len == 0 { |
|
| 204 | 204 | return; |
|
| 205 | 205 | } |
|
| 206 | 206 | let term = block.instrs[block.instrs.len - 1]; |
|
| 207 | 207 |
| 221 | 221 | else => {}, |
|
| 222 | 222 | } |
|
| 223 | 223 | } |
|
| 224 | 224 | ||
| 225 | 225 | /// Union a target block's live-in set into the block's live-out set. |
|
| 226 | - | unsafe fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 226 | + | unsafe fn unionBlockLiveIn(target: u32, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) { |
|
| 227 | 227 | bitset::union_(liveOut, &liveIn[target]); |
|
| 228 | 228 | } |
|
| 229 | 229 | ||
| 230 | 230 | /// Check if a register has any use after this instruction. |
|
| 231 | - | export unsafe fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 231 | + | export unsafe fn hasLaterUse(info: *unsafe LiveInfo, func: *unsafe il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 232 | 232 | let block = &func.blocks[blockIdx]; |
|
| 233 | 233 | ||
| 234 | 234 | if bitset::contains(&info.liveOut[blockIdx], reg.n) { |
|
| 235 | 235 | return true; |
|
| 236 | 236 | } |
lib/std/lang/gen/regalloc/spill.rad
+13 -12
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Spill decision for a function. |
|
| 52 | 52 | export record SpillInfo: Copy { |
|
| 53 | 53 | /// SSA register mapped to stack slot offset. `-1` means not spilled. |
|
| 54 | - | slots: *mut [i32], |
|
| 54 | + | slots: *[i32], |
|
| 55 | 55 | /// Total spill frame size needed in bytes. |
|
| 56 | 56 | frameSize: i32, |
|
| 57 | 57 | /// Values that must be allocated in callee-saved registers. |
|
| 58 | 58 | calleeClass: bitset::Bitset, |
|
| 59 | 59 | /// Maximum SSA register number. |
| 72 | 72 | cost: u32, |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Context for counting register uses. |
|
| 76 | 76 | record CountCtx: Copy { |
|
| 77 | - | costs: *mut [SpillCost], |
|
| 77 | + | costs: *unsafe mut [SpillCost], |
|
| 78 | 78 | weight: u32, |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Analyze a function and determine which values need spill slots. |
|
| 82 | 82 | export unsafe fn analyze( |
|
| 83 | - | func: *il::Fn, |
|
| 83 | + | func: *unsafe il::Fn, |
|
| 84 | 84 | live: &liveness::LiveInfo, |
|
| 85 | 85 | numRegs: u32, |
|
| 86 | 86 | numCalleeSaved: u32, |
|
| 87 | 87 | slotSize: u32, |
|
| 88 | 88 | arena: &mut alloc::Arena |
| 101 | 101 | let slots = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), maxReg) as *mut [i32]; |
|
| 102 | 102 | for i in 0..maxReg { |
|
| 103 | 103 | set slots[i] = -1; |
|
| 104 | 104 | } |
|
| 105 | 105 | // Allocate cost array. |
|
| 106 | - | let costs = try alloc::allocSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *mut [SpillCost]; |
|
| 106 | + | let costs = try alloc::allocRawSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *unsafe mut [SpillCost]; |
|
| 107 | 107 | for i in 0..maxReg { |
|
| 108 | 108 | set costs[i] = SpillCost { defs: 0, uses: 0 }; |
|
| 109 | 109 | } |
|
| 110 | 110 | // Phase 1: Calculate spill costs. |
|
| 111 | 111 | fillCosts(func, costs); |
| 176 | 176 | } |
|
| 177 | 177 | return SpillInfo { slots, frameSize, calleeClass, maxReg }; |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Calculate spill costs for all registers, weighted by loop depth. |
|
| 181 | - | unsafe fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) { |
|
| 181 | + | unsafe fn fillCosts(func: *unsafe il::Fn, costs: *unsafe mut [SpillCost]) { |
|
| 182 | 182 | for b in 0..func.blocks.len { |
|
| 183 | 183 | let block = &func.blocks[b]; |
|
| 184 | 184 | ||
| 185 | 185 | // Exponential weight for loop depth, capped to avoid overflow. |
|
| 186 | 186 | let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth; |
| 233 | 233 | bitset::clear(source, c.entries[i].reg); |
|
| 234 | 234 | } |
|
| 235 | 235 | } |
|
| 236 | 236 | ||
| 237 | 237 | /// Collect all values from a bitset into a candidates buffer with their costs. |
|
| 238 | - | unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *[SpillCost]) -> Candidates { |
|
| 238 | + | unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *unsafe [SpillCost]) -> Candidates { |
|
| 239 | 239 | let mut c = Candidates { entries: undefined, n: 0 }; |
|
| 240 | 240 | let mut it = bitset::iter(bs); |
|
| 241 | 241 | while let reg = bitset::iterNext(&mut it) { |
|
| 242 | 242 | assert c.n < MAX_CANDIDATES, "collectCandidates: too many live values"; |
|
| 243 | 243 | if reg < costs.len { |
| 250 | 250 | ||
| 251 | 251 | /// Limit register pressure by marking low-cost values as spilled. |
|
| 252 | 252 | unsafe fn limitPressure( |
|
| 253 | 253 | live: &mut bitset::Bitset, |
|
| 254 | 254 | spilled: &mut bitset::Bitset, |
|
| 255 | - | costs: *[SpillCost], |
|
| 255 | + | costs: *unsafe [SpillCost], |
|
| 256 | 256 | numRegs: u32 |
|
| 257 | 257 | ) { |
|
| 258 | 258 | let liveCount = bitset::count(live); |
|
| 259 | 259 | if liveCount <= numRegs { |
|
| 260 | 260 | return; |
| 277 | 277 | /// register. If the count exceeds `numCalleeSaved`, spill the cheapest |
|
| 278 | 278 | /// crossing values. |
|
| 279 | 279 | unsafe fn limitCrossCallPressure( |
|
| 280 | 280 | live: &mut bitset::Bitset, |
|
| 281 | 281 | spilled: &mut bitset::Bitset, |
|
| 282 | - | costs: *[SpillCost], |
|
| 282 | + | costs: *unsafe [SpillCost], |
|
| 283 | 283 | calleeClass: &mut bitset::Bitset, |
|
| 284 | 284 | numCalleeSaved: u32, |
|
| 285 | 285 | callDst: ?il::Reg |
|
| 286 | 286 | ) { |
|
| 287 | 287 | // Collect crossing candidates: live values excluding the call destination. |
| 311 | 311 | } |
|
| 312 | 312 | } |
|
| 313 | 313 | ||
| 314 | 314 | /// Callback for [`il::forEachReg`]: increments use count for register. |
|
| 315 | 315 | unsafe fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 316 | - | countRegUse(reg, ctxPtr as &mut CountCtx); |
|
| 316 | + | let weight = (ctxPtr as &mut CountCtx).weight; |
|
| 317 | + | countRegUse(reg, &mut (ctxPtr as &mut CountCtx).costs[..], weight); |
|
| 317 | 318 | } |
|
| 318 | 319 | ||
| 319 | 320 | /// Add the block weight to the register use count. |
|
| 320 | - | fn countRegUse(reg: il::Reg, ctx: &mut CountCtx) { |
|
| 321 | - | assert reg.n < ctx.costs.len, "countRegUseCallback: register out of bounds"; |
|
| 322 | - | set ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight; |
|
| 321 | + | fn countRegUse(reg: il::Reg, costs: &mut [SpillCost], weight: u32) { |
|
| 322 | + | assert reg.n < costs.len, "countRegUseCallback: register out of bounds"; |
|
| 323 | + | set costs[reg.n].uses += weight; |
|
| 323 | 324 | } |
|
| 324 | 325 | ||
| 325 | 326 | /// Callback for [`il::forEachReg`]: adds register to live set. |
|
| 326 | 327 | unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 327 | 328 | bitset::put(ctx as &mut bitset::Bitset, reg.n); |
lib/std/lang/il.rad
+14 -14
| 80 | 80 | ||
| 81 | 81 | /// Separator for qualified symbol names. |
|
| 82 | 82 | export constant PATH_SEPARATOR: *[u8] = "::"; |
|
| 83 | 83 | ||
| 84 | 84 | /// Format a qualified symbol name: `pkg::mod::path::name`. |
|
| 85 | - | export unsafe fn formatQualifiedName(arena: &mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 85 | + | export unsafe fn formatQualifiedName(arena: &mut alloc::Arena, path: &[*[u8]], name: *[u8]) -> *[u8] { |
|
| 86 | 86 | let mut totalLen: u32 = name.len; |
|
| 87 | 87 | for segment in path { |
|
| 88 | 88 | set totalLen += segment.len + PATH_SEPARATOR.len; |
|
| 89 | 89 | } |
|
| 90 | 90 | let buf = try! alloc::allocSlice(arena, 1, 1, totalLen) as *mut [u8]; |
| 179 | 179 | /// The constant value to match against. |
|
| 180 | 180 | value: i64, |
|
| 181 | 181 | /// The target block index. |
|
| 182 | 182 | target: u32, |
|
| 183 | 183 | /// Arguments to pass to the target block. |
|
| 184 | - | args: *mut [Val], |
|
| 184 | + | args: *unsafe mut [Val], |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// IL instruction. |
|
| 188 | 188 | /// SSA registers are represented as `Reg`, values as `Val`. |
|
| 189 | 189 | export union Instr: Copy { |
| 265 | 265 | /// Holds return value, for non-void function. |
|
| 266 | 266 | dst: ?Reg, |
|
| 267 | 267 | /// Function value, either a symbol or an address in a register. |
|
| 268 | 268 | func: Val, |
|
| 269 | 269 | /// Function arguments. |
|
| 270 | - | args: *[Val] |
|
| 270 | + | args: *unsafe [Val] |
|
| 271 | 271 | }, |
|
| 272 | 272 | ||
| 273 | 273 | ///////////////// |
|
| 274 | 274 | // Terminators // |
|
| 275 | 275 | ///////////////// |
|
| 276 | 276 | ||
| 277 | 277 | /// Return from function: `ret <val>;` or `ret;` |
|
| 278 | 278 | Ret { val: ?Val }, |
|
| 279 | 279 | /// Unconditional jump: `jmp @block(<arg>...);` |
|
| 280 | - | Jmp { target: u32, args: *mut [Val] }, |
|
| 280 | + | Jmp { target: u32, args: *unsafe mut [Val] }, |
|
| 281 | 281 | /// Compare-and-branch: `br.<op> <type> <a> <b> @then @else;` |
|
| 282 | - | Br { op: CmpOp, typ: Type, a: Val, b: Val, thenTarget: u32, thenArgs: *mut [Val], elseTarget: u32, elseArgs: *mut [Val] }, |
|
| 282 | + | Br { op: CmpOp, typ: Type, a: Val, b: Val, thenTarget: u32, thenArgs: *unsafe mut [Val], elseTarget: u32, elseArgs: *unsafe mut [Val] }, |
|
| 283 | 283 | /// Multi-way branch: `switch <val> (<n> @block) ... @default;` |
|
| 284 | - | Switch { val: Val, defaultTarget: u32, defaultArgs: *mut [Val], cases: *mut [SwitchCase] }, |
|
| 284 | + | Switch { val: Val, defaultTarget: u32, defaultArgs: *unsafe mut [Val], cases: *unsafe mut [SwitchCase] }, |
|
| 285 | 285 | /// Unreachable code marker: `unreachable;` |
|
| 286 | 286 | /// Indicates control flow cannot reach this point to allow for optimizations. |
|
| 287 | 287 | Unreachable, |
|
| 288 | 288 | ||
| 289 | 289 | ///////////////// |
| 311 | 311 | /// to another sequence. |
|
| 312 | 312 | export record Block: Copy { |
|
| 313 | 313 | /// Block label. |
|
| 314 | 314 | label: *[u8], |
|
| 315 | 315 | /// Block parameters. |
|
| 316 | - | params: *[Param], |
|
| 316 | + | params: *unsafe [Param], |
|
| 317 | 317 | /// Instructions in the block. The last instruction must be a terminator. |
|
| 318 | - | instrs: *mut [Instr], |
|
| 318 | + | instrs: *unsafe mut [Instr], |
|
| 319 | 319 | /// Source locations for debugging and error reporting. |
|
| 320 | 320 | /// One entry per instruction when debug info is enabled, empty otherwise. |
|
| 321 | - | locs: *[SrcLoc], |
|
| 321 | + | locs: *unsafe [SrcLoc], |
|
| 322 | 322 | /// Predecessor block indices. Used for control flow analysis. |
|
| 323 | - | preds: *[u32], |
|
| 323 | + | preds: *unsafe [u32], |
|
| 324 | 324 | /// Loop nesting depth. |
|
| 325 | 325 | /// Used for spill cost weighting in register allocation. |
|
| 326 | 326 | loopDepth: u32, |
|
| 327 | 327 | } |
|
| 328 | 328 | ||
| 329 | 329 | /// An IL function. |
|
| 330 | 330 | export record Fn: Copy { |
|
| 331 | 331 | /// Qualified function name (e.g. `$mod$path$func`). |
|
| 332 | 332 | name: *[u8], |
|
| 333 | 333 | /// Function parameters. |
|
| 334 | - | params: *[Param], |
|
| 334 | + | params: *unsafe [Param], |
|
| 335 | 335 | /// Return type. |
|
| 336 | 336 | returnType: Type, |
|
| 337 | 337 | /// Whether the function is extern (no body). |
|
| 338 | 338 | isExtern: bool, |
|
| 339 | 339 | /// Whether the function is a leaf (contains no call or ecall instructions). |
|
| 340 | 340 | isLeaf: bool, |
|
| 341 | 341 | /// Basic blocks. Empty for extern functions. |
|
| 342 | - | blocks: *[Block], |
|
| 342 | + | blocks: *unsafe [Block], |
|
| 343 | 343 | } |
|
| 344 | 344 | ||
| 345 | 345 | ///////////// |
|
| 346 | 346 | // Program // |
|
| 347 | 347 | ///////////// |
| 387 | 387 | values: *[DataValue], |
|
| 388 | 388 | } |
|
| 389 | 389 | ||
| 390 | 390 | /// An IL program (compilation unit). |
|
| 391 | 391 | export record Program: Copy { |
|
| 392 | - | /// Global data. |
|
| 392 | + | /// Immutable global data. |
|
| 393 | 393 | data: *[Data], |
|
| 394 | 394 | /// Functions. |
|
| 395 | - | fns: *[*Fn], |
|
| 395 | + | fns: *unsafe [*unsafe Fn], |
|
| 396 | 396 | } |
|
| 397 | 397 | ||
| 398 | 398 | /////////////////////// |
|
| 399 | 399 | // Utility Functions // |
|
| 400 | 400 | /////////////////////// |
lib/std/lang/il/printer.rad
+7 -6
| 144 | 144 | set buffer[start] = '%'; |
|
| 145 | 145 | write(out, &buffer[start..]); |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Write a comma-separated argument list in parentheses. |
|
| 149 | - | unsafe fn writeArgs(out: &mut sexpr::Output, args: *[super::Val]) { |
|
| 149 | + | unsafe fn writeArgs(out: &mut sexpr::Output, args: *unsafe [super::Val]) { |
|
| 150 | 150 | write(out, "("); |
|
| 151 | 151 | for arg, i in args { |
|
| 152 | 152 | if i > 0 { |
|
| 153 | 153 | write(out, ", "); |
|
| 154 | 154 | } |
| 163 | 163 | write(out, " "); |
|
| 164 | 164 | writeReg(out, param.value); |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | 167 | /// Write a comma-separated parameter list. |
|
| 168 | - | unsafe fn writeParams(out: &mut sexpr::Output, params: *[super::Param]) { |
|
| 168 | + | unsafe fn writeParams(out: &mut sexpr::Output, params: *unsafe [super::Param]) { |
|
| 169 | 169 | for param, i in params { |
|
| 170 | 170 | if i > 0 { |
|
| 171 | 171 | write(out, ", "); |
|
| 172 | 172 | } |
|
| 173 | 173 | writeParam(out, param); |
| 177 | 177 | ////////////////////////// |
|
| 178 | 178 | // Instruction printing // |
|
| 179 | 179 | ////////////////////////// |
|
| 180 | 180 | ||
| 181 | 181 | /// Write an instruction. |
|
| 182 | - | unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) { |
|
| 182 | + | unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) { |
|
| 183 | 183 | match inst { |
|
| 184 | 184 | // Memory operations. |
|
| 185 | 185 | case super::Instr::Reserve { dst, size, alignment } => { |
|
| 186 | 186 | write(out, "reserve "); |
|
| 187 | 187 | writeReg(out, dst); |
| 387 | 387 | //////////////////// |
|
| 388 | 388 | // Block printing // |
|
| 389 | 389 | //////////////////// |
|
| 390 | 390 | ||
| 391 | 391 | /// Write a basic block. |
|
| 392 | - | unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *[super::Block], block: *super::Block) { |
|
| 392 | + | unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *unsafe [super::Block], block: *unsafe super::Block) { |
|
| 393 | 393 | // Block label. |
|
| 394 | 394 | write(out, " @"); |
|
| 395 | 395 | write(out, block.label); |
|
| 396 | 396 | ||
| 397 | 397 | // Block parameters. |
| 413 | 413 | /////////////////////// |
|
| 414 | 414 | // Function printing // |
|
| 415 | 415 | /////////////////////// |
|
| 416 | 416 | ||
| 417 | 417 | /// Write a function. |
|
| 418 | - | unsafe fn writeFn(out: &mut sexpr::Output, f: *super::Fn) { |
|
| 418 | + | unsafe fn writeFn(out: &mut sexpr::Output, f: *unsafe super::Fn) { |
|
| 419 | 419 | // Function signature. |
|
| 420 | 420 | if f.isExtern { |
|
| 421 | 421 | write(out, "extern "); |
|
| 422 | 422 | } |
|
| 423 | 423 | write(out, "fn "); |
| 528 | 528 | export unsafe fn printProgramToBuffer( |
|
| 529 | 529 | program: &super::Program, |
|
| 530 | 530 | buf: *mut [u8] |
|
| 531 | 531 | ) -> *[u8] { |
|
| 532 | 532 | let mut pos: u32 = 0; |
|
| 533 | - | let mut out = sexpr::Output::Buffer { buf, pos: &mut pos }; |
|
| 533 | + | let raw: *unsafe mut [u8] = &mut buf[..]; |
|
| 534 | + | let mut out = sexpr::Output::Buffer { buf: raw, pos: &mut pos }; |
|
| 534 | 535 | printProgram(&mut out, program); |
|
| 535 | 536 | ||
| 536 | 537 | return &buf[..pos]; |
|
| 537 | 538 | } |
lib/std/lang/lower.rad
+241 -233
| 267 | 267 | /// Function sink used by lowerers that consume functions as they are produced. |
|
| 268 | 268 | export record FnSink: Copy { |
|
| 269 | 269 | /// Opaque context. It must remain valid for every sink callback. |
|
| 270 | 270 | ctx: *unsafe mut opaque, |
|
| 271 | 271 | /// Callback invoked for each lowered function. |
|
| 272 | - | emitFn: unsafe fn(*unsafe mut opaque, *il::Fn, FnRole), |
|
| 272 | + | emitFn: unsafe fn(*unsafe mut opaque, *unsafe il::Fn, FnRole), |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Destination for functions produced by the lowerer. |
|
| 276 | 276 | export union FnOutput: Copy { |
|
| 277 | 277 | /// Store lowered functions in the provided slice. |
|
| 278 | - | Accumulate(*mut [*il::Fn]), |
|
| 278 | + | Accumulate(*unsafe mut [*unsafe il::Fn]), |
|
| 279 | 279 | /// Send lowered functions to an external consumer immediately. |
|
| 280 | 280 | Stream(FnSink), |
|
| 281 | 281 | } |
|
| 282 | 282 | ||
| 283 | 283 | /// Module-level lowering context. Shared across all function lowerings. |
|
| 284 | 284 | /// Holds global state like the data section (strings, constants) and provides |
|
| 285 | 285 | /// access to the resolver for type queries. |
|
| 286 | - | export record Lowerer: Copy { |
|
| 286 | + | export record Lowerer { |
|
| 287 | 287 | /// Arena for persistent lowering state, including data and symbol names. |
|
| 288 | 288 | arena: *unsafe mut alloc::Arena, |
|
| 289 | 289 | /// Arena for allocations owned by the function currently being lowered. |
|
| 290 | 290 | fnArena: *unsafe mut alloc::Arena, |
|
| 291 | 291 | /// Allocator backed by the arena. |
| 314 | 314 | options: LowerOptions, |
|
| 315 | 315 | } |
|
| 316 | 316 | ||
| 317 | 317 | /// Entry mapping a function symbol to its qualified name. |
|
| 318 | 318 | record FnSymEntry: Copy { |
|
| 319 | - | sym: *resolver::Symbol, |
|
| 319 | + | sym: *unsafe resolver::Symbol, |
|
| 320 | 320 | qualName: *[u8], |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | 323 | /// Entry in the global error tag table. |
|
| 324 | 324 | record ErrTagEntry: Copy { |
| 327 | 327 | /// The globally unique tag assigned to this error type (non-zero). |
|
| 328 | 328 | tag: u32, |
|
| 329 | 329 | } |
|
| 330 | 330 | ||
| 331 | 331 | /// Compute the maximum size of any error type in a throw list. |
|
| 332 | - | fn maxErrSize(throwList: *[*resolver::Type]) -> u32 { |
|
| 332 | + | unsafe fn maxErrSize(throwList: *[*resolver::Type]) -> u32 { |
|
| 333 | 333 | let mut maxSize: u32 = 0; |
|
| 334 | 334 | for ty in throwList { |
|
| 335 | 335 | let size = resolver::getTypeLayout(*ty).size; |
|
| 336 | 336 | if size > maxSize { |
|
| 337 | 337 | set maxSize = size; |
| 341 | 341 | } |
|
| 342 | 342 | ||
| 343 | 343 | /// Get or assign a globally unique error tag for the given error type. |
|
| 344 | 344 | /// Tag `0` is reserved for success; error tags start at `1`. |
|
| 345 | 345 | fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 { |
|
| 346 | - | for entry in self.errTags { |
|
| 346 | + | for entry in &self.errTags[..] { |
|
| 347 | 347 | if entry.ty == errType { |
|
| 348 | 348 | return entry.tag; |
|
| 349 | 349 | } |
|
| 350 | 350 | } |
|
| 351 | 351 | let tag = self.errTagCounter; |
| 355 | 355 | ||
| 356 | 356 | return tag; |
|
| 357 | 357 | } |
|
| 358 | 358 | ||
| 359 | 359 | /// Emit one function to the active output. |
|
| 360 | - | unsafe fn emitFunction(self: &mut Lowerer, func: *il::Fn, role: FnRole) { |
|
| 360 | + | unsafe fn emitFunction(self: &mut Lowerer, func: *unsafe il::Fn, role: FnRole) { |
|
| 361 | 361 | match self.output { |
|
| 362 | 362 | case FnOutput::Accumulate(accumulated) => { |
|
| 363 | 363 | let mut fns = accumulated; |
|
| 364 | 364 | fns.append(func, self.allocator); |
|
| 365 | 365 | set self.output = FnOutput::Accumulate(fns); |
| 377 | 377 | } |
|
| 378 | 378 | return FnRole::Normal; |
|
| 379 | 379 | } |
|
| 380 | 380 | ||
| 381 | 381 | /// Builder for accumulating data values during constant lowering. |
|
| 382 | - | record DataValueBuilder: Copy { |
|
| 382 | + | record DataValueBuilder { |
|
| 383 | + | /// Allocator used to grow the value storage. |
|
| 383 | 384 | allocator: alloc::Allocator, |
|
| 385 | + | /// Owned values under construction. |
|
| 384 | 386 | values: *mut [il::DataValue], |
|
| 385 | 387 | /// Whether all pushed values can be represented by zero-filled memory. |
|
| 386 | 388 | zeroInit: bool, |
|
| 387 | 389 | } |
|
| 388 | 390 | ||
| 389 | 391 | /// Result of lowering constant data. |
|
| 390 | 392 | record ConstDataResult: Copy { |
|
| 393 | + | /// Immutable initializer values. |
|
| 391 | 394 | values: *[il::DataValue], |
|
| 395 | + | /// Whether the values can be represented by zero-filled memory. |
|
| 392 | 396 | zeroInit: bool, |
|
| 393 | 397 | } |
|
| 394 | 398 | ||
| 395 | 399 | /// Create a new builder. |
|
| 396 | 400 | fn dataBuilder(allocator: alloc::Allocator) -> DataValueBuilder { |
| 424 | 428 | set b.zeroInit = false; |
|
| 425 | 429 | } |
|
| 426 | 430 | } |
|
| 427 | 431 | ||
| 428 | 432 | /// Return the accumulated values. |
|
| 429 | - | fn dataBuilderFinish(b: &DataValueBuilder) -> ConstDataResult { |
|
| 430 | - | return ConstDataResult { |
|
| 431 | - | values: &b.values[..], |
|
| 432 | - | zeroInit: b.zeroInit, |
|
| 433 | - | }; |
|
| 433 | + | fn dataBuilderFinish(b: DataValueBuilder) -> ConstDataResult { |
|
| 434 | + | let case DataValueBuilder { values, zeroInit, .. } = b |
|
| 435 | + | else panic "expected data builder"; |
|
| 436 | + | return ConstDataResult { values, zeroInit }; |
|
| 434 | 437 | } |
|
| 435 | 438 | ||
| 436 | 439 | /////////////////////////// |
|
| 437 | 440 | // SSA Variable Tracking // |
|
| 438 | 441 | /////////////////////////// |
| 505 | 508 | record BlockData: Copy { |
|
| 506 | 509 | /// Block label for debugging and IL printing. |
|
| 507 | 510 | label: *[u8], |
|
| 508 | 511 | /// Block parameters for merging values at control flow joins. These |
|
| 509 | 512 | /// receive values from predecessor edges when control flow merges. |
|
| 510 | - | params: *mut [il::Param], |
|
| 513 | + | params: *unsafe mut [il::Param], |
|
| 511 | 514 | /// Variable ids in parameter order. Before sealing, these are the variables |
|
| 512 | 515 | /// whose predecessor arguments must be resolved. |
|
| 513 | - | paramVars: *mut [u32], |
|
| 516 | + | paramVars: *unsafe mut [u32], |
|
| 514 | 517 | /// Instructions accumulated so far. The last instruction should eventually |
|
| 515 | 518 | /// be a terminator. |
|
| 516 | - | instrs: *mut [il::Instr], |
|
| 519 | + | instrs: *unsafe mut [il::Instr], |
|
| 517 | 520 | /// Debug source locations, one per instruction. Only populated when |
|
| 518 | 521 | /// debug info is enabled. |
|
| 519 | - | locs: *mut [il::SrcLoc], |
|
| 522 | + | locs: *unsafe mut [il::SrcLoc], |
|
| 520 | 523 | /// Predecessor block ids. Used for SSA construction to propagate values |
|
| 521 | 524 | /// from predecessors when a variable is used before being defined locally. |
|
| 522 | - | preds: *mut [u32], |
|
| 525 | + | preds: *unsafe mut [u32], |
|
| 523 | 526 | /// The current SSA value of each variable in this block. Indexed by variable |
|
| 524 | 527 | /// id. A `nil` means the variable wasn't assigned in this block. Updated by |
|
| 525 | 528 | /// [`defVar`], queried by [`useVarInBlock`]. |
|
| 526 | - | vars: *mut [?il::Val], |
|
| 529 | + | vars: *unsafe mut [?il::Val], |
|
| 527 | 530 | /// Sealing state. Once sealed, all predecessors are known and we can resolve |
|
| 528 | 531 | /// variable uses that need to pull values from predecessors. |
|
| 529 | 532 | sealState: Sealed, |
|
| 530 | 533 | /// Loop nesting depth when this block was created. |
|
| 531 | 534 | loopDepth: u32, |
| 621 | 624 | /// Union type: tag compared against variant indices. |
|
| 622 | 625 | Union(resolver::UnionType), |
|
| 623 | 626 | } |
|
| 624 | 627 | ||
| 625 | 628 | /// Determine the kind of a match subject from its type. |
|
| 626 | - | fn matchSubjectKind(type: resolver::Type) -> MatchSubjectKind { |
|
| 629 | + | unsafe fn matchSubjectKind(type: resolver::Type) -> MatchSubjectKind { |
|
| 627 | 630 | if resolver::isOptionalPointer(type) { |
|
| 628 | 631 | return MatchSubjectKind::OptionalPtr; |
|
| 629 | 632 | } |
|
| 630 | 633 | if resolver::isOptionalAggregate(type) { |
|
| 631 | 634 | return MatchSubjectKind::OptionalAggregate; |
| 677 | 680 | low: *unsafe mut Lowerer, |
|
| 678 | 681 | /// Allocator for IL allocations. |
|
| 679 | 682 | allocator: alloc::Allocator, |
|
| 680 | 683 | /// Type signature of the function being lowered. |
|
| 681 | 684 | fnType: *resolver::FnType, |
|
| 685 | + | /// Number of SSA variable slots required for each block. |
|
| 686 | + | localCount: u32, |
|
| 682 | 687 | /// Function name, used as prefix for generated data symbols. |
|
| 683 | 688 | fnName: *[u8], |
|
| 684 | 689 | ||
| 685 | 690 | // ~ SSA variable tracking ~ // |
|
| 686 | 691 | ||
| 687 | 692 | /// Metadata (name, type, mutability) for each variable. Indexed by variable |
|
| 688 | 693 | /// id. Doesn't change after declaration. For the SSA value of a variable in |
|
| 689 | 694 | /// a specific block, see [`BlockData::vars`]. |
|
| 690 | - | vars: *mut [VarData], |
|
| 695 | + | vars: *unsafe mut [VarData], |
|
| 691 | 696 | /// Parameter-to-variable bindings, initialized in the entry block. |
|
| 692 | - | params: *mut [FnParamBinding], |
|
| 697 | + | params: *unsafe mut [FnParamBinding], |
|
| 693 | 698 | ||
| 694 | 699 | // ~ Basic block management ~ // |
|
| 695 | 700 | ||
| 696 | 701 | /// Block storage array, indexed by block id. |
|
| 697 | - | blockData: *mut [BlockData], |
|
| 702 | + | blockData: *unsafe mut [BlockData], |
|
| 698 | 703 | /// The entry block for this function. |
|
| 699 | 704 | entryBlock: ?BlockId, |
|
| 700 | 705 | /// The block currently receiving new instructions. |
|
| 701 | 706 | currentBlock: ?BlockId, |
|
| 702 | 707 | ||
| 703 | 708 | // ~ Loop management ~ // |
|
| 704 | 709 | ||
| 705 | 710 | /// Stack of loop contexts for break/continue resolution. |
|
| 706 | - | loopStack: *mut [LoopCtx], |
|
| 711 | + | loopStack: *unsafe mut [LoopCtx], |
|
| 707 | 712 | /// Current nesting depth (index into loopStack). |
|
| 708 | 713 | loopDepth: u32, |
|
| 709 | 714 | ||
| 710 | 715 | // ~ Counters ~ // |
|
| 711 | 716 |
| 747 | 752 | root: *ast::Node, |
|
| 748 | 753 | pkgName: *[u8], |
|
| 749 | 754 | arena: &mut alloc::Arena |
|
| 750 | 755 | ) -> il::Program throws (LowerError) { |
|
| 751 | 756 | let mut low = Lowerer { |
|
| 752 | - | arena: arena as *unsafe mut alloc::Arena, |
|
| 753 | - | fnArena: arena as *unsafe mut alloc::Arena, |
|
| 757 | + | arena: (&mut *arena) as *unsafe mut alloc::Arena, |
|
| 758 | + | fnArena: (&mut *arena) as *unsafe mut alloc::Arena, |
|
| 754 | 759 | allocator: alloc::arenaAllocator(arena), |
|
| 755 | 760 | resolver: res as *unsafe resolver::Resolver, |
|
| 756 | 761 | moduleGraph: nil, |
|
| 757 | 762 | pkgName, |
|
| 758 | 763 | currentMod: nil, |
| 763 | 768 | errTagCounter: 1, |
|
| 764 | 769 | options: LowerOptions { debug: false, buildTest: false }, |
|
| 765 | 770 | }; |
|
| 766 | 771 | try lowerDecls(&mut low, root, true); |
|
| 767 | 772 | ||
| 768 | - | return finalize(&low); |
|
| 773 | + | return finalize(low); |
|
| 769 | 774 | } |
|
| 770 | 775 | ||
| 771 | 776 | ///////////////////////////////// |
|
| 772 | 777 | // Multi-Module Lowering API // |
|
| 773 | 778 | ///////////////////////////////// |
| 781 | 786 | arena: *unsafe mut alloc::Arena, |
|
| 782 | 787 | fnArena: *unsafe mut alloc::Arena, |
|
| 783 | 788 | options: LowerOptions |
|
| 784 | 789 | ) -> Lowerer { |
|
| 785 | 790 | return Lowerer { |
|
| 786 | - | arena: arena as *unsafe mut alloc::Arena, |
|
| 787 | - | fnArena: fnArena as *unsafe mut alloc::Arena, |
|
| 788 | - | allocator: alloc::arenaAllocator(&mut *arena), |
|
| 789 | - | resolver: res as *unsafe resolver::Resolver, |
|
| 791 | + | arena, |
|
| 792 | + | fnArena, |
|
| 793 | + | allocator: alloc::arenaAllocator(arena), |
|
| 794 | + | resolver: res, |
|
| 790 | 795 | moduleGraph: graph as *unsafe module::ModuleGraph, |
|
| 791 | 796 | pkgName, |
|
| 792 | 797 | currentMod: nil, |
|
| 793 | 798 | data: &mut [], |
|
| 794 | 799 | output: FnOutput::Accumulate(&mut []), |
| 843 | 848 | else => {}, |
|
| 844 | 849 | } |
|
| 845 | 850 | } |
|
| 846 | 851 | } |
|
| 847 | 852 | ||
| 848 | - | /// Finalize lowering and return the unified IL program. |
|
| 849 | - | export fn finalize(low: &Lowerer) -> il::Program { |
|
| 850 | - | match low.output { |
|
| 853 | + | /// Consume the lowerer and publish its global data as an immutable array. |
|
| 854 | + | export fn finalize(low: Lowerer) -> il::Program { |
|
| 855 | + | let case Lowerer { data, output, .. } = low |
|
| 856 | + | else panic "expected lowerer"; |
|
| 857 | + | match output { |
|
| 851 | 858 | case FnOutput::Accumulate(accumulated) => { |
|
| 852 | 859 | return il::Program { |
|
| 853 | - | data: &low.data[..], |
|
| 854 | - | fns: &accumulated[..], |
|
| 860 | + | data, |
|
| 861 | + | fns: accumulated, |
|
| 855 | 862 | }; |
|
| 856 | 863 | } |
|
| 857 | 864 | case FnOutput::Stream(_) => { |
|
| 858 | 865 | panic "finalize: cannot finalize streaming lowerer"; |
|
| 859 | 866 | } |
| 875 | 882 | set id = self.currentMod; |
|
| 876 | 883 | } |
|
| 877 | 884 | let actualId = id else { |
|
| 878 | 885 | return &[]; |
|
| 879 | 886 | }; |
|
| 880 | - | let entry = module::get(&*graph, actualId) else { |
|
| 887 | + | let entry = module::get(graph, actualId) else { |
|
| 881 | 888 | return &[]; |
|
| 882 | 889 | }; |
|
| 883 | 890 | return module::moduleQualifiedPath(entry); |
|
| 884 | 891 | } |
|
| 885 | 892 |
| 888 | 895 | unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] { |
|
| 889 | 896 | let path = getModulePath(self, modId); |
|
| 890 | 897 | if path.len == 0 { |
|
| 891 | 898 | return name; |
|
| 892 | 899 | } |
|
| 893 | - | return il::formatQualifiedName(&mut *self.arena, path, name); |
|
| 900 | + | return il::formatQualifiedName(self.arena, path, name); |
|
| 894 | 901 | } |
|
| 895 | 902 | ||
| 896 | 903 | /// Register a function symbol with its qualified name. |
|
| 897 | 904 | /// Called when lowering function declarations, so cross-package calls can find |
|
| 898 | 905 | /// the function by name. |
|
| 899 | - | fn registerFnSym(self: &mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) { |
|
| 906 | + | fn registerFnSym(self: &mut Lowerer, sym: *unsafe resolver::Symbol, qualName: *[u8]) { |
|
| 900 | 907 | self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator); |
|
| 901 | 908 | } |
|
| 902 | 909 | ||
| 903 | 910 | /// Look up a function's qualified name by its symbol. |
|
| 904 | 911 | /// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered). |
|
| 905 | 912 | // TODO: This is kind of dubious as an optimization, if it depends on the order |
|
| 906 | 913 | // in which modules are lowered. |
|
| 907 | 914 | // TODO: Use a hash table here? |
|
| 908 | - | fn lookupFnSym(self: &Lowerer, sym: *resolver::Symbol) -> ?*[u8] { |
|
| 909 | - | for entry in self.fnSyms { |
|
| 915 | + | unsafe fn lookupFnSym(self: &Lowerer, sym: *unsafe resolver::Symbol) -> ?*[u8] { |
|
| 916 | + | for entry in &self.fnSyms[..] { |
|
| 910 | 917 | if entry.sym == sym { |
|
| 911 | 918 | return entry.qualName; |
|
| 912 | 919 | } |
|
| 913 | 920 | } |
|
| 914 | 921 | return nil; |
| 928 | 935 | self: &mut Lowerer, |
|
| 929 | 936 | node: *ast::Node, |
|
| 930 | 937 | fnType: *resolver::FnType, |
|
| 931 | 938 | qualName: *[u8] |
|
| 932 | 939 | ) -> FnLowerer { |
|
| 933 | - | let loopStack = try! alloc::allocSlice(&mut *self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx]; |
|
| 940 | + | let loopStack = try! alloc::allocRawSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *unsafe mut [LoopCtx]; |
|
| 934 | 941 | ||
| 935 | 942 | let mut fnLow = FnLowerer { |
|
| 936 | - | low: self as *unsafe mut Lowerer, |
|
| 937 | - | allocator: alloc::arenaAllocator(&mut *self.fnArena), |
|
| 943 | + | low: (&mut *self) as *unsafe mut Lowerer, |
|
| 944 | + | allocator: alloc::arenaAllocator(self.fnArena), |
|
| 938 | 945 | fnType: fnType, |
|
| 946 | + | localCount: resolver::nodeData(self.resolver, node).localCount, |
|
| 939 | 947 | fnName: qualName, |
|
| 940 | 948 | vars: &mut [], |
|
| 941 | 949 | params: &mut [], |
|
| 942 | 950 | blockData: &mut [], |
|
| 943 | 951 | entryBlock: nil, |
| 968 | 976 | /// This sets up the per-function lowering state, processes parameters, |
|
| 969 | 977 | /// then lowers the function body into a CFG of basic blocks. |
|
| 970 | 978 | /// |
|
| 971 | 979 | /// For throwing functions, the return type is a result aggregate |
|
| 972 | 980 | /// rather than the declared return type. |
|
| 973 | - | unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) { |
|
| 981 | + | unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 974 | 982 | if not shouldLowerFn(&decl, self.options.buildTest) { |
|
| 975 | 983 | return nil; |
|
| 976 | 984 | } |
|
| 977 | 985 | let case ast::NodeValue::Ident(name) = decl.name.value else { |
|
| 978 | 986 | throw LowerError::ExpectedIdentifier; |
|
| 979 | 987 | }; |
|
| 980 | - | let data = resolver::nodeData(&*self.resolver, node); |
|
| 988 | + | let data = resolver::nodeData(self.resolver, node); |
|
| 981 | 989 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 982 | 990 | throw LowerError::ExpectedFunction; |
|
| 983 | 991 | }; |
|
| 984 | 992 | let isExtern = checkAttr(decl.attrs, ast::Attribute::Extern); |
|
| 985 | 993 |
| 997 | 1005 | // as the first argument; the callee writes the return value into it. |
|
| 998 | 1006 | if requiresReturnParam(fnType) and not isExtern { |
|
| 999 | 1007 | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1000 | 1008 | } |
|
| 1001 | 1009 | let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil); |
|
| 1002 | - | let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1010 | + | let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1003 | 1011 | ||
| 1004 | 1012 | set *func = il::Fn { |
|
| 1005 | 1013 | name: qualName, |
|
| 1006 | 1014 | params: lowParams, |
|
| 1007 | 1015 | returnType: undefined, |
| 1031 | 1039 | ||
| 1032 | 1040 | /// Build a qualified name of the form "Type::method". |
|
| 1033 | 1041 | unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] { |
|
| 1034 | 1042 | let sepLen: u32 = 2; // "::" |
|
| 1035 | 1043 | let totalLen = typeName.len + sepLen + methodName.len; |
|
| 1036 | - | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1044 | + | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1037 | 1045 | let mut pos: u32 = 0; |
|
| 1038 | 1046 | ||
| 1039 | 1047 | set pos += try! mem::copy(&mut buf[pos..], typeName); |
|
| 1040 | 1048 | set pos += try! mem::copy(&mut buf[pos..], "::"); |
|
| 1041 | 1049 | set pos += try! mem::copy(&mut buf[pos..], methodName); |
| 1047 | 1055 | /// Build a v-table data name of the form "vtable::Type::Trait". |
|
| 1048 | 1056 | unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] { |
|
| 1049 | 1057 | let prefix = "vtable::"; |
|
| 1050 | 1058 | let sepLen: u32 = 2; // "::" |
|
| 1051 | 1059 | let totalLen = prefix.len + typeName.len + sepLen + traitName.len; |
|
| 1052 | - | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1060 | + | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1053 | 1061 | let mut pos: u32 = 0; |
|
| 1054 | 1062 | ||
| 1055 | 1063 | set pos += try! mem::copy(&mut buf[pos..], prefix); |
|
| 1056 | 1064 | set pos += try! mem::copy(&mut buf[pos..], typeName); |
|
| 1057 | 1065 | set pos += try! mem::copy(&mut buf[pos..], "::"); |
| 1071 | 1079 | unsafe fn lowerInstanceDecl( |
|
| 1072 | 1080 | self: &mut Lowerer, |
|
| 1073 | 1081 | node: *ast::Node, |
|
| 1074 | 1082 | traitNameNode: *ast::Node, |
|
| 1075 | 1083 | targetTypeNode: *ast::Node, |
|
| 1076 | - | methods: *mut [*ast::Node] |
|
| 1084 | + | methods: *[*ast::Node] |
|
| 1077 | 1085 | ) throws (LowerError) { |
|
| 1078 | 1086 | // Look up the trait and type from the resolver. |
|
| 1079 | - | let traitSym = resolver::nodeData(&*self.resolver, traitNameNode).sym |
|
| 1087 | + | let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym |
|
| 1080 | 1088 | else throw LowerError::MissingSymbol(traitNameNode); |
|
| 1081 | 1089 | let case resolver::SymbolData::Trait(traitInfo) = traitSym.data |
|
| 1082 | 1090 | else throw LowerError::MissingMetadata; |
|
| 1083 | - | let typeSym = resolver::nodeData(&*self.resolver, targetTypeNode).sym |
|
| 1091 | + | let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym |
|
| 1084 | 1092 | else throw LowerError::MissingSymbol(targetTypeNode); |
|
| 1085 | 1093 | ||
| 1086 | 1094 | let tName = traitSym.name; |
|
| 1087 | 1095 | let typeName = typeSym.name; |
|
| 1088 | 1096 |
| 1122 | 1130 | } |
|
| 1123 | 1131 | ||
| 1124 | 1132 | // Create v-table in data section, used for dynamic dispatch. |
|
| 1125 | 1133 | let vName = vtableName(self, nil, typeName, tName); |
|
| 1126 | 1134 | let values = try! alloc::allocSlice( |
|
| 1127 | - | &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1135 | + | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1128 | 1136 | ) as *mut [il::DataValue]; |
|
| 1129 | 1137 | ||
| 1130 | 1138 | for i in 0..traitInfo.methods.len { |
|
| 1131 | 1139 | set values[i] = il::DataValue { |
|
| 1132 | 1140 | item: il::DataItem::Fn(methodNames[i]), |
| 1150 | 1158 | node: *ast::Node, |
|
| 1151 | 1159 | qualName: *[u8], |
|
| 1152 | 1160 | receiverName: *ast::Node, |
|
| 1153 | 1161 | sig: ast::FnSig, |
|
| 1154 | 1162 | body: *ast::Node, |
|
| 1155 | - | ) -> ?*il::Fn throws (LowerError) { |
|
| 1156 | - | let data = resolver::nodeData(&*self.resolver, node); |
|
| 1163 | + | ) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 1164 | + | let data = resolver::nodeData(self.resolver, node); |
|
| 1157 | 1165 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 1158 | 1166 | throw LowerError::ExpectedFunction; |
|
| 1159 | 1167 | }; |
|
| 1160 | 1168 | let sym = data.sym else throw LowerError::MissingSymbol(node); |
|
| 1161 | 1169 | registerFnSym(self, sym, qualName); |
| 1163 | 1171 | let mut fnLow = fnLowerer(self, node, fnType, qualName); |
|
| 1164 | 1172 | if requiresReturnParam(fnType) { |
|
| 1165 | 1173 | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1166 | 1174 | } |
|
| 1167 | 1175 | let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName); |
|
| 1168 | - | let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1176 | + | let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1169 | 1177 | ||
| 1170 | 1178 | set *func = il::Fn { |
|
| 1171 | 1179 | name: qualName, |
|
| 1172 | 1180 | params: lowParams, |
|
| 1173 | 1181 | returnType: ilType(self, *fnType.returnType), |
| 1191 | 1199 | node: *ast::Node, |
|
| 1192 | 1200 | name: *ast::Node, |
|
| 1193 | 1201 | receiverName: *ast::Node, |
|
| 1194 | 1202 | sig: ast::FnSig, |
|
| 1195 | 1203 | body: *ast::Node, |
|
| 1196 | - | ) -> ?*il::Fn throws (LowerError) { |
|
| 1197 | - | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1204 | + | ) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 1205 | + | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1198 | 1206 | else throw LowerError::MissingSymbol(node); |
|
| 1199 | 1207 | let case ast::NodeValue::Ident(mName) = name.value |
|
| 1200 | 1208 | else throw LowerError::ExpectedIdentifier; |
|
| 1201 | - | let me = resolver::findMethodBySymbol(&*self.resolver, sym) |
|
| 1209 | + | let me = resolver::findMethodBySymbol(self.resolver, sym) |
|
| 1202 | 1210 | else throw LowerError::MissingMetadata; |
|
| 1203 | 1211 | let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName); |
|
| 1204 | 1212 | ||
| 1205 | 1213 | return try lowerMethod(self, node, qualName, receiverName, sig, body); |
|
| 1206 | 1214 | } |
| 1225 | 1233 | /// This ensures unique labels like `@then0`, `@then1`, etc. |
|
| 1226 | 1234 | unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) { |
|
| 1227 | 1235 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1228 | 1236 | let start = fmt::formatU32(suffix, &mut digits[..]); |
|
| 1229 | 1237 | let totalLen = base.len + digits.len - start; |
|
| 1230 | - | let buf = try! alloc::allocSlice(&mut *self.low.fnArena, 1, 1, totalLen) as *mut [u8]; |
|
| 1238 | + | let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8]; |
|
| 1231 | 1239 | ||
| 1232 | 1240 | try! mem::copy(&mut buf[..base.len], base); |
|
| 1233 | 1241 | try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]); |
|
| 1234 | 1242 | ||
| 1235 | 1243 | return &buf[..totalLen]; |
| 1274 | 1282 | } |
|
| 1275 | 1283 | return il::Val::Imm(constToScalar(val)); |
|
| 1276 | 1284 | } |
|
| 1277 | 1285 | ||
| 1278 | 1286 | /// Convert a resolver constant value to an IL data initializer item. |
|
| 1279 | - | fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem { |
|
| 1287 | + | unsafe fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem { |
|
| 1280 | 1288 | if let case resolver::ConstValue::String(s) = val { |
|
| 1281 | 1289 | return il::DataItem::Str(s); |
|
| 1282 | 1290 | } |
|
| 1283 | 1291 | // Bool and char are byte-sized; integer uses the declared type. |
|
| 1284 | 1292 | let mut irTyp = il::Type::W8; |
| 1295 | 1303 | node: *ast::Node, |
|
| 1296 | 1304 | ty: resolver::Type, |
|
| 1297 | 1305 | dataPrefix: *[u8], |
|
| 1298 | 1306 | b: &mut DataValueBuilder |
|
| 1299 | 1307 | ) throws (LowerError) { |
|
| 1300 | - | let val = resolver::constValueEntry(&*self.resolver, node) else { |
|
| 1301 | - | if let idx = voidVariantIndex(&*self.resolver, node) { |
|
| 1308 | + | let val = resolver::constValueEntry(self.resolver, node) else { |
|
| 1309 | + | if let idx = voidVariantIndex(self.resolver, node) { |
|
| 1302 | 1310 | dataBuilderPush(b, il::DataValue { |
|
| 1303 | 1311 | item: il::DataItem::Val { typ: il::Type::W8, val: idx }, |
|
| 1304 | 1312 | count: 1 |
|
| 1305 | 1313 | }); |
|
| 1306 | 1314 | return; |
| 1326 | 1334 | self: &mut Lowerer, |
|
| 1327 | 1335 | node: *ast::Node, |
|
| 1328 | 1336 | value: *ast::Node, |
|
| 1329 | 1337 | readOnly: bool |
|
| 1330 | 1338 | ) throws (LowerError) { |
|
| 1331 | - | let data = resolver::nodeData(&*self.resolver, node); |
|
| 1339 | + | let data = resolver::nodeData(self.resolver, node); |
|
| 1332 | 1340 | let sym = data.sym else { |
|
| 1333 | 1341 | throw LowerError::MissingSymbol(node); |
|
| 1334 | 1342 | }; |
|
| 1335 | 1343 | if data.ty == resolver::Type::Unknown { |
|
| 1336 | 1344 | throw LowerError::MissingType(node); |
|
| 1337 | 1345 | } |
|
| 1338 | 1346 | let layout = resolver::getTypeLayout(data.ty); |
|
| 1339 | 1347 | let qualName = qualifyName(self, nil, sym.name); |
|
| 1340 | 1348 | let mut b = dataBuilder(self.allocator); |
|
| 1341 | 1349 | try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b); |
|
| 1342 | - | let result = dataBuilderFinish(&b); |
|
| 1350 | + | let result = dataBuilderFinish(b); |
|
| 1343 | 1351 | ||
| 1344 | 1352 | self.data.append(il::Data { |
|
| 1345 | 1353 | name: qualName, |
|
| 1346 | 1354 | size: layout.size, |
|
| 1347 | 1355 | alignment: layout.alignment, |
| 1381 | 1389 | dataPrefix: *[u8], |
|
| 1382 | 1390 | b: &mut DataValueBuilder |
|
| 1383 | 1391 | ) throws (LowerError) { |
|
| 1384 | 1392 | let case resolver::Type::Slice { mutable, .. } = ty |
|
| 1385 | 1393 | else throw LowerError::ExpectedSliceOrArray; |
|
| 1386 | - | let targetTy = resolver::typeFor(&*self.resolver, addr.target) |
|
| 1394 | + | let targetTy = resolver::typeFor(self.resolver, addr.target) |
|
| 1387 | 1395 | else throw LowerError::MissingType(addr.target); |
|
| 1388 | 1396 | let case resolver::Type::Array(arrInfo) = targetTy |
|
| 1389 | 1397 | else throw LowerError::ExpectedArray; |
|
| 1390 | 1398 | ||
| 1391 | 1399 | let mut nested = dataBuilder(self.allocator); |
|
| 1392 | 1400 | let layout = resolver::getTypeLayout(targetTy); |
|
| 1393 | 1401 | try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested); |
|
| 1394 | 1402 | ||
| 1395 | - | let backing = dataBuilderFinish(&nested); |
|
| 1403 | + | let backing = dataBuilderFinish(nested); |
|
| 1396 | 1404 | let readOnly = not mutable; |
|
| 1397 | 1405 | let mut dataName: *[u8] = undefined; |
|
| 1398 | 1406 | if readOnly { |
|
| 1399 | 1407 | if let found = findConstData(self, backing.values, layout.alignment) { |
|
| 1400 | 1408 | set dataName = found; |
| 1416 | 1424 | dataPrefix: *[u8], |
|
| 1417 | 1425 | b: &mut DataValueBuilder |
|
| 1418 | 1426 | ) throws (LowerError) { |
|
| 1419 | 1427 | // Function pointer references in constant data. |
|
| 1420 | 1428 | if let case resolver::Type::Fn(_) = ty { |
|
| 1421 | - | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1429 | + | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1422 | 1430 | else throw LowerError::MissingSymbol(node); |
|
| 1423 | - | let modId = resolver::moduleIdForSymbol(&*self.resolver, sym); |
|
| 1431 | + | let modId = resolver::moduleIdForSymbol(self.resolver, sym); |
|
| 1424 | 1432 | let qualName = qualifyName(self, modId, sym.name); |
|
| 1425 | 1433 | dataBuilderPush(b, il::DataValue { |
|
| 1426 | 1434 | item: il::DataItem::Fn(qualName), count: 1, |
|
| 1427 | 1435 | }); |
|
| 1428 | 1436 | return; |
|
| 1429 | 1437 | } |
|
| 1430 | 1438 | // In constant data, a void variant of a mixed union still occupies the |
|
| 1431 | 1439 | // full tagged-union slot. Emitting only the tag corrupts following fields. |
|
| 1432 | - | if let sym = resolver::nodeData(&*self.resolver, node).sym { |
|
| 1440 | + | if let sym = resolver::nodeData(self.resolver, node).sym { |
|
| 1433 | 1441 | if let case resolver::SymbolData::Variant { type: resolver::Type::Void, .. } = sym.data { |
|
| 1434 | 1442 | if let case resolver::Type::Nominal(resolver::NominalType::Union(_)) = ty { |
|
| 1435 | 1443 | try lowerConstUnionVariantInto(self, node, sym, ty, &mut [], dataPrefix, b); |
|
| 1436 | 1444 | return; |
|
| 1437 | 1445 | } |
| 1450 | 1458 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 1451 | 1459 | try lowerConstArrayRepeatInto(self, repeat, ty, dataPrefix, b), |
|
| 1452 | 1460 | case ast::NodeValue::RecordLit(recLit) => |
|
| 1453 | 1461 | try lowerConstRecordLitInto(self, node, recLit, ty, dataPrefix, b), |
|
| 1454 | 1462 | case ast::NodeValue::Call(call) => { |
|
| 1455 | - | let calleeSym = resolver::nodeData(&*self.resolver, call.callee).sym |
|
| 1463 | + | let calleeSym = resolver::nodeData(self.resolver, call.callee).sym |
|
| 1456 | 1464 | else throw LowerError::MissingSymbol(call.callee); |
|
| 1457 | 1465 | match calleeSym.data { |
|
| 1458 | 1466 | case resolver::SymbolData::Variant { .. } => |
|
| 1459 | 1467 | try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b), |
|
| 1460 | 1468 | case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => { |
| 1466 | 1474 | case ast::NodeValue::AddressOf(addr) => { |
|
| 1467 | 1475 | try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b); |
|
| 1468 | 1476 | } |
|
| 1469 | 1477 | case ast::NodeValue::Ident(_) => { |
|
| 1470 | 1478 | // Identifier referencing a constant. |
|
| 1471 | - | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1479 | + | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1472 | 1480 | else throw LowerError::MissingSymbol(node); |
|
| 1473 | 1481 | let case ast::NodeValue::ConstDecl(decl) = sym.node.value |
|
| 1474 | 1482 | else throw LowerError::MissingConst(node); |
|
| 1475 | 1483 | ||
| 1476 | 1484 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1477 | 1485 | }, |
|
| 1478 | 1486 | case ast::NodeValue::ScopeAccess(_) => { |
|
| 1479 | - | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1487 | + | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1480 | 1488 | else throw LowerError::MissingSymbol(node); |
|
| 1481 | 1489 | if let case ast::NodeValue::ConstDecl(decl) = sym.node.value { |
|
| 1482 | 1490 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1483 | 1491 | } else { |
|
| 1484 | 1492 | try lowerConstScalarDataInto(self, node, ty, dataPrefix, b); |
| 1511 | 1519 | ||
| 1512 | 1520 | /// Flatten a constant array literal `[a, b, c]` into a builder. |
|
| 1513 | 1521 | /// Each element payload fills its type size; no extra slot padding is needed. |
|
| 1514 | 1522 | unsafe fn lowerConstArrayLitInto( |
|
| 1515 | 1523 | self: &mut Lowerer, |
|
| 1516 | - | elems: *mut [*ast::Node], |
|
| 1524 | + | elems: *[*ast::Node], |
|
| 1517 | 1525 | ty: resolver::Type, |
|
| 1518 | 1526 | dataPrefix: *[u8], |
|
| 1519 | 1527 | b: &mut DataValueBuilder |
|
| 1520 | 1528 | ) throws (LowerError) { |
|
| 1521 | 1529 | let case resolver::Type::Array(arrInfo) = ty |
| 1546 | 1554 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1547 | 1555 | dataBuilderPush(b, il::DataValue { |
|
| 1548 | 1556 | item: il::DataItem::Undef, |
|
| 1549 | 1557 | count: elemLayout.size * length |
|
| 1550 | 1558 | }); |
|
| 1551 | - | } else if let val = resolver::constValueEntry(&*self.resolver, repeat.item) { |
|
| 1559 | + | } else if let val = resolver::constValueEntry(self.resolver, repeat.item) { |
|
| 1552 | 1560 | if let case resolver::ConstValue::String(_) = val { |
|
| 1553 | 1561 | // A string used as a slice is represented by a three-word slice |
|
| 1554 | 1562 | // header, not by the bytes of the string itself. |
|
| 1555 | 1563 | for _ in 0..length { |
|
| 1556 | 1564 | try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b); |
| 1584 | 1592 | } |
|
| 1585 | 1593 | case resolver::Type::Nominal(resolver::NominalType::Union(_)) => { |
|
| 1586 | 1594 | let typeName = recLit.typeName else { |
|
| 1587 | 1595 | throw LowerError::ExpectedVariant; |
|
| 1588 | 1596 | }; |
|
| 1589 | - | let sym = resolver::nodeData(&*self.resolver, typeName).sym else { |
|
| 1597 | + | let sym = resolver::nodeData(self.resolver, typeName).sym else { |
|
| 1590 | 1598 | throw LowerError::MissingSymbol(typeName); |
|
| 1591 | 1599 | }; |
|
| 1592 | 1600 | try lowerConstUnionVariantInto(self, node, sym, ty, recLit.fields, dataPrefix, b); |
|
| 1593 | 1601 | } |
|
| 1594 | 1602 | else => throw LowerError::ExpectedRecord, |
| 1596 | 1604 | } |
|
| 1597 | 1605 | ||
| 1598 | 1606 | /// Build data values for record constants. |
|
| 1599 | 1607 | unsafe fn lowerConstRecordCtorInto( |
|
| 1600 | 1608 | self: &mut Lowerer, |
|
| 1601 | - | args: *mut [*ast::Node], |
|
| 1609 | + | args: *[*ast::Node], |
|
| 1602 | 1610 | recInfo: resolver::RecordType, |
|
| 1603 | 1611 | dataPrefix: *[u8], |
|
| 1604 | 1612 | b: &mut DataValueBuilder |
|
| 1605 | 1613 | ) throws (LowerError) { |
|
| 1606 | 1614 | let layout = recInfo.layout; |
| 1623 | 1631 | ||
| 1624 | 1632 | /// Build data values for a constant union variant value from payload fields/args. |
|
| 1625 | 1633 | unsafe fn lowerConstUnionVariantInto( |
|
| 1626 | 1634 | self: &mut Lowerer, |
|
| 1627 | 1635 | node: *ast::Node, |
|
| 1628 | - | variantSym: *mut resolver::Symbol, |
|
| 1636 | + | variantSym: *unsafe mut resolver::Symbol, |
|
| 1629 | 1637 | ty: resolver::Type, |
|
| 1630 | - | payloadArgs: *mut [*ast::Node], |
|
| 1638 | + | payloadArgs: *[*ast::Node], |
|
| 1631 | 1639 | dataPrefix: *[u8], |
|
| 1632 | 1640 | b: &mut DataValueBuilder |
|
| 1633 | 1641 | ) throws (LowerError) { |
|
| 1634 | 1642 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data |
|
| 1635 | 1643 | else throw LowerError::UnexpectedNodeValue(node); |
| 1681 | 1689 | } |
|
| 1682 | 1690 | ||
| 1683 | 1691 | /// Find an existing string data entry with matching content. |
|
| 1684 | 1692 | // TODO: Optimize with hash table or remove? |
|
| 1685 | 1693 | fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] { |
|
| 1686 | - | for d in self.data { |
|
| 1694 | + | for d in &self.data[..] { |
|
| 1687 | 1695 | if d.values.len == 1 { |
|
| 1688 | 1696 | if let case il::DataItem::Str(existing) = d.values[0].item { |
|
| 1689 | 1697 | if mem::eq(existing, s) { |
|
| 1690 | 1698 | return d.name; |
|
| 1691 | 1699 | } |
| 1708 | 1716 | set totalLen += segment.len; |
|
| 1709 | 1717 | } |
|
| 1710 | 1718 | if segments.len > 1 { |
|
| 1711 | 1719 | set totalLen += segments.len - 1; |
|
| 1712 | 1720 | } |
|
| 1713 | - | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1721 | + | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1714 | 1722 | let mut pos: u32 = 0; |
|
| 1715 | 1723 | ||
| 1716 | 1724 | for segment, i in segments { |
|
| 1717 | 1725 | set pos += try! mem::copy(&mut buf[pos..], segment); |
|
| 1718 | 1726 | if i + 1 <> segments.len { |
| 1732 | 1740 | count: u32, |
|
| 1733 | 1741 | namespace: *[u8] |
|
| 1734 | 1742 | ) -> *[u8] throws (LowerError) { |
|
| 1735 | 1743 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1736 | 1744 | let start = fmt::formatU32(count, &mut digits[..]); |
|
| 1737 | - | let suffix = try! alloc::allocSlice(&mut *self.arena, 1, 1, digits.len - start) as *mut [u8]; |
|
| 1745 | + | let suffix = try! alloc::allocSlice(self.arena, 1, 1, digits.len - start) as *mut [u8]; |
|
| 1738 | 1746 | try! mem::copy(suffix, &digits[start..]); |
|
| 1739 | 1747 | let segments = [prefix, namespace, suffix]; |
|
| 1740 | 1748 | ||
| 1741 | 1749 | return try buildSegmentedName(self, &segments[..]); |
|
| 1742 | 1750 | } |
| 1772 | 1780 | ) -> *[u8] throws (LowerError) { |
|
| 1773 | 1781 | if let existing = findStringData(self, s) { |
|
| 1774 | 1782 | return existing; |
|
| 1775 | 1783 | } |
|
| 1776 | 1784 | let values = try! alloc::allocSlice( |
|
| 1777 | - | &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1 |
|
| 1785 | + | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1 |
|
| 1778 | 1786 | ) as *mut [il::DataValue]; |
|
| 1779 | 1787 | ||
| 1780 | 1788 | set values[0] = il::DataValue { |
|
| 1781 | 1789 | item: il::DataItem::Str(s), |
|
| 1782 | 1790 | count: 1 |
| 1838 | 1846 | } |
|
| 1839 | 1847 | ||
| 1840 | 1848 | /// Find an existing read-only slice data entry with matching values. |
|
| 1841 | 1849 | // TODO: Optimize with hash table or remove? |
|
| 1842 | 1850 | fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1843 | - | for d in self.data { |
|
| 1851 | + | for d in &self.data[..] { |
|
| 1844 | 1852 | if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) { |
|
| 1845 | 1853 | return d.name; |
|
| 1846 | 1854 | } |
|
| 1847 | 1855 | } |
|
| 1848 | 1856 | return nil; |
| 1876 | 1884 | let elemLayout = resolver::getTypeLayout(*elemTy); |
|
| 1877 | 1885 | let size = elemLayout.size * length; |
|
| 1878 | 1886 | let mut dataName: *[u8] = undefined; |
|
| 1879 | 1887 | let mut found: ?*[u8] = nil; |
|
| 1880 | 1888 | if readOnly { |
|
| 1881 | - | set found = findConstData(&mut *self.low, values, alignment); |
|
| 1889 | + | set found = findConstData(self.low, values, alignment); |
|
| 1882 | 1890 | } |
|
| 1883 | 1891 | if let name = found { |
|
| 1884 | 1892 | set dataName = name; |
|
| 1885 | 1893 | } else { |
|
| 1886 | 1894 | set dataName = try nextDataName(self); |
| 1906 | 1914 | /// Generate a unique data name for inline literals, eg. `fnName$literal$N`. |
|
| 1907 | 1915 | unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) { |
|
| 1908 | 1916 | let counter = self.dataCounter; |
|
| 1909 | 1917 | set self.dataCounter += 1; |
|
| 1910 | 1918 | let fnName = self.fnName; |
|
| 1911 | - | return try nextDeclDataName(&mut *self.low, fnName, counter, "literal"); |
|
| 1919 | + | return try nextDeclDataName(self.low, fnName, counter, "literal"); |
|
| 1912 | 1920 | } |
|
| 1913 | 1921 | ||
| 1914 | 1922 | /// Assign a unique function-local data symbol name. |
|
| 1915 | 1923 | unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 1916 | - | let sym = resolver::nodeData(&*self.low.resolver, node).sym |
|
| 1924 | + | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1917 | 1925 | else throw LowerError::MissingSymbol(node); |
|
| 1918 | 1926 | ||
| 1919 | 1927 | let prefix = self.fnName; |
|
| 1920 | 1928 | let segments = [prefix, "nominal", sym.name]; |
|
| 1921 | - | let name = try buildSegmentedName(&mut *self.low, &segments[..]); |
|
| 1929 | + | let name = try buildSegmentedName(self.low, &segments[..]); |
|
| 1922 | 1930 | ||
| 1923 | 1931 | set sym.name = name; |
|
| 1924 | 1932 | } |
|
| 1925 | 1933 | ||
| 1926 | 1934 | /// Get the next available SSA register. |
| 1930 | 1938 | return reg; |
|
| 1931 | 1939 | } |
|
| 1932 | 1940 | ||
| 1933 | 1941 | /// Look up the resolved type of an AST node, or throw `MissingType`. |
|
| 1934 | 1942 | unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 1935 | - | let ty = resolver::typeFor(&*self.low.resolver, node) |
|
| 1943 | + | let ty = resolver::typeFor(self.low.resolver, node) |
|
| 1936 | 1944 | else throw LowerError::MissingType(node); |
|
| 1937 | 1945 | return ty; |
|
| 1938 | 1946 | } |
|
| 1939 | 1947 | ||
| 1940 | 1948 | /// Look up the symbol for an AST node, or throw `MissingSymbol`. |
|
| 1941 | - | unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) { |
|
| 1942 | - | let sym = resolver::nodeData(&*self.low.resolver, node).sym |
|
| 1949 | + | unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) { |
|
| 1950 | + | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1943 | 1951 | else throw LowerError::MissingSymbol(node); |
|
| 1944 | 1952 | return sym; |
|
| 1945 | 1953 | } |
|
| 1946 | 1954 | ||
| 1947 | 1955 | /// Remove the last block parameter and its associated variable. |
| 1961 | 1969 | /// Rewrite cached SSA values for a variable across all blocks, and also |
|
| 1962 | 1970 | /// rewrite any terminator arguments that reference the provisional register. |
|
| 1963 | 1971 | /// The latter is necessary because recursive SSA resolution may have already |
|
| 1964 | 1972 | /// patched terminator arguments with the provisional value before it was |
|
| 1965 | 1973 | /// found to be trivial. |
|
| 1966 | - | fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1974 | + | unsafe fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1967 | 1975 | for i in 0..self.blockData.len { |
|
| 1968 | 1976 | let blk = getBlockMut(self, BlockId(i)); |
|
| 1969 | 1977 | if blk.vars[*v] == from { |
|
| 1970 | 1978 | set blk.vars[*v] = to; |
|
| 1971 | 1979 | } |
| 1989 | 1997 | } |
|
| 1990 | 1998 | } |
|
| 1991 | 1999 | } |
|
| 1992 | 2000 | ||
| 1993 | 2001 | /// Replace all occurrences of `from` with `to` in an args slice. |
|
| 1994 | - | fn rewriteValInSlice(args: *mut [il::Val], from: il::Val, to: il::Val) { |
|
| 2002 | + | unsafe fn rewriteValInSlice(args: *unsafe mut [il::Val], from: il::Val, to: il::Val) { |
|
| 1995 | 2003 | for i in 0..args.len { |
|
| 1996 | 2004 | if args[i] == from { |
|
| 1997 | 2005 | set args[i] = to; |
|
| 1998 | 2006 | } |
|
| 1999 | 2007 | } |
| 2021 | 2029 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 2022 | 2030 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 2023 | 2031 | unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 2024 | 2032 | let label = try nextLabel(self, labelBase); |
|
| 2025 | 2033 | let id = BlockId(self.blockData.len); |
|
| 2026 | - | let varCount = self.fnType.localCount; |
|
| 2027 | - | let vars = try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val]; |
|
| 2034 | + | let varCount = self.localCount; |
|
| 2035 | + | let vars = try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val]; |
|
| 2028 | 2036 | ||
| 2029 | 2037 | for i in 0..varCount { |
|
| 2030 | 2038 | set vars[i] = nil; |
|
| 2031 | 2039 | } |
|
| 2032 | 2040 | self.blockData.append(BlockData { |
| 2088 | 2096 | try sealBlock(self, block); |
|
| 2089 | 2097 | switchToBlock(self, block); |
|
| 2090 | 2098 | } |
|
| 2091 | 2099 | ||
| 2092 | 2100 | /// Get block data by block id. |
|
| 2093 | - | fn getBlock(self: &FnLowerer, block: BlockId) -> *BlockData { |
|
| 2101 | + | unsafe fn getBlock(self: &FnLowerer, block: BlockId) -> *unsafe BlockData { |
|
| 2094 | 2102 | return &self.blockData[*block]; |
|
| 2095 | 2103 | } |
|
| 2096 | 2104 | ||
| 2097 | 2105 | /// Get mutable block data by block id. |
|
| 2098 | - | fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *mut BlockData { |
|
| 2106 | + | unsafe fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *unsafe mut BlockData { |
|
| 2099 | 2107 | return &mut self.blockData[*block]; |
|
| 2100 | 2108 | } |
|
| 2101 | 2109 | ||
| 2102 | 2110 | /// Get the current block being built. |
|
| 2103 | 2111 | fn currentBlock(self: &FnLowerer) -> BlockId { |
| 2262 | 2270 | let operandTy = scalarComparisonType(leftTy, rightTy); |
|
| 2263 | 2271 | let unsigned = isUnsignedType(operandTy); |
|
| 2264 | 2272 | if let op = cmpOpFrom(binop.op, unsigned) { |
|
| 2265 | 2273 | let a = try lowerExpr(self, binop.left); |
|
| 2266 | 2274 | let b = try lowerExpr(self, binop.right); |
|
| 2267 | - | let typ = ilType(&mut *self.low, operandTy); |
|
| 2275 | + | let typ = ilType(self.low, operandTy); |
|
| 2268 | 2276 | ||
| 2269 | 2277 | // Swap operands if needed. |
|
| 2270 | 2278 | match binop.op { |
|
| 2271 | 2279 | case ast::BinaryOp::Gt => // `a > b` = `b < a` |
|
| 2272 | 2280 | try emitBrCmp(self, op, typ, b, a, thenBlock, elseBlock), |
| 2348 | 2356 | ||
| 2349 | 2357 | /// Emit a load instruction for a scalar value at `src` plus `offset`. |
|
| 2350 | 2358 | /// For reading values that may be aggregates, use `emitRead` instead. |
|
| 2351 | 2359 | unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2352 | 2360 | let dst = nextReg(self); |
|
| 2353 | - | let ilTyp = ilType(&mut *self.low, typ); |
|
| 2361 | + | let ilTyp = ilType(self.low, typ); |
|
| 2354 | 2362 | ||
| 2355 | 2363 | if isSignedType(typ) { |
|
| 2356 | 2364 | emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset }); |
|
| 2357 | 2365 | } else { |
|
| 2358 | 2366 | emit(self, il::Instr::Load { typ: ilTyp, dst, src, offset }); |
| 2369 | 2377 | } |
|
| 2370 | 2378 | return emitLoad(self, src, offset, typ); |
|
| 2371 | 2379 | } |
|
| 2372 | 2380 | ||
| 2373 | 2381 | /// Emit a copy instruction that loads a data symbol's address into a register. |
|
| 2374 | - | unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2382 | + | unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg { |
|
| 2375 | 2383 | let dst = nextReg(self); |
|
| 2376 | - | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym); |
|
| 2377 | - | let qualName = qualifyName(&mut *self.low, modId, sym.name); |
|
| 2384 | + | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2385 | + | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2378 | 2386 | ||
| 2379 | 2387 | emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) }); |
|
| 2380 | 2388 | ||
| 2381 | 2389 | return dst; |
|
| 2382 | 2390 | } |
|
| 2383 | 2391 | ||
| 2384 | 2392 | /// Emit a copy instruction that loads a function's address into a register. |
|
| 2385 | - | unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2393 | + | unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg { |
|
| 2386 | 2394 | let dst = nextReg(self); |
|
| 2387 | - | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym); |
|
| 2388 | - | let qualName = qualifyName(&mut *self.low, modId, sym.name); |
|
| 2395 | + | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2396 | + | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2389 | 2397 | ||
| 2390 | 2398 | emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) }); |
|
| 2391 | 2399 | ||
| 2392 | 2400 | return dst; |
|
| 2393 | 2401 | } |
| 2443 | 2451 | } |
|
| 2444 | 2452 | case MatchSubjectKind::Union(unionInfo) => { |
|
| 2445 | 2453 | assert not isNil; |
|
| 2446 | 2454 | ||
| 2447 | 2455 | let case resolver::NodeExtra::UnionVariant { tag: variantTag, .. } = |
|
| 2448 | - | resolver::nodeData(&*self.low.resolver, pattern).extra |
|
| 2456 | + | resolver::nodeData(self.low.resolver, pattern).extra |
|
| 2449 | 2457 | else { |
|
| 2450 | 2458 | throw LowerError::ExpectedVariant; |
|
| 2451 | 2459 | }; |
|
| 2452 | 2460 | // Void unions are passed by value (the tag itself). |
|
| 2453 | 2461 | // Non-void unions are passed by reference (need to load tag). |
| 2545 | 2553 | } |
|
| 2546 | 2554 | try sealBlock(self, target); |
|
| 2547 | 2555 | } |
|
| 2548 | 2556 | ||
| 2549 | 2557 | /// Check if the current block already has a terminator instruction. |
|
| 2550 | - | fn blockHasTerminator(self: &FnLowerer) -> bool { |
|
| 2558 | + | unsafe fn blockHasTerminator(self: &FnLowerer) -> bool { |
|
| 2551 | 2559 | let blk = getBlock(self, currentBlock(self)); |
|
| 2552 | 2560 | if blk.instrs.len == 0 { |
|
| 2553 | 2561 | return false; |
|
| 2554 | 2562 | } |
|
| 2555 | 2563 | match blk.instrs[blk.instrs.len - 1] { |
| 2593 | 2601 | // Control Flow Edge Management // |
|
| 2594 | 2602 | ////////////////////////////////// |
|
| 2595 | 2603 | ||
| 2596 | 2604 | /// Add a predecessor edge from `pred` to `target`. |
|
| 2597 | 2605 | /// Must be called before the target block is sealed. Duplicates are ignored. |
|
| 2598 | - | fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2606 | + | unsafe fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2599 | 2607 | let blk = getBlockMut(self, target); |
|
| 2600 | 2608 | assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block"; |
|
| 2601 | 2609 | let preds = &mut blk.preds; |
|
| 2602 | 2610 | for i in 0..preds.len { |
|
| 2603 | 2611 | if preds[i] == *pred { // Avoid duplicate predecessor entries. |
| 2606 | 2614 | } |
|
| 2607 | 2615 | preds.append(*pred, self.allocator); |
|
| 2608 | 2616 | } |
|
| 2609 | 2617 | ||
| 2610 | 2618 | /// Finalize all blocks and return the block array. |
|
| 2611 | - | unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *[il::Block] throws (LowerError) { |
|
| 2619 | + | unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *unsafe [il::Block] throws (LowerError) { |
|
| 2612 | 2620 | let blockCount = self.blockData.len; |
|
| 2613 | - | let blocks = try! alloc::allocSlice( |
|
| 2614 | - | &mut *self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount |
|
| 2615 | - | ) as *mut [il::Block]; |
|
| 2621 | + | let blocks = try! alloc::allocRawSlice( |
|
| 2622 | + | self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount |
|
| 2623 | + | ) as *unsafe mut [il::Block]; |
|
| 2616 | 2624 | ||
| 2617 | 2625 | for i in 0..self.blockData.len { |
|
| 2618 | 2626 | let data = &self.blockData[i]; |
|
| 2619 | 2627 | ||
| 2620 | 2628 | set blocks[i] = il::Block { |
| 2633 | 2641 | // Loop Management // |
|
| 2634 | 2642 | ///////////////////// |
|
| 2635 | 2643 | ||
| 2636 | 2644 | /// Enter a loop context for break/continue handling. |
|
| 2637 | 2645 | /// `continueBlock` is `nil` when the continue target is created lazily. |
|
| 2638 | - | fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) { |
|
| 2646 | + | unsafe fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) { |
|
| 2639 | 2647 | assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow"; |
|
| 2640 | 2648 | let slot = &mut self.loopStack[self.loopDepth]; |
|
| 2641 | 2649 | ||
| 2642 | 2650 | set slot.breakTarget = breakBlock; |
|
| 2643 | 2651 | set slot.continueTarget = continueBlock; |
| 2649 | 2657 | assert self.loopDepth <> 0, "exitLoop: loopDepth is zero"; |
|
| 2650 | 2658 | set self.loopDepth -= 1; |
|
| 2651 | 2659 | } |
|
| 2652 | 2660 | ||
| 2653 | 2661 | /// Get the current loop context. |
|
| 2654 | - | fn currentLoop(self: &mut FnLowerer) -> ?*mut LoopCtx { |
|
| 2662 | + | unsafe fn currentLoop(self: &mut FnLowerer) -> ?*unsafe mut LoopCtx { |
|
| 2655 | 2663 | if self.loopDepth == 0 { |
|
| 2656 | 2664 | return nil; |
|
| 2657 | 2665 | } |
|
| 2658 | 2666 | return &mut self.loopStack[self.loopDepth - 1]; |
|
| 2659 | 2667 | } |
| 2670 | 2678 | set ctx.continueTarget = block; |
|
| 2671 | 2679 | return block; |
|
| 2672 | 2680 | } |
|
| 2673 | 2681 | ||
| 2674 | 2682 | /// Allocate a slice of values in the lowering arena. |
|
| 2675 | - | unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) { |
|
| 2676 | - | return try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val]; |
|
| 2683 | + | unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *unsafe mut [il::Val] throws (LowerError) { |
|
| 2684 | + | return try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val]; |
|
| 2677 | 2685 | } |
|
| 2678 | 2686 | ||
| 2679 | 2687 | /// Allocate a single-value slice in the lowering arena. |
|
| 2680 | - | unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) { |
|
| 2688 | + | unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) { |
|
| 2681 | 2689 | let args = try allocVals(self, 1); |
|
| 2682 | 2690 | set args[0] = val; |
|
| 2683 | 2691 | return args; |
|
| 2684 | 2692 | } |
|
| 2685 | 2693 |
| 2771 | 2779 | // a block parameter, but defer filling in the terminator arguments. When the |
|
| 2772 | 2780 | // block is later sealed via [`sealBlock`], all incomplete block params are resolved. |
|
| 2773 | 2781 | ||
| 2774 | 2782 | /// Declare a new source-level variable and define its initial value. |
|
| 2775 | 2783 | /// If called before any block exists (e.g., for parameters), the definition is skipped. |
|
| 2776 | - | fn newVar( |
|
| 2784 | + | unsafe fn newVar( |
|
| 2777 | 2785 | self: &mut FnLowerer, |
|
| 2778 | 2786 | name: ?*[u8], |
|
| 2779 | 2787 | type: il::Type, |
|
| 2780 | 2788 | mutable: bool, |
|
| 2781 | 2789 | val: il::Val |
| 2792 | 2800 | ||
| 2793 | 2801 | /// Define (write) a variable. Record the SSA value of a variable in the |
|
| 2794 | 2802 | /// current block. Called when a variable is assigned or initialized (`let` |
|
| 2795 | 2803 | /// bindings, assignments, loop updates). When [`useVar`] is later called, |
|
| 2796 | 2804 | /// it will retrieve this value. |
|
| 2797 | - | fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) { |
|
| 2805 | + | unsafe fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) { |
|
| 2798 | 2806 | assert *v < self.vars.len; |
|
| 2799 | 2807 | set getBlockMut(self, currentBlock(self)).vars[*v] = val; |
|
| 2800 | 2808 | } |
|
| 2801 | 2809 | ||
| 2802 | 2810 | /// Use (read) the current value of a variable in the current block. |
| 2844 | 2852 | return try createBlockParam(self, block, v); |
|
| 2845 | 2853 | } |
|
| 2846 | 2854 | ||
| 2847 | 2855 | /// Look up a variable by name in the current scope. |
|
| 2848 | 2856 | /// Searches from most recently declared to first, enabling shadowing. |
|
| 2849 | - | fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var { |
|
| 2857 | + | unsafe fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var { |
|
| 2850 | 2858 | let mut id = self.vars.len; |
|
| 2851 | 2859 | while id > 0 { |
|
| 2852 | 2860 | set id -= 1; |
|
| 2853 | 2861 | if let varName = self.vars[id].name { |
|
| 2854 | 2862 | // Names are interned strings, so pointer comparison suffices. |
| 2859 | 2867 | } |
|
| 2860 | 2868 | return nil; |
|
| 2861 | 2869 | } |
|
| 2862 | 2870 | ||
| 2863 | 2871 | /// Look up a local variable bound to an identifier node. |
|
| 2864 | - | fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var { |
|
| 2872 | + | unsafe fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var { |
|
| 2865 | 2873 | let case ast::NodeValue::Ident(name) = node.value else { |
|
| 2866 | 2874 | return nil; |
|
| 2867 | 2875 | }; |
|
| 2868 | 2876 | return lookupVarByName(self, name); |
|
| 2869 | 2877 | } |
|
| 2870 | 2878 | ||
| 2871 | 2879 | /// Save current lexical variable scope depth. |
|
| 2872 | - | fn enterVarScope(self: &FnLowerer) -> u32 { |
|
| 2880 | + | unsafe fn enterVarScope(self: &FnLowerer) -> u32 { |
|
| 2873 | 2881 | return self.vars.len; |
|
| 2874 | 2882 | } |
|
| 2875 | 2883 | ||
| 2876 | 2884 | /// Restore lexical variable scope depth. |
|
| 2877 | 2885 | unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) { |
|
| 2878 | 2886 | set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2879 | 2887 | } |
|
| 2880 | 2888 | ||
| 2881 | 2889 | /// Get the metadata for a variable. |
|
| 2882 | - | fn getVar(self: &FnLowerer, v: Var) -> *VarData { |
|
| 2890 | + | unsafe fn getVar(self: &FnLowerer, v: Var) -> *unsafe VarData { |
|
| 2883 | 2891 | assert *v < self.vars.len; |
|
| 2884 | 2892 | return &self.vars[*v]; |
|
| 2885 | 2893 | } |
|
| 2886 | 2894 | ||
| 2887 | 2895 | /// Create a block parameter to merge a variable's value from multiple |
| 3044 | 3052 | } |
|
| 3045 | 3053 | } |
|
| 3046 | 3054 | } |
|
| 3047 | 3055 | ||
| 3048 | 3056 | /// Grow an args array to hold at least the given capacity. |
|
| 3049 | - | unsafe fn growArgs(self: &mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] { |
|
| 3057 | + | unsafe fn growArgs(self: &mut FnLowerer, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] { |
|
| 3050 | 3058 | if args.len >= capacity { |
|
| 3051 | 3059 | return args; |
|
| 3052 | 3060 | } |
|
| 3053 | - | let newArgs = try! alloc::allocSlice( |
|
| 3054 | - | &mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3055 | - | ) as *mut [il::Val]; |
|
| 3061 | + | let newArgs = try! alloc::allocRawSlice( |
|
| 3062 | + | self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3063 | + | ) as *unsafe mut [il::Val]; |
|
| 3056 | 3064 | ||
| 3057 | 3065 | for arg, i in args { |
|
| 3058 | 3066 | set newArgs[i] = arg; |
|
| 3059 | 3067 | } |
|
| 3060 | 3068 | for i in args.len..capacity { |
| 3077 | 3085 | /// Lower function parameters. Declares variables for each parameter. |
|
| 3078 | 3086 | /// When a receiver name is passed, we're handling a trait method. |
|
| 3079 | 3087 | unsafe fn lowerParams( |
|
| 3080 | 3088 | self: &mut FnLowerer, |
|
| 3081 | 3089 | fnType: resolver::FnType, |
|
| 3082 | - | astParams: *mut [*ast::Node], |
|
| 3090 | + | astParams: *[*ast::Node], |
|
| 3083 | 3091 | receiverName: ?*ast::Node |
|
| 3084 | - | ) -> *[il::Param] throws (LowerError) { |
|
| 3092 | + | ) -> *unsafe [il::Param] throws (LowerError) { |
|
| 3085 | 3093 | let offset: u32 = 1 if self.returnReg <> nil else 0; |
|
| 3086 | 3094 | let totalLen = fnType.paramTypes.len as u32 + offset; |
|
| 3087 | 3095 | if totalLen == 0 { |
|
| 3088 | 3096 | return &[]; |
|
| 3089 | 3097 | } |
|
| 3090 | 3098 | assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS; |
|
| 3091 | 3099 | ||
| 3092 | - | let params = try! alloc::allocSlice( |
|
| 3093 | - | &mut *self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3094 | - | ) as *mut [il::Param]; |
|
| 3100 | + | let params = try! alloc::allocRawSlice( |
|
| 3101 | + | self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3102 | + | ) as *unsafe mut [il::Param]; |
|
| 3095 | 3103 | ||
| 3096 | 3104 | if let reg = self.returnReg { |
|
| 3097 | 3105 | set params[0] = il::Param { value: reg, type: il::Type::W64 }; |
|
| 3098 | 3106 | } |
|
| 3099 | 3107 | for i in 0..fnType.paramTypes.len as u32 { |
|
| 3100 | - | let type = ilType(&mut *self.low, *fnType.paramTypes[i]); |
|
| 3108 | + | let type = ilType(self.low, *fnType.paramTypes[i]); |
|
| 3101 | 3109 | let reg = nextReg(self); |
|
| 3102 | 3110 | ||
| 3103 | 3111 | set params[i + offset] = il::Param { value: reg, type }; |
|
| 3104 | 3112 | ||
| 3105 | 3113 | // Declare the parameter variable. For the receiver, the name comes |
| 3141 | 3149 | ||
| 3142 | 3150 | let mut bindType = unwrapped.effectiveTy; |
|
| 3143 | 3151 | if let case resolver::Type::Optional(inner) = unwrapped.effectiveTy { |
|
| 3144 | 3152 | set bindType = *inner; |
|
| 3145 | 3153 | } |
|
| 3146 | - | let ilType = ilType(&mut *self.low, unwrapped.effectiveTy); |
|
| 3154 | + | let ilType = ilType(self.low, unwrapped.effectiveTy); |
|
| 3147 | 3155 | let kind = matchSubjectKind(unwrapped.effectiveTy); |
|
| 3148 | 3156 | ||
| 3149 | 3157 | return MatchSubject { val, type: unwrapped.effectiveTy, ilType, bindType, kind, by: unwrapped.by }; |
|
| 3150 | 3158 | } |
|
| 3151 | 3159 |
| 3245 | 3253 | case resolver::MatchBy::Value => |
|
| 3246 | 3254 | set payload = tvalPayloadVal(self, base, bindType, valOffset), |
|
| 3247 | 3255 | case resolver::MatchBy::Ref, resolver::MatchBy::MutRef => |
|
| 3248 | 3256 | set payload = tvalPayloadAddr(self, base, valOffset), |
|
| 3249 | 3257 | }; |
|
| 3250 | - | return newVar(self, name, ilType(&mut *self.low, bindType), mutable, payload); |
|
| 3258 | + | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3251 | 3259 | } |
|
| 3252 | 3260 | ||
| 3253 | 3261 | /// Bind an identifier from a matched subject. |
|
| 3254 | 3262 | unsafe fn bindMatchVariable( |
|
| 3255 | 3263 | self: &mut FnLowerer, |
| 3266 | 3274 | if let case MatchSubjectKind::OptionalAggregate = subject.kind { |
|
| 3267 | 3275 | let valOffset = resolver::getOptionalValOffset(subject.bindType) as i32; |
|
| 3268 | 3276 | return try bindPayloadVariable(self, name, subject.val, subject.bindType, subject.by, valOffset, mutable); |
|
| 3269 | 3277 | } |
|
| 3270 | 3278 | // Declare the variable in the current block's scope. |
|
| 3271 | - | return newVar(self, name, ilType(&mut *self.low, subject.bindType), mutable, subject.val); |
|
| 3279 | + | return newVar(self, name, ilType(self.low, subject.bindType), mutable, subject.val); |
|
| 3272 | 3280 | } |
|
| 3273 | 3281 | ||
| 3274 | 3282 | /// Bind variables from inside case patterns (union variants, records, slices). |
|
| 3275 | 3283 | /// `failBlock` is passed when nested patterns may require additional tests |
|
| 3276 | 3284 | /// that branch on mismatch (e.g. nested union variant tests). |
|
| 3277 | 3285 | unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) { |
|
| 3278 | 3286 | for pattern in patterns { |
|
| 3279 | 3287 | ||
| 3280 | 3288 | // Handle simple variant patterns like `Variant(x)`. |
|
| 3281 | - | if let arg = resolver::variantPatternBinding(&*self.low.resolver, pattern) { |
|
| 3289 | + | if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) { |
|
| 3282 | 3290 | let case MatchSubjectKind::Union(unionInfo) = subject.kind |
|
| 3283 | 3291 | else panic "bindPatternVariables: expected union subject"; |
|
| 3284 | 3292 | let valOffset = unionInfo.valOffset as i32; |
|
| 3285 | 3293 | ||
| 3286 | 3294 | // Get the actual field type from the variant's record info. |
|
| 3287 | 3295 | // This preserves the original data layout type (e.g. `*T`) even when |
|
| 3288 | 3296 | // the resolver resolved the pattern against a dereferenced type (`T`). |
|
| 3289 | - | let variantExtra = resolver::nodeData(&*self.low.resolver, pattern).extra; |
|
| 3297 | + | let variantExtra = resolver::nodeData(self.low.resolver, pattern).extra; |
|
| 3290 | 3298 | let case resolver::NodeExtra::UnionVariant { ordinal, .. } = variantExtra |
|
| 3291 | 3299 | else panic "bindPatternVariables: expected variant extra"; |
|
| 3292 | 3300 | let payloadType = unionInfo.variants[ordinal].valueType; |
|
| 3293 | 3301 | let payloadRec = resolver::getRecord(payloadType) |
|
| 3294 | 3302 | else panic "bindPatternVariables: expected record payload"; |
| 3329 | 3337 | /// Each element is either bound as a variable, skipped (placeholder), or |
|
| 3330 | 3338 | /// tested against the subject element, branching to `failBlock` on mismatch. |
|
| 3331 | 3339 | unsafe fn bindArrayPatternElements( |
|
| 3332 | 3340 | self: &mut FnLowerer, |
|
| 3333 | 3341 | subject: &MatchSubject, |
|
| 3334 | - | items: *mut [*ast::Node], |
|
| 3342 | + | items: *[*ast::Node], |
|
| 3335 | 3343 | failBlock: BlockId |
|
| 3336 | 3344 | ) throws (LowerError) { |
|
| 3337 | 3345 | let case resolver::Type::Array(arrInfo) = subject.type |
|
| 3338 | 3346 | else throw LowerError::ExpectedSliceOrArray; |
|
| 3339 | 3347 |
| 3373 | 3381 | let case MatchSubjectKind::Union(unionInfo) = subject.kind |
|
| 3374 | 3382 | else panic "bindRecordPatternFields: expected union subject"; |
|
| 3375 | 3383 | ||
| 3376 | 3384 | // Get the variant index from the pattern node. |
|
| 3377 | 3385 | let case resolver::NodeExtra::UnionVariant { ordinal: variantOrdinal, .. } = |
|
| 3378 | - | resolver::nodeData(&*self.low.resolver, pattern).extra |
|
| 3386 | + | resolver::nodeData(self.low.resolver, pattern).extra |
|
| 3379 | 3387 | else throw LowerError::MissingMetadata; |
|
| 3380 | 3388 | ||
| 3381 | 3389 | // Get the record type from the variant's payload type. |
|
| 3382 | 3390 | let payloadType = unionInfo.variants[variantOrdinal].valueType; |
|
| 3383 | 3391 | let recInfo = resolver::getRecord(payloadType) |
| 3404 | 3412 | match binding.value { |
|
| 3405 | 3413 | case ast::NodeValue::Ident(name) => { |
|
| 3406 | 3414 | let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType) |
|
| 3407 | 3415 | if matchBy == resolver::MatchBy::Value |
|
| 3408 | 3416 | else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset)); |
|
| 3409 | - | newVar(self, name, ilType(&mut *self.low, fieldInfo.fieldType), false, val); |
|
| 3417 | + | newVar(self, name, ilType(self.low, fieldInfo.fieldType), false, val); |
|
| 3410 | 3418 | } |
|
| 3411 | 3419 | case ast::NodeValue::Placeholder => {} |
|
| 3412 | 3420 | case ast::NodeValue::RecordLit(lit) => { |
|
| 3413 | 3421 | // Check if this record literal is a union variant pattern. |
|
| 3414 | 3422 | if let keyNode = resolver::patternVariantKeyNode(binding) { |
|
| 3415 | - | if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(&*self.low.resolver, keyNode).extra { |
|
| 3423 | + | if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(self.low.resolver, keyNode).extra { |
|
| 3416 | 3424 | try emitNestedFieldTest(self, binding, base, fieldInfo, matchBy, failBlock); |
|
| 3417 | 3425 | return; |
|
| 3418 | 3426 | } |
|
| 3419 | 3427 | } |
|
| 3420 | 3428 | // Plain nested record destructuring pattern. |
| 3464 | 3472 | set derefBase = ptrReg; |
|
| 3465 | 3473 | set fieldType = *target; |
|
| 3466 | 3474 | } |
|
| 3467 | 3475 | } |
|
| 3468 | 3476 | // Build a MatchSubject for the nested field. |
|
| 3469 | - | let ilTy = ilType(&mut *self.low, fieldType); |
|
| 3477 | + | let ilTy = ilType(self.low, fieldType); |
|
| 3470 | 3478 | let kind = matchSubjectKind(fieldType); |
|
| 3471 | 3479 | ||
| 3472 | 3480 | // Determine the subject value. |
|
| 3473 | 3481 | let mut val: il::Val = undefined; |
|
| 3474 | 3482 | if let reg = derefBase { |
| 3511 | 3519 | ) throws (LowerError) { |
|
| 3512 | 3520 | for fieldNode in lit.fields { |
|
| 3513 | 3521 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 3514 | 3522 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 3515 | 3523 | }; |
|
| 3516 | - | let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) |
|
| 3524 | + | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) |
|
| 3517 | 3525 | else throw LowerError::MissingMetadata; |
|
| 3518 | 3526 | if fieldIdx >= recInfo.fields.len { |
|
| 3519 | 3527 | throw LowerError::MissingMetadata; |
|
| 3520 | 3528 | } |
|
| 3521 | 3529 | let fieldInfo = recInfo.fields[fieldIdx]; |
| 3523 | 3531 | try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock); |
|
| 3524 | 3532 | } |
|
| 3525 | 3533 | } |
|
| 3526 | 3534 | ||
| 3527 | 3535 | /// Lower function body to a list of basic blocks. |
|
| 3528 | - | unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) { |
|
| 3536 | + | unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) { |
|
| 3529 | 3537 | // Create and switch to entry block. |
|
| 3530 | 3538 | let entry = try createBlock(self, "entry"); |
|
| 3531 | 3539 | set self.entryBlock = entry; |
|
| 3532 | 3540 | switchToBlock(self, entry); |
|
| 3533 | 3541 |
| 3556 | 3564 | } |
|
| 3557 | 3565 | return try finalizeBlocks(self); |
|
| 3558 | 3566 | } |
|
| 3559 | 3567 | ||
| 3560 | 3568 | /// Lower a scalar match as a switch instruction. |
|
| 3561 | - | unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *mut [*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 3569 | + | unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 3562 | 3570 | let mut blocks: [BlockId; 32] = undefined; |
|
| 3563 | - | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 3571 | + | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 3564 | 3572 | let mut defaultIdx: u32 = 0; |
|
| 3565 | 3573 | let entry = currentBlock(self); |
|
| 3566 | 3574 | ||
| 3567 | 3575 | for p, i in prongs { |
|
| 3568 | 3576 | let case ast::NodeValue::MatchProng(prong) = p.value |
| 3574 | 3582 | set defaultIdx = i; |
|
| 3575 | 3583 | } |
|
| 3576 | 3584 | case ast::ProngArm::Case(pats) => { |
|
| 3577 | 3585 | set blocks[i] = try createBlock(self, "case"); |
|
| 3578 | 3586 | for pat in pats { |
|
| 3579 | - | let cv = resolver::constValueEntry(&*self.low.resolver, pat) |
|
| 3587 | + | let cv = resolver::constValueEntry(self.low.resolver, pat) |
|
| 3580 | 3588 | else throw LowerError::MissingConst(pat); |
|
| 3581 | 3589 | ||
| 3582 | 3590 | cases.append(il::SwitchCase { |
|
| 3583 | 3591 | value: constToScalar(cv), |
|
| 3584 | 3592 | target: *blocks[i], |
| 3669 | 3677 | let subject = try lowerMatchSubject(self, m.subject); |
|
| 3670 | 3678 | // Merge block created lazily if any arm needs it (i.e., doesn't diverge). |
|
| 3671 | 3679 | let mut mergeBlock: ?BlockId = nil; |
|
| 3672 | 3680 | ||
| 3673 | 3681 | // Use `switch` instruction for matches with constant patterns. |
|
| 3674 | - | if resolver::isMatchConst(&*self.low.resolver, node) { |
|
| 3682 | + | if resolver::isMatchConst(self.low.resolver, node) { |
|
| 3675 | 3683 | try lowerMatchSwitch(self, prongs, &subject, &mut mergeBlock); |
|
| 3676 | 3684 | return; |
|
| 3677 | 3685 | } |
|
| 3678 | 3686 | // Fallback: chained branches. |
|
| 3679 | 3687 | let firstArm = try createBlock(self, "arm"); |
| 3685 | 3693 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 3686 | 3694 | else panic "lowerMatch: expected match prong"; |
|
| 3687 | 3695 | ||
| 3688 | 3696 | let isLastArm = i + 1 == prongs.len; |
|
| 3689 | 3697 | let hasGuard = prong.guard <> nil; |
|
| 3690 | - | let catchAll = resolver::isProngCatchAll(&*self.low.resolver, prongNode); |
|
| 3698 | + | let catchAll = resolver::isProngCatchAll(self.low.resolver, prongNode); |
|
| 3691 | 3699 | ||
| 3692 | 3700 | // Entry block: guard block if present, otherwise the body block. |
|
| 3693 | 3701 | // The guard block must be created before the body block so that |
|
| 3694 | 3702 | // block indices are in reverse post-order (RPO), which the register |
|
| 3695 | 3703 | // allocator requires. |
| 3712 | 3720 | // Emit pattern test: branch to entry block on match, next arm on fail. |
|
| 3713 | 3721 | match prong.arm { |
|
| 3714 | 3722 | case ast::ProngArm::Binding(_) if not catchAll => |
|
| 3715 | 3723 | try emitBindingTest(self, &subject, entryBlock, nextArm), |
|
| 3716 | 3724 | case ast::ProngArm::Case(patterns) if not catchAll => |
|
| 3717 | - | try emitPatternMatches(self, &subject, &patterns[..], entryBlock, nextArm), |
|
| 3725 | + | try emitPatternMatches(self, &subject, patterns, entryBlock, nextArm), |
|
| 3718 | 3726 | else => |
|
| 3719 | 3727 | try emitJmp(self, entryBlock), |
|
| 3720 | 3728 | } |
|
| 3721 | 3729 | // Switch to entry block, where any variable bindings need to be created. |
|
| 3722 | 3730 | try switchToAndSeal(self, entryBlock); |
| 3727 | 3735 | // block. |
|
| 3728 | 3736 | match prong.arm { |
|
| 3729 | 3737 | case ast::ProngArm::Binding(pat) => |
|
| 3730 | 3738 | try bindMatchVariable(self, &subject, pat, false), |
|
| 3731 | 3739 | case ast::ProngArm::Case(patterns) => |
|
| 3732 | - | try bindPatternVariables(self, &subject, &patterns[..], nextArm), |
|
| 3740 | + | try bindPatternVariables(self, &subject, patterns, nextArm), |
|
| 3733 | 3741 | else => {}, |
|
| 3734 | 3742 | } |
|
| 3735 | 3743 | ||
| 3736 | 3744 | // Evaluate guard if present; can still fail to next arm. |
|
| 3737 | 3745 | if let g = prong.guard { |
| 3947 | 3955 | try lowerLet(self, node, l); |
|
| 3948 | 3956 | } |
|
| 3949 | 3957 | case ast::NodeValue::ConstDecl(decl) => { |
|
| 3950 | 3958 | // Local constants lower to data declarations and emit no runtime code. |
|
| 3951 | 3959 | try registerLocalDataDeclName(self, node); |
|
| 3952 | - | try lowerDataDecl(&mut *self.low, node, decl.value, true); |
|
| 3960 | + | try lowerDataDecl(self.low, node, decl.value, true); |
|
| 3953 | 3961 | } |
|
| 3954 | 3962 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 3955 | 3963 | // Local statics lower to data declarations and emit no runtime code. |
|
| 3956 | 3964 | try registerLocalDataDeclName(self, node); |
|
| 3957 | - | try lowerDataDecl(&mut *self.low, node, decl.value, false); |
|
| 3965 | + | try lowerDataDecl(self.low, node, decl.value, false); |
|
| 3958 | 3966 | } |
|
| 3959 | 3967 | case ast::NodeValue::If(i) => { |
|
| 3960 | 3968 | try lowerIf(self, i); |
|
| 3961 | 3969 | } |
|
| 3962 | 3970 | case ast::NodeValue::IfLet(i) => { |
| 4039 | 4047 | /////////////////////////////////////// |
|
| 4040 | 4048 | // Record and Aggregate Type Helpers // |
|
| 4041 | 4049 | /////////////////////////////////////// |
|
| 4042 | 4050 | ||
| 4043 | 4051 | /// Extract the nominal record info from a resolver type. |
|
| 4044 | - | fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType { |
|
| 4052 | + | unsafe fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType { |
|
| 4045 | 4053 | let case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) = typ |
|
| 4046 | 4054 | else return nil; |
|
| 4047 | 4055 | ||
| 4048 | 4056 | return recInfo; |
|
| 4049 | 4057 | } |
|
| 4050 | 4058 | ||
| 4051 | 4059 | /// Extract the nominal union info from a resolver type. |
|
| 4052 | - | fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType { |
|
| 4060 | + | unsafe fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType { |
|
| 4053 | 4061 | let case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) = typ |
|
| 4054 | 4062 | else return nil; |
|
| 4055 | 4063 | ||
| 4056 | 4064 | return unionInfo; |
|
| 4057 | 4065 | } |
| 4060 | 4068 | /// the resolver. `lowerExpr` already materializes the coercion in the |
|
| 4061 | 4069 | /// IL value, so the lowerer must use the post-coercion type when |
|
| 4062 | 4070 | /// choosing how to compare or store that value. |
|
| 4063 | 4071 | unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 4064 | 4072 | let ty = try typeOf(self, node); |
|
| 4065 | - | if let coerce = resolver::coercionFor(&*self.low.resolver, node) { |
|
| 4073 | + | if let coerce = resolver::coercionFor(self.low.resolver, node) { |
|
| 4066 | 4074 | if let case resolver::Coercion::OptionalLift(optTy) = coerce { |
|
| 4067 | 4075 | return optTy; |
|
| 4068 | 4076 | } |
|
| 4069 | 4077 | } |
|
| 4070 | 4078 | return ty; |
|
| 4071 | 4079 | } |
|
| 4072 | 4080 | ||
| 4073 | 4081 | /// Check if a resolver type lowers to an aggregate in memory. |
|
| 4074 | - | fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4082 | + | unsafe fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4075 | 4083 | match typ { |
|
| 4076 | 4084 | case resolver::Type::Slice { .. }, |
|
| 4077 | 4085 | resolver::Type::TraitObject { .. } => return true, |
|
| 4078 | 4086 | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4079 | 4087 | // Optional pointers are scalar due to NPO. |
| 4093 | 4101 | } |
|
| 4094 | 4102 | } |
|
| 4095 | 4103 | ||
| 4096 | 4104 | /// Check if a resolver type is a small aggregate that can be |
|
| 4097 | 4105 | /// passed or returned by value in a register. |
|
| 4098 | - | fn isSmallAggregate(typ: resolver::Type) -> bool { |
|
| 4106 | + | unsafe fn isSmallAggregate(typ: resolver::Type) -> bool { |
|
| 4099 | 4107 | match typ { |
|
| 4100 | 4108 | case resolver::Type::Nominal(_) => { |
|
| 4101 | 4109 | if resolver::isVoidUnion(typ) { |
|
| 4102 | 4110 | return false; |
|
| 4103 | 4111 | } |
| 4111 | 4119 | /// Whether a function needs a hidden return parameter. |
|
| 4112 | 4120 | /// |
|
| 4113 | 4121 | /// This is the case for throwing functions, which return a result aggregate, |
|
| 4114 | 4122 | /// and for functions returning large aggregates that cannot be passed in |
|
| 4115 | 4123 | /// registers. |
|
| 4116 | - | fn requiresReturnParam(fnType: *resolver::FnType) -> bool { |
|
| 4124 | + | unsafe fn requiresReturnParam(fnType: *resolver::FnType) -> bool { |
|
| 4117 | 4125 | return fnType.throwList.len > 0 |
|
| 4118 | 4126 | or (isAggregateType(*fnType.returnType) |
|
| 4119 | 4127 | and not isSmallAggregate(*fnType.returnType)); |
|
| 4120 | 4128 | } |
|
| 4121 | 4129 | ||
| 4122 | 4130 | /// Check if a node is a void union variant literal (e.g. `Color::Red`). |
|
| 4123 | 4131 | /// If so, returns the variant's tag index. This enables optimized comparisons |
|
| 4124 | 4132 | /// that only check the tag instead of doing full aggregate comparison. |
|
| 4125 | - | fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4133 | + | unsafe fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4126 | 4134 | let data = resolver::nodeData(res, node); |
|
| 4127 | 4135 | let sym = data.sym else { |
|
| 4128 | 4136 | return nil; |
|
| 4129 | 4137 | }; |
|
| 4130 | 4138 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
| 4167 | 4175 | let layout = resolver::getTypeLayout(typ); |
|
| 4168 | 4176 | ||
| 4169 | 4177 | emit(self, il::Instr::Blit { dst, src, size: il::Val::Imm(layout.size as i64) }); |
|
| 4170 | 4178 | } else { |
|
| 4171 | 4179 | emit(self, il::Instr::Store { |
|
| 4172 | - | typ: ilType(&mut *self.low, typ), |
|
| 4180 | + | typ: ilType(self.low, typ), |
|
| 4173 | 4181 | src, |
|
| 4174 | 4182 | dst: base, |
|
| 4175 | 4183 | offset, |
|
| 4176 | 4184 | }); |
|
| 4177 | 4185 | } |
| 4298 | 4306 | ||
| 4299 | 4307 | /// Build a trait object fat pointer from a data pointer and a v-table. |
|
| 4300 | 4308 | unsafe fn buildTraitObject( |
|
| 4301 | 4309 | self: &mut FnLowerer, |
|
| 4302 | 4310 | dataVal: il::Val, |
|
| 4303 | - | traitInfo: *resolver::TraitType, |
|
| 4311 | + | traitInfo: *unsafe resolver::TraitType, |
|
| 4304 | 4312 | inst: &resolver::InstanceEntry |
|
| 4305 | 4313 | ) -> il::Val throws (LowerError) { |
|
| 4306 | - | let vName = vtableName(&mut *self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name); |
|
| 4314 | + | let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name); |
|
| 4307 | 4315 | ||
| 4308 | 4316 | // Reserve space for the trait object on the stack. |
|
| 4309 | 4317 | let slot = emitReserveLayout(self, resolver::Layout { |
|
| 4310 | 4318 | size: resolver::PTR_SIZE * 2, |
|
| 4311 | 4319 | alignment: resolver::PTR_SIZE, |
| 4438 | 4446 | } |
|
| 4439 | 4447 | // For scalar types, load and compare directly. |
|
| 4440 | 4448 | let a = emitLoad(self, left, offset, fieldType); |
|
| 4441 | 4449 | let b = emitLoad(self, right, offset, fieldType); |
|
| 4442 | 4450 | let dst = nextReg(self); |
|
| 4443 | - | emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(&mut *self.low, fieldType), dst, a, b }); |
|
| 4451 | + | emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(self.low, fieldType), dst, a, b }); |
|
| 4444 | 4452 | ||
| 4445 | 4453 | return il::Val::Reg(dst); |
|
| 4446 | 4454 | } |
|
| 4447 | 4455 | ||
| 4448 | 4456 | /// Compare two record values for equality. |
| 4630 | 4638 | addPredecessor(self, mergeBlock, currentBlock(self)); |
|
| 4631 | 4639 | ||
| 4632 | 4640 | // Create comparison blocks for each non-void variant and build switch cases. |
|
| 4633 | 4641 | // Void variants jump directly to merge with `true`. |
|
| 4634 | 4642 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4635 | - | let cases = try! alloc::allocSlice( |
|
| 4636 | - | &mut *self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4637 | - | ) as *mut [il::SwitchCase]; |
|
| 4643 | + | let cases = try! alloc::allocRawSlice( |
|
| 4644 | + | self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4645 | + | ) as *unsafe mut [il::SwitchCase]; |
|
| 4638 | 4646 | ||
| 4639 | 4647 | let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined; |
|
| 4640 | 4648 | for variant, i in unionInfo.variants { |
|
| 4641 | 4649 | if variant.valueType == resolver::Type::Void { |
|
| 4642 | 4650 | set cases[i] = il::SwitchCase { |
| 4793 | 4801 | /// The `offset` is added to each field's offset when storing. |
|
| 4794 | 4802 | unsafe fn lowerRecordFields( |
|
| 4795 | 4803 | self: &mut FnLowerer, |
|
| 4796 | 4804 | dst: il::Reg, |
|
| 4797 | 4805 | recInfo: &resolver::RecordType, |
|
| 4798 | - | fields: *mut [*ast::Node], |
|
| 4806 | + | fields: *[*ast::Node], |
|
| 4799 | 4807 | offset: i32 |
|
| 4800 | 4808 | ) throws (LowerError) { |
|
| 4801 | 4809 | for fieldNode, i in fields { |
|
| 4802 | 4810 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 4803 | 4811 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 4804 | 4812 | }; |
|
| 4805 | 4813 | let mut fieldIdx: u32 = i; |
|
| 4806 | 4814 | if recInfo.labeled { |
|
| 4807 | - | let idx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) else { |
|
| 4815 | + | let idx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) else { |
|
| 4808 | 4816 | throw LowerError::MissingMetadata; |
|
| 4809 | 4817 | }; |
|
| 4810 | 4818 | set fieldIdx = idx; |
|
| 4811 | 4819 | } |
|
| 4812 | 4820 | // Skip `undefined` fields, they need no initialization. |
| 4819 | 4827 | } |
|
| 4820 | 4828 | } |
|
| 4821 | 4829 | } |
|
| 4822 | 4830 | ||
| 4823 | 4831 | /// Lower an unlabeled record constructor call. |
|
| 4824 | - | unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4832 | + | unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4825 | 4833 | let case resolver::NominalType::Record(recInfo) = *nominal else { |
|
| 4826 | 4834 | throw LowerError::ExpectedRecord; |
|
| 4827 | 4835 | }; |
|
| 4828 | 4836 | let typ = resolver::Type::Nominal(nominal); |
|
| 4829 | 4837 | let dst = try emitReserve(self, typ); |
| 4838 | 4846 | } |
|
| 4839 | 4847 | return il::Val::Reg(dst); |
|
| 4840 | 4848 | } |
|
| 4841 | 4849 | ||
| 4842 | 4850 | /// Lower an array literal expression like `[1, 2, 3]`. |
|
| 4843 | - | unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val |
|
| 4851 | + | unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *[*ast::Node]) -> il::Val |
|
| 4844 | 4852 | throws (LowerError) |
|
| 4845 | 4853 | { |
|
| 4846 | 4854 | let typ = try typeOf(self, node); |
|
| 4847 | 4855 | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 4848 | 4856 | throw LowerError::ExpectedArray; |
| 4885 | 4893 | } |
|
| 4886 | 4894 | return il::Val::Reg(dst); |
|
| 4887 | 4895 | } |
|
| 4888 | 4896 | ||
| 4889 | 4897 | /// Lower a union constructor call like `Union::Variant(...)`. |
|
| 4890 | - | unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 4898 | + | unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 4891 | 4899 | throws (LowerError) |
|
| 4892 | 4900 | { |
|
| 4893 | 4901 | let unionTy = try typeOf(self, node); |
|
| 4894 | 4902 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
|
| 4895 | 4903 | throw LowerError::ExpectedVariant; |
| 4910 | 4918 | ||
| 4911 | 4919 | /// Lower a field access into a pointer to the field. |
|
| 4912 | 4920 | unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) { |
|
| 4913 | 4921 | let parentTy = try typeOf(self, access.parent); |
|
| 4914 | 4922 | let subjectTy = resolver::autoDeref(parentTy); |
|
| 4915 | - | let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, access.child) else { |
|
| 4923 | + | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else { |
|
| 4916 | 4924 | throw LowerError::MissingMetadata; |
|
| 4917 | 4925 | }; |
|
| 4918 | 4926 | let fieldInfo = resolver::getRecordField(subjectTy, fieldIdx) else { |
|
| 4919 | 4927 | throw LowerError::FieldNotFound; |
|
| 4920 | 4928 | }; |
| 5007 | 5015 | self: &mut FnLowerer, |
|
| 5008 | 5016 | container: *ast::Node, |
|
| 5009 | 5017 | range: ast::Range, |
|
| 5010 | 5018 | sliceNode: *ast::Node |
|
| 5011 | 5019 | ) -> il::Val throws (LowerError) { |
|
| 5012 | - | let info = resolver::sliceRangeInfoFor(&*self.low.resolver, sliceNode) else { |
|
| 5020 | + | let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else { |
|
| 5013 | 5021 | throw LowerError::MissingMetadata; |
|
| 5014 | 5022 | }; |
|
| 5015 | 5023 | let r = try resolveSliceRangePtr(self, container, range, info); |
|
| 5016 | 5024 | return try buildSliceValue( |
|
| 5017 | 5025 | self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count |
| 5051 | 5059 | // Already address-taken; return existing stack pointer. |
|
| 5052 | 5060 | return val; |
|
| 5053 | 5061 | } |
|
| 5054 | 5062 | // Materialize a stack slot using the declaration's resolved |
|
| 5055 | 5063 | // layout so `align(N)` on locals is honored. |
|
| 5056 | - | let layout = resolver::getLayout(&*self.low.resolver, addr.target, typ); |
|
| 5064 | + | let layout = resolver::getLayout(self.low.resolver, addr.target, typ); |
|
| 5057 | 5065 | let slot = emitReserveLayout(self, layout); |
|
| 5058 | 5066 | try emitStore(self, slot, 0, typ, val); |
|
| 5059 | 5067 | let stackVal = il::Val::Reg(slot); |
|
| 5060 | 5068 | ||
| 5061 | 5069 | set self.vars[*v].addressTaken = true; |
|
| 5062 | 5070 | defVar(self, v, stackVal); |
|
| 5063 | 5071 | ||
| 5064 | 5072 | return stackVal; |
|
| 5065 | 5073 | } |
|
| 5066 | 5074 | // Fall back to symbol lookup for constants/statics. |
|
| 5067 | - | if let sym = resolver::nodeData(&*self.low.resolver, addr.target).sym { |
|
| 5075 | + | if let sym = resolver::nodeData(self.low.resolver, addr.target).sym { |
|
| 5068 | 5076 | return il::Val::Reg(emitDataAddr(self, sym)); |
|
| 5069 | 5077 | } else { |
|
| 5070 | 5078 | throw LowerError::MissingSymbol(node); |
|
| 5071 | 5079 | } |
|
| 5072 | 5080 | } |
| 5104 | 5112 | if length == 0 { |
|
| 5105 | 5113 | return try buildSliceValue( |
|
| 5106 | 5114 | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 5107 | 5115 | ); |
|
| 5108 | 5116 | } |
|
| 5109 | - | if resolver::isConstExpr(&*self.low.resolver, arrayNode) { |
|
| 5117 | + | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 5110 | 5118 | let fnName = self.fnName; |
|
| 5111 | 5119 | let mut b = dataBuilder(self.low.allocator); |
|
| 5112 | 5120 | match arrayNode.value { |
|
| 5113 | 5121 | case ast::NodeValue::ArrayLit(elements) => |
|
| 5114 | - | try lowerConstArrayLitInto(&mut *self.low, elements, arrayTy, fnName, &mut b), |
|
| 5122 | + | try lowerConstArrayLitInto(self.low, elements, arrayTy, fnName, &mut b), |
|
| 5115 | 5123 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 5116 | - | try lowerConstArrayRepeatInto(&mut *self.low, repeat, arrayTy, fnName, &mut b), |
|
| 5124 | + | try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, fnName, &mut b), |
|
| 5117 | 5125 | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5118 | 5126 | } |
|
| 5119 | - | let result = dataBuilderFinish(&b); |
|
| 5127 | + | let result = dataBuilderFinish(b); |
|
| 5120 | 5128 | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5121 | 5129 | return try lowerConstDataAsSlice( |
|
| 5122 | 5130 | self, &result, alignment, not mutable, |
|
| 5123 | 5131 | item, mutable, length |
|
| 5124 | 5132 | ); |
| 5156 | 5164 | case resolver::Type::Array(arrInfo) => { |
|
| 5157 | 5165 | set elemType = *arrInfo.item; |
|
| 5158 | 5166 | // Runtime safety check: index must be strictly less than array length. |
|
| 5159 | 5167 | // Skip when the index is a compile-time constant, since we check |
|
| 5160 | 5168 | // that in the resolver. |
|
| 5161 | - | if not resolver::isConstExpr(&*self.low.resolver, index) { |
|
| 5169 | + | if not resolver::isConstExpr(self.low.resolver, index) { |
|
| 5162 | 5170 | let arrLen = il::Val::Imm(arrInfo.length as i64); |
|
| 5163 | 5171 | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen); |
|
| 5164 | 5172 | } |
|
| 5165 | 5173 | } |
|
| 5166 | 5174 | else => throw LowerError::ExpectedSliceOrArray, |
| 5204 | 5212 | } |
|
| 5205 | 5213 | let case ast::NodeValue::Ident(name) = l.ident.value else { |
|
| 5206 | 5214 | throw LowerError::ExpectedIdentifier; |
|
| 5207 | 5215 | }; |
|
| 5208 | 5216 | let typ = try typeOf(self, l.value); |
|
| 5209 | - | let ilType = ilType(&mut *self.low, typ); |
|
| 5217 | + | let ilType = ilType(self.low, typ); |
|
| 5210 | 5218 | let mut varVal = val; |
|
| 5211 | 5219 | ||
| 5212 | 5220 | // Aggregates with persistent storage need a local copy to avoid aliasing. |
|
| 5213 | 5221 | // Temporaries such as literals or call results can be adopted directly. |
|
| 5214 | 5222 | // This is because aggregates are represented as memory addresses |
| 5218 | 5226 | // Void variant literals (e.g. `Option::None`) use scope access syntax and |
|
| 5219 | 5227 | // are flagged as place expressions, but they are freshly constructed |
|
| 5220 | 5228 | // temporaries with no persistent storage. |
|
| 5221 | 5229 | if isAggregateType(typ) and |
|
| 5222 | 5230 | ast::isPlaceExpr(l.value) and |
|
| 5223 | - | voidVariantIndex(&*self.low.resolver, l.value) == nil { |
|
| 5231 | + | voidVariantIndex(self.low.resolver, l.value) == nil { |
|
| 5224 | 5232 | set varVal = try emitStackVal(self, typ, val); |
|
| 5225 | 5233 | } |
|
| 5226 | 5234 | ||
| 5227 | 5235 | // If the resolver determined that this variable's address is taken |
|
| 5228 | 5236 | // anywhere in the function, allocate a stack slot immediately so the |
|
| 5229 | 5237 | // SSA value is always a pointer. This avoids mixing integer and pointer |
|
| 5230 | 5238 | // values in loop phis when `&var` or `&mut var` appears inside a loop. |
|
| 5231 | 5239 | if not isAggregateType(typ) { |
|
| 5232 | - | if let sym = resolver::nodeData(&*self.low.resolver, node).sym { |
|
| 5240 | + | if let sym = resolver::nodeData(self.low.resolver, node).sym { |
|
| 5233 | 5241 | if let case resolver::SymbolData::Value { addressTaken, .. } = sym.data; addressTaken { |
|
| 5234 | - | let layout = resolver::getLayout(&*self.low.resolver, node, typ); |
|
| 5242 | + | let layout = resolver::getLayout(self.low.resolver, node, typ); |
|
| 5235 | 5243 | let slot = emitReserveLayout(self, layout); |
|
| 5236 | 5244 | try emitStore(self, slot, 0, typ, varVal); |
|
| 5237 | 5245 | ||
| 5238 | 5246 | let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot)); |
|
| 5239 | 5247 | set self.vars[*v].addressTaken = true; |
| 5356 | 5364 | let current = emitRead(self, place.base, place.offset, place.fieldType); |
|
| 5357 | 5365 | let left = try applyCoercion(self, binop.left, current); |
|
| 5358 | 5366 | let right = try lowerExpr(self, binop.right); |
|
| 5359 | 5367 | let exprType = try typeOf(self, expr); |
|
| 5360 | 5368 | let result = emitScalarBinOp( |
|
| 5361 | - | self, binop.op, ilType(&mut *self.low, exprType), left, right, isUnsignedType(exprType) |
|
| 5369 | + | self, binop.op, ilType(self.low, exprType), left, right, isUnsignedType(exprType) |
|
| 5362 | 5370 | ); |
|
| 5363 | 5371 | let assigned = try applyCoercion(self, expr, result); |
|
| 5364 | 5372 | try emitStore(self, place.base, place.offset, place.fieldType, assigned); |
|
| 5365 | 5373 | ||
| 5366 | 5374 | return true; |
|
| 5367 | 5375 | } |
|
| 5368 | 5376 | ||
| 5369 | 5377 | /// Lower an assignment statement. |
|
| 5370 | 5378 | unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) { |
|
| 5371 | 5379 | // Slice assignment: `slice[range] = value`. |
|
| 5372 | - | if let info = resolver::sliceRangeInfoFor(&*self.low.resolver, node) { |
|
| 5380 | + | if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) { |
|
| 5373 | 5381 | let case ast::NodeValue::Subscript { container, index } = a.left.value |
|
| 5374 | 5382 | else panic "lowerAssign: slice assign without subscript"; |
|
| 5375 | 5383 | let case ast::NodeValue::Range(range) = index.value |
|
| 5376 | 5384 | else panic "lowerAssign: slice assign without range"; |
|
| 5377 | 5385 | try lowerSliceAssign(self, a.right, container, range, info); |
| 5647 | 5655 | } |
|
| 5648 | 5656 | ||
| 5649 | 5657 | /// Lower a `for` loop over a range, array, or slice. |
|
| 5650 | 5658 | unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) { |
|
| 5651 | 5659 | let savedVarsLen = enterVarScope(self); |
|
| 5652 | - | let info = resolver::forLoopInfoFor(&*self.low.resolver, node) else { |
|
| 5660 | + | let info = resolver::forLoopInfoFor(self.low.resolver, node) else { |
|
| 5653 | 5661 | throw LowerError::MissingMetadata; |
|
| 5654 | 5662 | }; |
|
| 5655 | 5663 | match info { |
|
| 5656 | 5664 | case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName } => { |
|
| 5657 | 5665 | let endExpr = range.end else { |
| 5660 | 5668 | let mut startVal = il::Val::Imm(0); |
|
| 5661 | 5669 | if let start = range.start { |
|
| 5662 | 5670 | set startVal = try lowerExpr(self, start); |
|
| 5663 | 5671 | } |
|
| 5664 | 5672 | let endVal = try lowerExpr(self, endExpr); |
|
| 5665 | - | let iterType = ilType(&mut *self.low, *valType); |
|
| 5673 | + | let iterType = ilType(self.low, *valType); |
|
| 5666 | 5674 | let valVar = newVar(self, bindingName, iterType, false, startVal); |
|
| 5667 | 5675 | ||
| 5668 | 5676 | let mut indexVar: ?Var = nil; |
|
| 5669 | 5677 | if indexName <> nil { // Optional index always starts at zero. |
|
| 5670 | 5678 | set indexVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0)); |
| 5695 | 5703 | let mut valVar: ?Var = nil; |
|
| 5696 | 5704 | if bindingName <> nil { |
|
| 5697 | 5705 | set valVar = newVar( |
|
| 5698 | 5706 | self, |
|
| 5699 | 5707 | bindingName, |
|
| 5700 | - | ilType(&mut *self.low, *elemType), |
|
| 5708 | + | ilType(self.low, *elemType), |
|
| 5701 | 5709 | false, |
|
| 5702 | 5710 | il::Val::Undef |
|
| 5703 | 5711 | ); |
|
| 5704 | 5712 | } |
|
| 5705 | 5713 | let iter = ForIter::Collection { valVar, idxVar, dataReg, lengthVal, elemType }; |
| 5763 | 5771 | unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) { |
|
| 5764 | 5772 | assert self.fnType.throwList.len > 0; |
|
| 5765 | 5773 | ||
| 5766 | 5774 | let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1 |
|
| 5767 | 5775 | else try typeOf(self, expr); |
|
| 5768 | - | let tag = getOrAssignErrorTag(&mut *self.low, errType) as i64; |
|
| 5776 | + | let tag = getOrAssignErrorTag(self.low, errType) as i64; |
|
| 5769 | 5777 | let errVal = try lowerExpr(self, expr); |
|
| 5770 | 5778 | let resultVal = try buildResult(self, tag, errVal, errType); |
|
| 5771 | 5779 | ||
| 5772 | 5780 | try emitRetVal(self, resultVal); |
|
| 5773 | 5781 | } |
| 5904 | 5912 | ||
| 5905 | 5913 | return il::Val::Reg(dst); |
|
| 5906 | 5914 | } else { |
|
| 5907 | 5915 | try emitCondBranch(self, cond.condition, thenBlock, elseBlock); |
|
| 5908 | 5916 | ||
| 5909 | - | let resultType = ilType(&mut *self.low, typ); |
|
| 5917 | + | let resultType = ilType(self.low, typ); |
|
| 5910 | 5918 | let resultReg = nextReg(self); |
|
| 5911 | 5919 | let mergeBlock = try createBlockWithParam( |
|
| 5912 | 5920 | self, "cond#merge", il::Param { value: resultReg, type: resultType } |
|
| 5913 | 5921 | ); |
|
| 5914 | 5922 | try switchToAndSeal(self, thenBlock); |
| 5975 | 5983 | ||
| 5976 | 5984 | if isComparison { |
|
| 5977 | 5985 | let leftTy = try effectiveType(self, binop.left); |
|
| 5978 | 5986 | let rightTy = try effectiveType(self, binop.right); |
|
| 5979 | 5987 | // Optimize: comparing with a void variant just needs tag comparison. |
|
| 5980 | - | if let idx = voidVariantIndex(&*self.low.resolver, binop.left) { |
|
| 5988 | + | if let idx = voidVariantIndex(self.low.resolver, binop.left) { |
|
| 5981 | 5989 | return try emitTagCmp(self, binop.op, b, idx, rightTy); |
|
| 5982 | - | } else if let idx = voidVariantIndex(&*self.low.resolver, binop.right) { |
|
| 5990 | + | } else if let idx = voidVariantIndex(self.low.resolver, binop.right) { |
|
| 5983 | 5991 | return try emitTagCmp(self, binop.op, a, idx, leftTy); |
|
| 5984 | 5992 | } |
|
| 5985 | 5993 | // Aggregate types require element-wise comparison. |
|
| 5986 | 5994 | // When comparing `?T` with `T`, wrap the scalar side. |
|
| 5987 | 5995 | if isAggregateType(leftTy) { |
| 5995 | 6003 | let lhs = try wrapInOptional(self, a, rightTy); |
|
| 5996 | 6004 | return try emitAggregateEqOp(self, binop.op, rightTy, lhs, b); |
|
| 5997 | 6005 | } |
|
| 5998 | 6006 | set resultTy = scalarComparisonType(leftTy, rightTy); |
|
| 5999 | 6007 | } |
|
| 6000 | - | return emitScalarBinOp(self, binop.op, ilType(&mut *self.low, resultTy), a, b, isUnsignedType(resultTy)); |
|
| 6008 | + | return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy)); |
|
| 6001 | 6009 | } |
|
| 6002 | 6010 | ||
| 6003 | 6011 | /// Emit an aggregate equality or inequality comparison. |
|
| 6004 | 6012 | unsafe fn emitAggregateEqOp( |
|
| 6005 | 6013 | self: &mut FnLowerer, |
| 6116 | 6124 | return il::Val::Imm((0 - lit.magnitude) as i64); |
|
| 6117 | 6125 | } |
|
| 6118 | 6126 | } |
|
| 6119 | 6127 | let val = try lowerExpr(self, unop.value); |
|
| 6120 | 6128 | let t = try typeOf(self, node); |
|
| 6121 | - | let typ = ilType(&mut *self.low, t); |
|
| 6129 | + | let typ = ilType(self.low, t); |
|
| 6122 | 6130 | let dst = nextReg(self); |
|
| 6123 | 6131 | let mut needsExt: bool = false; |
|
| 6124 | 6132 | ||
| 6125 | 6133 | match unop.op { |
|
| 6126 | 6134 | case ast::UnaryOp::Not => { |
| 6179 | 6187 | let sliceTy = try typeOf(self, node); |
|
| 6180 | 6188 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6181 | 6189 | else throw LowerError::ExpectedSliceOrArray; |
|
| 6182 | 6190 | // Build the string data value. |
|
| 6183 | 6191 | let ptr = try! alloc::alloc( |
|
| 6184 | - | &mut *self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6192 | + | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6185 | 6193 | ) as *mut il::DataValue; |
|
| 6186 | 6194 | ||
| 6187 | 6195 | set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 }; |
|
| 6188 | 6196 | let result = ConstDataResult { values: @sliceOf(ptr, 1), zeroInit: false }; |
|
| 6189 | 6197 |
| 6191 | 6199 | self, &result, 1, true, item, mutable, s.len |
|
| 6192 | 6200 | ); |
|
| 6193 | 6201 | } |
|
| 6194 | 6202 | ||
| 6195 | 6203 | /// Lower a builtin call expression. |
|
| 6196 | - | unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6204 | + | unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6197 | 6205 | match kind { |
|
| 6198 | 6206 | case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args), |
|
| 6199 | 6207 | case ast::Builtin::SizeOf, ast::Builtin::AlignOf => { |
|
| 6200 | - | let constVal = resolver::constValueEntry(&*self.low.resolver, node) else { |
|
| 6208 | + | let constVal = resolver::constValueEntry(self.low.resolver, node) else { |
|
| 6201 | 6209 | throw LowerError::MissingConst(node); |
|
| 6202 | 6210 | }; |
|
| 6203 | 6211 | return try constValueToVal(self, constVal, node); |
|
| 6204 | 6212 | } |
|
| 6205 | 6213 | } |
|
| 6206 | 6214 | } |
|
| 6207 | 6215 | ||
| 6208 | 6216 | /// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call. |
|
| 6209 | - | unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6217 | + | unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6210 | 6218 | if args.len <> 2 and args.len <> 3 { |
|
| 6211 | 6219 | throw LowerError::InvalidArgCount; |
|
| 6212 | 6220 | } |
|
| 6213 | 6221 | let sliceTy = try typeOf(self, node); |
|
| 6214 | 6222 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
| 6239 | 6247 | // Type of the try expression, which is either the return type of the function |
|
| 6240 | 6248 | // if successful, or an optional of it, if using `try?`. |
|
| 6241 | 6249 | let tryExprTy = try typeOf(self, node); |
|
| 6242 | 6250 | // Check for trait method dispatch or standalone method call. |
|
| 6243 | 6251 | let mut resVal: il::Val = undefined; |
|
| 6244 | - | let callNodeExtra = resolver::nodeData(&*self.low.resolver, t.expr).extra; |
|
| 6252 | + | let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra; |
|
| 6245 | 6253 | if let case resolver::NodeExtra::TraitMethodCall { |
|
| 6246 | 6254 | traitInfo, methodIndex |
|
| 6247 | 6255 | } = callNodeExtra { |
|
| 6248 | 6256 | set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex); |
|
| 6249 | 6257 | } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra { |
|
| 6250 | - | set resVal = try lowerMethodCall(self, t.expr, callExpr, &*method); |
|
| 6258 | + | set resVal = try lowerMethodCall(self, t.expr, callExpr, method); |
|
| 6251 | 6259 | } else { |
|
| 6252 | 6260 | set resVal = try lowerCall(self, t.expr, callExpr); |
|
| 6253 | 6261 | } |
|
| 6254 | 6262 | let base = emitValToReg(self, resVal); // The result value. |
|
| 6255 | 6263 | let tagReg = resultTagReg(self, base); // The result tag. |
| 6317 | 6325 | let case ast::NodeValue::Ident(name) = binding.value else { |
|
| 6318 | 6326 | throw LowerError::ExpectedIdentifier; |
|
| 6319 | 6327 | }; |
|
| 6320 | 6328 | let errTy = *calleeInfo.throwList[0]; |
|
| 6321 | 6329 | let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET); |
|
| 6322 | - | let _ = newVar(self, name, ilType(&mut *self.low, errTy), false, errVal); |
|
| 6330 | + | let _ = newVar(self, name, ilType(self.low, errTy), false, errVal); |
|
| 6323 | 6331 | } |
|
| 6324 | 6332 | try lowerBlock(self, first.body); |
|
| 6325 | 6333 | try emitMergeIfUnterminated(self, &mut mergeBlock); |
|
| 6326 | 6334 | exitVarScope(self, savedVarsLen); |
|
| 6327 | 6335 | } |
| 6370 | 6378 | /// Emits a switch on the global error tag to dispatch to the correct catch |
|
| 6371 | 6379 | /// clause. Each typed clause extracts the error payload for its specific type |
|
| 6372 | 6380 | /// and binds it to the clause's identifier. |
|
| 6373 | 6381 | unsafe fn lowerMultiCatch( |
|
| 6374 | 6382 | self: &mut FnLowerer, |
|
| 6375 | - | catches: *mut [*ast::Node], |
|
| 6383 | + | catches: *[*ast::Node], |
|
| 6376 | 6384 | calleeInfo: *resolver::FnType, |
|
| 6377 | 6385 | base: il::Reg, |
|
| 6378 | 6386 | tagReg: il::Reg, |
|
| 6379 | 6387 | mergeBlock: &mut ?BlockId |
|
| 6380 | 6388 | ) throws (LowerError) { |
|
| 6381 | 6389 | let entry = currentBlock(self); |
|
| 6382 | 6390 | ||
| 6383 | 6391 | // First pass: create blocks, resolve error types, and build switch cases. |
|
| 6384 | 6392 | let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined; |
|
| 6385 | 6393 | let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined; |
|
| 6386 | - | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 6394 | + | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 6387 | 6395 | let mut defaultIdx: ?u32 = nil; |
|
| 6388 | 6396 | ||
| 6389 | 6397 | for clauseNode, i in catches { |
|
| 6390 | 6398 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value |
|
| 6391 | 6399 | else panic "lowerMultiCatch: expected CatchClause"; |
| 6396 | 6404 | if let typeNode = clause.typeNode { |
|
| 6397 | 6405 | let errTy = try typeOf(self, typeNode); |
|
| 6398 | 6406 | set errTypes[i] = errTy; |
|
| 6399 | 6407 | ||
| 6400 | 6408 | cases.append(il::SwitchCase { |
|
| 6401 | - | value: getOrAssignErrorTag(&mut *self.low, errTy) as i64, |
|
| 6409 | + | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6402 | 6410 | target: *blocks[i], |
|
| 6403 | 6411 | args: &mut [] |
|
| 6404 | 6412 | }, self.allocator); |
|
| 6405 | 6413 | } else { |
|
| 6406 | 6414 | set errTypes[i] = nil; |
| 6436 | 6444 | throw LowerError::ExpectedIdentifier; |
|
| 6437 | 6445 | }; |
|
| 6438 | 6446 | let errTy = errTypes[i] else panic "lowerMultiCatch: catch-all with binding"; |
|
| 6439 | 6447 | let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET); |
|
| 6440 | 6448 | ||
| 6441 | - | newVar(self, name, ilType(&mut *self.low, errTy), false, errVal); |
|
| 6449 | + | newVar(self, name, ilType(self.low, errTy), false, errVal); |
|
| 6442 | 6450 | } |
|
| 6443 | 6451 | try lowerBlock(self, clause.body); |
|
| 6444 | 6452 | try emitMergeIfUnterminated(self, mergeBlock); |
|
| 6445 | 6453 | ||
| 6446 | 6454 | exitVarScope(self, savedVarsLen); |
| 6642 | 6650 | emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET); |
|
| 6643 | 6651 | } |
|
| 6644 | 6652 | ||
| 6645 | 6653 | /// Lower a call expression, which may be a function call or type constructor. |
|
| 6646 | 6654 | unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6647 | - | let nodeData = resolver::nodeData(&*self.low.resolver, node).extra; |
|
| 6655 | + | let nodeData = resolver::nodeData(self.low.resolver, node).extra; |
|
| 6648 | 6656 | ||
| 6649 | 6657 | // Check for slice method dispatch. |
|
| 6650 | 6658 | if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData { |
|
| 6651 | 6659 | return try lowerSliceAppend(self, call, elemType); |
|
| 6652 | 6660 | } |
| 6658 | 6666 | if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData { |
|
| 6659 | 6667 | return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex); |
|
| 6660 | 6668 | } |
|
| 6661 | 6669 | // Check for standalone method call. |
|
| 6662 | 6670 | if let case resolver::NodeExtra::MethodCall { method } = nodeData { |
|
| 6663 | - | return try lowerMethodCall(self, node, call, &*method); |
|
| 6671 | + | return try lowerMethodCall(self, node, call, method); |
|
| 6664 | 6672 | } |
|
| 6665 | - | if let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym { |
|
| 6673 | + | if let sym = resolver::nodeData(self.low.resolver, call.callee).sym { |
|
| 6666 | 6674 | if let case resolver::SymbolData::Type(nominal) = sym.data { |
|
| 6667 | 6675 | let case resolver::NominalType::Record(_) = *nominal else { |
|
| 6668 | 6676 | throw LowerError::ExpectedRecord; |
|
| 6669 | 6677 | }; |
|
| 6670 | 6678 | return try lowerRecordCtor(self, nominal, call.args); |
| 6687 | 6695 | /// |
|
| 6688 | 6696 | unsafe fn lowerTraitMethodCall( |
|
| 6689 | 6697 | self: &mut FnLowerer, |
|
| 6690 | 6698 | node: *ast::Node, |
|
| 6691 | 6699 | call: ast::Call, |
|
| 6692 | - | traitInfo: *resolver::TraitType, |
|
| 6700 | + | traitInfo: *unsafe resolver::TraitType, |
|
| 6693 | 6701 | methodIndex: u32 |
|
| 6694 | 6702 | ) -> il::Val throws (LowerError) { |
|
| 6695 | 6703 | // Method calls look like field accesses. |
|
| 6696 | 6704 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6697 | 6705 | else throw LowerError::MissingMetadata; |
| 6769 | 6777 | /// return parameter; that slot is filled by this function. |
|
| 6770 | 6778 | unsafe fn emitCallValue( |
|
| 6771 | 6779 | self: &mut FnLowerer, |
|
| 6772 | 6780 | callee: il::Val, |
|
| 6773 | 6781 | fnInfo: *resolver::FnType, |
|
| 6774 | - | args: *mut [il::Val], |
|
| 6782 | + | args: *unsafe mut [il::Val], |
|
| 6775 | 6783 | ) -> il::Val throws (LowerError) { |
|
| 6776 | 6784 | let retTy = *fnInfo.returnType; |
|
| 6777 | 6785 | ||
| 6778 | 6786 | if requiresReturnParam(fnInfo) { |
|
| 6779 | 6787 | if fnInfo.throwList.len > 0 { |
| 6795 | 6803 | let mut dst: ?il::Reg = nil; |
|
| 6796 | 6804 | if retTy <> resolver::Type::Void { |
|
| 6797 | 6805 | set dst = nextReg(self); |
|
| 6798 | 6806 | } |
|
| 6799 | 6807 | emit(self, il::Instr::Call { |
|
| 6800 | - | retTy: ilType(&mut *self.low, retTy), |
|
| 6808 | + | retTy: ilType(self.low, retTy), |
|
| 6801 | 6809 | dst, |
|
| 6802 | 6810 | func: callee, |
|
| 6803 | 6811 | args, |
|
| 6804 | 6812 | }); |
|
| 6805 | 6813 |
| 6838 | 6846 | let val = try lowerExpr(self, parent); |
|
| 6839 | 6847 | if isAggregateType(parentTy) { |
|
| 6840 | 6848 | return val; |
|
| 6841 | 6849 | } |
|
| 6842 | 6850 | // Scalar value: store to a stack slot and return the slot pointer. |
|
| 6843 | - | let layout = resolver::getLayout(&*self.low.resolver, parent, parentTy); |
|
| 6851 | + | let layout = resolver::getLayout(self.low.resolver, parent, parentTy); |
|
| 6844 | 6852 | let slot = emitReserveLayout(self, layout); |
|
| 6845 | 6853 | try emitStore(self, slot, 0, parentTy, val); |
|
| 6846 | 6854 | ||
| 6847 | 6855 | return il::Val::Reg(slot); |
|
| 6848 | 6856 | } |
| 6865 | 6873 | ||
| 6866 | 6874 | // Get the receiver as a pointer. |
|
| 6867 | 6875 | let parentTy = try typeOf(self, access.parent); |
|
| 6868 | 6876 | let receiverVal = try lowerReceiver(self, access.parent, parentTy); |
|
| 6869 | 6877 | ||
| 6870 | - | let qualName = instanceMethodName(&mut *self.low, nil, method.concreteTypeName, method.name); |
|
| 6878 | + | let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name); |
|
| 6871 | 6879 | let case resolver::SymbolData::Value { type: resolver::Type::Fn(fnInfo), .. } = method.symbol.data |
|
| 6872 | 6880 | else panic "lowerMethodCall: expected Fn type on method symbol"; |
|
| 6873 | 6881 | ||
| 6874 | 6882 | // Build args: optional return param slot + receiver + user args. |
|
| 6875 | 6883 | let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0; |
| 6884 | 6892 | } |
|
| 6885 | 6893 | ||
| 6886 | 6894 | /// Check if a call is to a compiler intrinsic and lower it directly. |
|
| 6887 | 6895 | unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) { |
|
| 6888 | 6896 | // Get the callee symbol and check if it's marked as an intrinsic. |
|
| 6889 | - | let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym else { |
|
| 6897 | + | let sym = resolver::nodeData(self.low.resolver, call.callee).sym else { |
|
| 6890 | 6898 | // Expressions or function pointers may not have an associated symbol. |
|
| 6891 | 6899 | return nil; |
|
| 6892 | 6900 | }; |
|
| 6893 | 6901 | if not ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic) { |
|
| 6894 | 6902 | return nil; |
| 6943 | 6951 | ||
| 6944 | 6952 | /// Resolve callee to an IL value. For direct function calls, use the symbol name. |
|
| 6945 | 6953 | /// For variables holding function pointers or complex expressions (eg. `array[i]()`), |
|
| 6946 | 6954 | /// lower the callee expression. |
|
| 6947 | 6955 | unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) { |
|
| 6948 | - | if let sym = resolver::nodeData(&*self.low.resolver, callee).sym { |
|
| 6956 | + | if let sym = resolver::nodeData(self.low.resolver, callee).sym { |
|
| 6949 | 6957 | if let case ast::NodeValue::FnDecl(_) = sym.node.value { |
|
| 6950 | 6958 | // First try to look up the symbol in our registered functions. |
|
| 6951 | 6959 | // This handles cross-package calls correctly, since packages are |
|
| 6952 | 6960 | // lowered in dependency order. |
|
| 6953 | - | if let qualName = lookupFnSym(&mut *self.low, sym) { |
|
| 6961 | + | if let qualName = lookupFnSym(self.low, sym) { |
|
| 6954 | 6962 | return il::Val::FnAddr(qualName); |
|
| 6955 | 6963 | } |
|
| 6956 | 6964 | // Fall back to computing the qualified name from the module graph. |
|
| 6957 | 6965 | // This works for functions in the current package. |
|
| 6958 | - | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym) else { |
|
| 6966 | + | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else { |
|
| 6959 | 6967 | throw LowerError::MissingMetadata; |
|
| 6960 | 6968 | }; |
|
| 6961 | - | return il::Val::FnAddr(qualifyName(&mut *self.low, modId, sym.name)); |
|
| 6969 | + | return il::Val::FnAddr(qualifyName(self.low, modId, sym.name)); |
|
| 6962 | 6970 | } |
|
| 6963 | 6971 | } |
|
| 6964 | 6972 | return try lowerExpr(self, callee); |
|
| 6965 | 6973 | } |
|
| 6966 | 6974 |
| 6986 | 6994 | return try emitCallValue(self, callee, fnInfo, args); |
|
| 6987 | 6995 | } |
|
| 6988 | 6996 | ||
| 6989 | 6997 | /// Apply coercions requested by the resolver. |
|
| 6990 | 6998 | unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) { |
|
| 6991 | - | let coerce = resolver::coercionFor(&*self.low.resolver, node) else { |
|
| 6999 | + | let coerce = resolver::coercionFor(self.low.resolver, node) else { |
|
| 6992 | 7000 | return val; |
|
| 6993 | 7001 | }; |
|
| 6994 | 7002 | match coerce { |
|
| 6995 | 7003 | case resolver::Coercion::OptionalLift(optType) => { |
|
| 6996 | 7004 | if let case ast::NodeValue::Nil = node.value { |
| 7004 | 7012 | case resolver::Coercion::ResultWrap => { |
|
| 7005 | 7013 | let payloadType = *self.fnType.returnType; |
|
| 7006 | 7014 | return try buildResult(self, 0, val, payloadType); |
|
| 7007 | 7015 | } |
|
| 7008 | 7016 | case resolver::Coercion::TraitObject { traitInfo, inst } => { |
|
| 7009 | - | return try buildTraitObject(self, val, traitInfo, &*inst); |
|
| 7017 | + | return try buildTraitObject(self, val, traitInfo, inst); |
|
| 7010 | 7018 | } |
|
| 7011 | 7019 | case resolver::Coercion::Identity => return val, |
|
| 7012 | 7020 | } |
|
| 7013 | 7021 | } |
|
| 7014 | 7022 |
| 7025 | 7033 | return val; |
|
| 7026 | 7034 | } |
|
| 7027 | 7035 | // Widening: extend based on source signedness. |
|
| 7028 | 7036 | // Narrowing: truncate and normalize to destination width. |
|
| 7029 | 7037 | let widening = srcLayout.size < dstLayout.size; |
|
| 7030 | - | let extType = ilType(&mut *self.low, srcType) if widening else ilType(&mut *self.low, dstType); |
|
| 7038 | + | let extType = ilType(self.low, srcType) if widening else ilType(self.low, dstType); |
|
| 7031 | 7039 | let signed = isSignedType(srcType) if widening else isSignedType(dstType); |
|
| 7032 | 7040 | let dst = nextReg(self); |
|
| 7033 | 7041 | ||
| 7034 | 7042 | if signed { |
|
| 7035 | 7043 | emit(self, il::Instr::Sext { typ: extType, dst, val }); |
| 7038 | 7046 | } |
|
| 7039 | 7047 | return il::Val::Reg(dst); |
|
| 7040 | 7048 | } |
|
| 7041 | 7049 | ||
| 7042 | 7050 | /// Lower a global value symbol. |
|
| 7043 | - | unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 7051 | + | unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 7044 | 7052 | // Function pointer reference: return the function's address directly. |
|
| 7045 | 7053 | // Functions have no separate storage cell in the data section. |
|
| 7046 | 7054 | if let case resolver::Type::Fn(_) = ty { |
|
| 7047 | 7055 | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 7048 | 7056 | } |
| 7052 | 7060 | } |
|
| 7053 | 7061 | ||
| 7054 | 7062 | /// Lower an identifier that refers to a global symbol. |
|
| 7055 | 7063 | unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7056 | 7064 | // First try to get a compile-time constant value. |
|
| 7057 | - | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7065 | + | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7058 | 7066 | return try constValueToVal(self, constVal, node); |
|
| 7059 | 7067 | } |
|
| 7060 | 7068 | // Otherwise get the symbol. |
|
| 7061 | 7069 | let sym = try symOf(self, node); |
|
| 7062 | 7070 |
| 7085 | 7093 | ||
| 7086 | 7094 | /// Lower a scope access expression like `Module::Const` or `Union::Variant`. |
|
| 7087 | 7095 | /// This doesn't handle record literal variants. |
|
| 7088 | 7096 | unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7089 | 7097 | // First try to get a compile-time constant value. |
|
| 7090 | - | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7098 | + | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7091 | 7099 | return try constValueToVal(self, constVal, node); |
|
| 7092 | 7100 | } |
|
| 7093 | 7101 | // Otherwise get the associated symbol. |
|
| 7094 | - | let data = resolver::nodeData(&*self.low.resolver, node); |
|
| 7102 | + | let data = resolver::nodeData(self.low.resolver, node); |
|
| 7095 | 7103 | let sym = data.sym else { |
|
| 7096 | 7104 | throw LowerError::MissingSymbol(node); |
|
| 7097 | 7105 | }; |
|
| 7098 | 7106 | match sym.data { |
|
| 7099 | 7107 | case resolver::SymbolData::Variant { index, .. } => { |
|
| 7100 | 7108 | let mut indexValue = index as i64; |
|
| 7101 | - | if let idx = voidVariantIndex(&*self.low.resolver, node) { |
|
| 7109 | + | if let idx = voidVariantIndex(self.low.resolver, node) { |
|
| 7102 | 7110 | set indexValue = idx; |
|
| 7103 | 7111 | } |
|
| 7104 | 7112 | // Void union variant like `Option::None`. |
|
| 7105 | 7113 | if data.ty == resolver::Type::Unknown { |
|
| 7106 | 7114 | throw LowerError::MissingType(node); |
| 7214 | 7222 | case ast::NodeValue::Try(t) => { |
|
| 7215 | 7223 | set val = try lowerTry(self, node, t); |
|
| 7216 | 7224 | } |
|
| 7217 | 7225 | case ast::NodeValue::FieldAccess(access) => { |
|
| 7218 | 7226 | // Check for compile-time constant (e.g., `arr.len` on fixed-size arrays). |
|
| 7219 | - | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7227 | + | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7220 | 7228 | match constVal { |
|
| 7221 | 7229 | // TODO: Handle `u32` values that don't fit in an `i32`. |
|
| 7222 | 7230 | // Perhaps just store the `ConstInt`. |
|
| 7223 | 7231 | case resolver::ConstValue::Int(i) => set val = il::Val::Imm(constIntToI64(i)), |
|
| 7224 | 7232 | else => set val = try lowerFieldAccess(self, access), |
| 7273 | 7281 | set val = il::Val::Undef; |
|
| 7274 | 7282 | } |
|
| 7275 | 7283 | // Lower these as statements. |
|
| 7276 | 7284 | case ast::NodeValue::ConstDecl(decl) => { |
|
| 7277 | 7285 | try registerLocalDataDeclName(self, node); |
|
| 7278 | - | try lowerDataDecl(&mut *self.low, node, decl.value, true); |
|
| 7286 | + | try lowerDataDecl(self.low, node, decl.value, true); |
|
| 7279 | 7287 | set val = il::Val::Undef; |
|
| 7280 | 7288 | } |
|
| 7281 | 7289 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 7282 | 7290 | try registerLocalDataDeclName(self, node); |
|
| 7283 | - | try lowerDataDecl(&mut *self.low, node, decl.value, false); |
|
| 7291 | + | try lowerDataDecl(self.low, node, decl.value, false); |
|
| 7284 | 7292 | set val = il::Val::Undef; |
|
| 7285 | 7293 | } |
|
| 7286 | 7294 | case ast::NodeValue::Throw { .. }, |
|
| 7287 | 7295 | ast::NodeValue::Return { .. }, |
|
| 7288 | 7296 | ast::NodeValue::Continue, |
| 7303 | 7311 | /// are used. If a Radiance type doesn't fit in a machine word, it is passed |
|
| 7304 | 7312 | /// by reference. |
|
| 7305 | 7313 | /// |
|
| 7306 | 7314 | /// The IL doesn't track signedness - that's encoded in the instructions |
|
| 7307 | 7315 | /// (e.g., Slt vs Ult). |
|
| 7308 | - | fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type { |
|
| 7316 | + | unsafe fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type { |
|
| 7309 | 7317 | match typ { |
|
| 7310 | 7318 | case resolver::Type::Bool, |
|
| 7311 | 7319 | resolver::Type::I8, |
|
| 7312 | 7320 | resolver::Type::U8 => return il::Type::W8, |
|
| 7313 | 7321 | case resolver::Type::I16, |
lib/std/lang/module.rad
+18 -22
| 77 | 77 | /// Child module identifiers declared directly inside this module. |
|
| 78 | 78 | children: [u16; MAX_MODULES], |
|
| 79 | 79 | /// Number of entries stored in `children`. |
|
| 80 | 80 | childrenLen: u32, |
|
| 81 | 81 | /// Parsed AST root for this module when available. |
|
| 82 | - | ast: ?*mut ast::Node, |
|
| 82 | + | ast: ?*ast::Node, |
|
| 83 | 83 | /// Source text for this module (for error reporting). |
|
| 84 | 84 | source: ?*[u8], |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 88 | - | export record ModuleGraph: Copy { |
|
| 88 | + | export record ModuleGraph { |
|
| 89 | 89 | /// Permanent storage for module entries. |
|
| 90 | 90 | entries: *mut [ModuleEntry], |
|
| 91 | 91 | /// Number of initialized entries. |
|
| 92 | 92 | entriesLen: u32, |
|
| 93 | - | /// Permanent storage for interned names. |
|
| 94 | - | pool: *mut strings::Pool, |
|
| 95 | 93 | /// AST arena. It must outlive the graph. |
|
| 96 | 94 | arena: ?*unsafe ast::NodeArena, |
|
| 97 | 95 | } |
|
| 98 | 96 | ||
| 99 | 97 | /// Initialize an empty module graph backed by the provided storage. |
|
| 100 | 98 | /// The AST arena must outlive the returned graph. |
|
| 101 | 99 | export unsafe fn moduleGraph( |
|
| 102 | 100 | storage: *mut [ModuleEntry], |
|
| 103 | - | pool: *mut strings::Pool, |
|
| 104 | 101 | arena: &mut ast::NodeArena |
|
| 105 | 102 | ) -> ModuleGraph { |
|
| 106 | 103 | return ModuleGraph { |
|
| 107 | 104 | entries: storage, |
|
| 108 | 105 | entriesLen: 0, |
|
| 109 | - | pool, |
|
| 110 | 106 | arena: arena as *unsafe ast::NodeArena, |
|
| 111 | 107 | }; |
|
| 112 | 108 | } |
|
| 113 | 109 | ||
| 114 | 110 | /// Register a root module residing at `path` for a package. |
|
| 115 | - | export fn registerRoot(graph: &mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 111 | + | export fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 116 | 112 | let name = try basenameSlice(filePath); |
|
| 117 | - | return try registerRootWithName(graph, packageId, name, filePath); |
|
| 113 | + | return try registerRootWithName(graph, pool, packageId, name, filePath); |
|
| 118 | 114 | } |
|
| 119 | 115 | ||
| 120 | 116 | /// Register a root module with an explicit name and file path for a package. |
|
| 121 | 117 | export fn registerRootWithName( |
|
| 122 | - | graph: &mut ModuleGraph, |
|
| 118 | + | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 123 | 119 | packageId: u16, |
|
| 124 | 120 | name: *[u8], |
|
| 125 | 121 | filePath: *[u8] |
|
| 126 | 122 | ) -> u16 throws (ModuleError) { |
|
| 127 | - | let m = try allocModule(graph, packageId, name, filePath); |
|
| 128 | - | try appendPathSegment(m, m.name); |
|
| 123 | + | let m = try allocModule(graph, pool, packageId, name, filePath); |
|
| 124 | + | try appendPathSegment(m, name); |
|
| 129 | 125 | ||
| 130 | 126 | set m.state = ModuleState::Registered; |
|
| 131 | 127 | ||
| 132 | 128 | return m.id; |
|
| 133 | 129 | } |
|
| 134 | 130 | ||
| 135 | 131 | /// Register a child module. |
|
| 136 | 132 | /// Returns the module identifier. |
|
| 137 | 133 | export fn registerChild( |
|
| 138 | - | graph: &mut ModuleGraph, |
|
| 134 | + | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 139 | 135 | parentId: u16, |
|
| 140 | 136 | name: *[u8], |
|
| 141 | 137 | filePath: *[u8] |
|
| 142 | 138 | ) -> u16 throws (ModuleError) { |
|
| 143 | 139 | assert name.len > 0, "registerChild: name must not be empty"; |
| 149 | 145 | // If the child already exists under this parent, return it. |
|
| 150 | 146 | if let child = findChild(graph, name, parentId) { |
|
| 151 | 147 | return child.id; |
|
| 152 | 148 | } |
|
| 153 | 149 | // Inherit packageId from parent. |
|
| 154 | - | let m = try allocModule(graph, parent.packageId, name, filePath); |
|
| 150 | + | let m = try allocModule(graph, pool, parent.packageId, name, filePath); |
|
| 155 | 151 | ||
| 156 | 152 | set m.state = ModuleState::Registered; |
|
| 157 | 153 | set m.parent = parentId; |
|
| 158 | 154 | ||
| 159 | 155 | // Inherit path prefix from parent. |
|
| 160 | 156 | for p in moduleQualifiedPath(parent) { |
|
| 161 | 157 | try appendPathSegment(m, p); |
|
| 162 | 158 | } |
|
| 163 | - | try appendPathSegment(m, m.name); |
|
| 159 | + | try appendPathSegment(m, name); |
|
| 164 | 160 | ||
| 165 | 161 | return try addChild(parent, m.id); |
|
| 166 | 162 | } |
|
| 167 | 163 | ||
| 168 | 164 | /// Fetch a read-only view of the module identified by `id`. |
| 205 | 201 | }; |
|
| 206 | 202 | return m.state; |
|
| 207 | 203 | } |
|
| 208 | 204 | ||
| 209 | 205 | /// Record the parsed AST root for `id`. |
|
| 210 | - | export fn setAst(graph: &mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) { |
|
| 206 | + | export fn setAst(graph: &mut ModuleGraph, id: u16, root: *ast::Node) throws (ModuleError) { |
|
| 211 | 207 | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 212 | 208 | set m.ast = root; |
|
| 213 | 209 | set m.state = ModuleState::Parsed; |
|
| 214 | 210 | } |
|
| 215 | 211 |
| 270 | 266 | ||
| 271 | 267 | /// Register a module from a file path, creating the full hierarchy as needed. |
|
| 272 | 268 | /// The path is split into components and the module hierarchy is built accordingly. |
|
| 273 | 269 | /// If `rootId` is `nil`, registers a new root for the given package. |
|
| 274 | 270 | /// Returns the module ID of the last component. |
|
| 275 | - | export fn registerFromPath(graph: &mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 271 | + | export fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 276 | 272 | let root = rootId else { |
|
| 277 | - | return try registerRoot(graph, packageId, filePath); |
|
| 273 | + | return try registerRoot(graph, pool, packageId, filePath); |
|
| 278 | 274 | }; |
|
| 279 | 275 | let rootEntry = get(graph, root) else { |
|
| 280 | 276 | panic "registerFromPath: root is missing from storage"; |
|
| 281 | 277 | }; |
|
| 282 | 278 |
| 311 | 307 | let child = findChild(graph, part, parentId) else { |
|
| 312 | 308 | throw ModuleError::MissingParent; |
|
| 313 | 309 | }; |
|
| 314 | 310 | set parentId = child.id; |
|
| 315 | 311 | } |
|
| 316 | - | return try registerChild(graph, parentId, childName, filePath); |
|
| 312 | + | return try registerChild(graph, pool, parentId, childName, filePath); |
|
| 317 | 313 | } |
|
| 318 | 314 | ||
| 319 | 315 | /// Allocate a fresh entry in the graph. |
|
| 320 | - | fn allocModule(graph: &mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 316 | + | fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 321 | 317 | if graph.entriesLen >= graph.entries.len { |
|
| 322 | 318 | throw ModuleError::CapacityExceeded; |
|
| 323 | 319 | } |
|
| 324 | 320 | let idx = graph.entriesLen; |
|
| 325 | 321 | set graph.entriesLen += 1; |
| 330 | 326 | id: idx as u16, |
|
| 331 | 327 | packageId, |
|
| 332 | 328 | parent: nil, |
|
| 333 | 329 | filePath, |
|
| 334 | 330 | dirLen: 0, |
|
| 335 | - | name: strings::intern(graph.pool, name), |
|
| 331 | + | name: strings::intern(pool, name), |
|
| 336 | 332 | path: [""; MAX_MODULE_PATH_DEPTH], |
|
| 337 | 333 | pathDepth: 0, |
|
| 338 | 334 | state: ModuleState::Vacant, |
|
| 339 | 335 | children: [0; MAX_MODULES], |
|
| 340 | 336 | childrenLen: 0, |
| 345 | 341 | ||
| 346 | 342 | return m; |
|
| 347 | 343 | } |
|
| 348 | 344 | ||
| 349 | 345 | /// Append a logical path segment (module identifier) to the entry. |
|
| 350 | - | fn appendPathSegment(entry: *mut ModuleEntry, segment: *[u8]) throws (ModuleError) { |
|
| 346 | + | fn appendPathSegment(entry: &mut ModuleEntry, segment: *[u8]) throws (ModuleError) { |
|
| 351 | 347 | if entry.pathDepth >= entry.path.len { |
|
| 352 | 348 | throw ModuleError::PathTooDeep; |
|
| 353 | 349 | } |
|
| 354 | 350 | set entry.path[entry.pathDepth] = segment; |
|
| 355 | 351 | set entry.pathDepth += 1; |
|
| 356 | 352 | } |
|
| 357 | 353 | ||
| 358 | 354 | /// Append a child identifier to the parent's child list. |
|
| 359 | - | fn addChild(parent: *mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 355 | + | fn addChild(parent: &mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 360 | 356 | if parent.childrenLen >= parent.children.len { |
|
| 361 | 357 | throw ModuleError::CapacityExceeded; |
|
| 362 | 358 | } |
|
| 363 | 359 | set parent.children[parent.childrenLen] = childId; |
|
| 364 | 360 | set parent.childrenLen += 1; |
lib/std/lang/module/tests.rad
+24 -24
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | @test unsafe fn testRegisterChildren() throws (testing::TestError) { |
|
| 32 | 32 | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 33 | 33 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 34 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 34 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 35 | 35 | ||
| 36 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/root.rad") catch { |
|
| 36 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch { |
|
| 37 | 37 | throw testing::TestError::Failed; |
|
| 38 | 38 | }; |
|
| 39 | 39 | let root = super::get(&graph, rootId) else { |
|
| 40 | 40 | throw testing::TestError::Failed; |
|
| 41 | 41 | }; |
|
| 42 | 42 | try expectPathSegments(root, &["root"]); |
|
| 43 | 43 | ||
| 44 | 44 | // First child. |
|
| 45 | - | let firstId = try super::registerFromPath(&mut graph, 0, rootId, "src/root/first.rad") catch { |
|
| 45 | + | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/first.rad") catch { |
|
| 46 | 46 | throw testing::TestError::Failed; |
|
| 47 | 47 | }; |
|
| 48 | 48 | let first = super::get(&graph, firstId) else { |
|
| 49 | 49 | throw testing::TestError::Failed; |
|
| 50 | 50 | }; |
|
| 51 | 51 | try expectSliceEq(first.filePath, "src/root/first.rad"); |
|
| 52 | 52 | try expectSliceEq(first.name, "first"); |
|
| 53 | 53 | try expectPathSegments(first, &["root", "first"]); |
|
| 54 | 54 | ||
| 55 | 55 | // Second child. |
|
| 56 | - | let secondId = try super::registerFromPath(&mut graph, 0, rootId, "src/root/second.rad") catch { |
|
| 56 | + | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/second.rad") catch { |
|
| 57 | 57 | throw testing::TestError::Failed; |
|
| 58 | 58 | }; |
|
| 59 | 59 | let second = super::get(&graph, secondId) else { |
|
| 60 | 60 | throw testing::TestError::Failed; |
|
| 61 | 61 | }; |
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | @test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) { |
|
| 76 | 76 | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 77 | 77 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 78 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 78 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 79 | 79 | ||
| 80 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/main.rad") catch { |
|
| 80 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch { |
|
| 81 | 81 | throw testing::TestError::Failed; |
|
| 82 | 82 | }; |
|
| 83 | - | let firstId = try super::registerFromPath(&mut graph, 0, rootId, "src/main/util.rad") catch { |
|
| 83 | + | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 84 | 84 | throw testing::TestError::Failed; |
|
| 85 | 85 | }; |
|
| 86 | - | let secondId = try super::registerFromPath(&mut graph, 0, rootId, "src/main/util.rad") catch { |
|
| 86 | + | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 87 | 87 | throw testing::TestError::Failed; |
|
| 88 | 88 | }; |
|
| 89 | 89 | try testing::expect(firstId == secondId); |
|
| 90 | 90 | ||
| 91 | 91 | let parent = super::get(&graph, rootId) else { |
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | @test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) { |
|
| 147 | 147 | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 148 | 148 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 149 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 149 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 150 | 150 | ||
| 151 | 151 | // Register root module. |
|
| 152 | - | let stdId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch { |
|
| 152 | + | let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 153 | 153 | throw testing::TestError::Failed; |
|
| 154 | 154 | }; |
|
| 155 | 155 | let std = super::get(&graph, stdId) else { |
|
| 156 | 156 | throw testing::TestError::Failed; |
|
| 157 | 157 | }; |
|
| 158 | 158 | try expectSliceEq(std.filePath, "lib/std.rad"); |
|
| 159 | 159 | try expectSliceEq(super::moduleDir(std), "lib/"); |
|
| 160 | 160 | try expectPathSegments(std, &["std"]); |
|
| 161 | 161 | ||
| 162 | 162 | // Register child of root. |
|
| 163 | - | let langId = try super::registerFromPath(&mut graph, 0, stdId, "lib/std/lang.rad") catch { |
|
| 163 | + | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang.rad") catch { |
|
| 164 | 164 | throw testing::TestError::Failed; |
|
| 165 | 165 | }; |
|
| 166 | 166 | let lang = super::get(&graph, langId) else { |
|
| 167 | 167 | throw testing::TestError::Failed; |
|
| 168 | 168 | }; |
| 170 | 170 | try expectSliceEq(lang.name, "lang"); |
|
| 171 | 171 | try expectSliceEq(super::moduleDir(lang), "lib/std/"); |
|
| 172 | 172 | try expectPathSegments(lang, &["std", "lang"]); |
|
| 173 | 173 | ||
| 174 | 174 | // Register grandchild. |
|
| 175 | - | let parserId = try super::registerFromPath(&mut graph, 0, stdId, "lib/std/lang/parser.rad") catch { |
|
| 175 | + | let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang/parser.rad") catch { |
|
| 176 | 176 | throw testing::TestError::Failed; |
|
| 177 | 177 | }; |
|
| 178 | 178 | let parser = super::get(&graph, parserId) else { |
|
| 179 | 179 | throw testing::TestError::Failed; |
|
| 180 | 180 | }; |
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | 195 | @test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) { |
|
| 196 | 196 | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 197 | 197 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 198 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 198 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 199 | 199 | ||
| 200 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch { |
|
| 200 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 201 | 201 | throw testing::TestError::Failed; |
|
| 202 | 202 | }; |
|
| 203 | - | let _ = try super::registerFromPath(&mut graph, 0, rootId, "std/lang/parser.rad") catch { |
|
| 203 | + | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "std/lang/parser.rad") catch { |
|
| 204 | 204 | // Expected to fail due to missing intermediate parent. |
|
| 205 | 205 | return; |
|
| 206 | 206 | }; |
|
| 207 | 207 | throw testing::TestError::Failed; |
|
| 208 | 208 | } |
|
| 209 | 209 | ||
| 210 | 210 | @test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) { |
|
| 211 | 211 | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 212 | 212 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 213 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 213 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 214 | 214 | ||
| 215 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch { |
|
| 215 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 216 | 216 | throw testing::TestError::Failed; |
|
| 217 | 217 | }; |
|
| 218 | - | let _ = try super::registerFromPath(&mut graph, 0, rootId, "parser.rad") catch { |
|
| 218 | + | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "parser.rad") catch { |
|
| 219 | 219 | // Expected to fail due to missing intermediate parent. |
|
| 220 | 220 | return; |
|
| 221 | 221 | }; |
|
| 222 | 222 | throw testing::TestError::Failed; |
|
| 223 | 223 | } |
|
| 224 | 224 | ||
| 225 | 225 | @test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) { |
|
| 226 | 226 | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 227 | 227 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 228 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 228 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 229 | 229 | ||
| 230 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch { |
|
| 230 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 231 | 231 | throw testing::TestError::Failed; |
|
| 232 | 232 | }; |
|
| 233 | 233 | // Verify root module was registered with correct ID. |
|
| 234 | 234 | try testing::expect(rootId == 0); |
|
| 235 | 235 |
| 242 | 242 | } |
|
| 243 | 243 | ||
| 244 | 244 | @test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) { |
|
| 245 | 245 | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 246 | 246 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 247 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 247 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 248 | 248 | ||
| 249 | - | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/pkg/root.rad") catch { |
|
| 249 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch { |
|
| 250 | 250 | throw testing::TestError::Failed; |
|
| 251 | 251 | }; |
|
| 252 | 252 | // Verify root module was registered with correct ID. |
|
| 253 | 253 | try testing::expect(rootId == 0); |
|
| 254 | 254 | ||
| 255 | - | let langId = try super::registerFromPath(&mut graph, 0, rootId, "src/pkg/root/lang.rad") catch { |
|
| 255 | + | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang.rad") catch { |
|
| 256 | 256 | throw testing::TestError::Failed; |
|
| 257 | 257 | }; |
|
| 258 | 258 | let lang = super::get(&graph, langId) else { |
|
| 259 | 259 | throw testing::TestError::Failed; |
|
| 260 | 260 | }; |
|
| 261 | 261 | try expectSliceEq(lang.name, "lang"); |
|
| 262 | 262 | try expectPathSegments(lang, &["root", "lang"]); |
|
| 263 | 263 | ||
| 264 | - | let parserId = try super::registerFromPath(&mut graph, 0, rootId, "src/pkg/root/lang/parser.rad") catch { |
|
| 264 | + | let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang/parser.rad") catch { |
|
| 265 | 265 | throw testing::TestError::Failed; |
|
| 266 | 266 | }; |
|
| 267 | 267 | let parser = super::get(&graph, parserId) else { |
|
| 268 | 268 | throw testing::TestError::Failed; |
|
| 269 | 269 | }; |
lib/std/lang/package.rad
+3 -3
| 22 | 22 | /// Initialize `pkg` with the provided name and ID. |
|
| 23 | 23 | export fn init( |
|
| 24 | 24 | pkg: &mut Package, |
|
| 25 | 25 | id: u16, |
|
| 26 | 26 | name: *[u8], |
|
| 27 | - | pool: *mut strings::Pool |
|
| 27 | + | pool: &mut strings::Pool |
|
| 28 | 28 | ) { |
|
| 29 | 29 | set pkg.id = id; |
|
| 30 | 30 | set pkg.name = strings::intern(pool, name); |
|
| 31 | 31 | set pkg.rootModuleId = nil; |
|
| 32 | 32 | ||
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Register a module described by the file path. |
|
| 36 | - | export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, filePath: *[u8]) -> u16 |
|
| 36 | + | export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16 |
|
| 37 | 37 | throws (module::ModuleError) |
|
| 38 | 38 | { |
|
| 39 | - | let modId = try module::registerFromPath(graph, pkg.id, pkg.rootModuleId, filePath); |
|
| 39 | + | let modId = try module::registerFromPath(graph, pool, pkg.id, pkg.rootModuleId, filePath); |
|
| 40 | 40 | // First registered module becomes the root. |
|
| 41 | 41 | if pkg.rootModuleId == nil { |
|
| 42 | 42 | set pkg.rootModuleId = modId; |
|
| 43 | 43 | } |
|
| 44 | 44 | return modId; |
lib/std/lang/parser.rad
+37 -41
| 160 | 160 | /// Current parsing context (normal or conditional). |
|
| 161 | 161 | context: Context, |
|
| 162 | 162 | } |
|
| 163 | 163 | ||
| 164 | 164 | /// Create a new parser initialized with the given source kind, source and node arena. |
|
| 165 | - | /// The node arena must outlive the returned parser. |
|
| 166 | - | export unsafe fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> Parser { |
|
| 165 | + | /// The node arena and string pool must outlive every copy of the parser. |
|
| 166 | + | export unsafe fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: &mut ast::NodeArena, pool: *unsafe mut strings::Pool) -> Parser { |
|
| 167 | 167 | return Parser { |
|
| 168 | 168 | scanner: scanner::scanner(sourceLoc, source, pool), |
|
| 169 | 169 | current: scanner::invalid(0, ""), |
|
| 170 | 170 | previous: scanner::invalid(0, ""), |
|
| 171 | 171 | errors: ErrorList { list: undefined, count: 0 }, |
|
| 172 | - | arena: arena as *unsafe mut ast::NodeArena, |
|
| 172 | + | arena: (&mut *arena) as *unsafe mut ast::NodeArena, |
|
| 173 | 173 | allocator: alloc::arenaAllocator(&mut arena.arena), |
|
| 174 | 174 | context: Context::Normal, |
|
| 175 | 175 | }; |
|
| 176 | 176 | } |
|
| 177 | 177 |
| 265 | 265 | return node(p, ast::NodeValue::ArrayRepeatLit( |
|
| 266 | 266 | ast::ArrayRepeatLit { item: firstExpr, count } |
|
| 267 | 267 | )); |
|
| 268 | 268 | } |
|
| 269 | 269 | // Regular array literal: `[a, b, ...]`. |
|
| 270 | - | let mut items = ast::nodeSlice(&mut *p.arena, 64).append(firstExpr, p.allocator); |
|
| 270 | + | let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator); |
|
| 271 | 271 | ||
| 272 | 272 | while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) { |
|
| 273 | 273 | let elem = try parseNormalExpr(p); |
|
| 274 | 274 | items.append(elem, p.allocator); |
|
| 275 | 275 | } |
| 698 | 698 | } |
|
| 699 | 699 | try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name"); |
|
| 700 | 700 | ||
| 701 | 701 | // Parse arguments into a list. Use capacity 4 to handle any valid argument count |
|
| 702 | 702 | // plus some extra for error recovery. |
|
| 703 | - | let mut args = ast::nodeSlice(&mut *p.arena, 4); |
|
| 703 | + | let mut args = ast::nodeSlice(p.arena, 4); |
|
| 704 | 704 | ||
| 705 | 705 | if kind == ast::Builtin::SliceOf { |
|
| 706 | 706 | // Parse comma-separated expressions until closing paren. |
|
| 707 | 707 | // Argument count validation is done in semantic analysis. |
|
| 708 | 708 | while not check(p, scanner::TokenKind::RParen) { |
| 730 | 730 | let expr = try parseBinary(p, left, -1); |
|
| 731 | 731 | return try parseCondExpr(p, expr); |
|
| 732 | 732 | } |
|
| 733 | 733 | ||
| 734 | 734 | /// Try to consume a compound assignment operator and return its binary op. |
|
| 735 | - | fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp { |
|
| 735 | + | unsafe fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp { |
|
| 736 | 736 | match p.current.kind { |
|
| 737 | 737 | case scanner::TokenKind::PlusEqual => { advance(p); return ast::BinaryOp::Add; } |
|
| 738 | 738 | case scanner::TokenKind::MinusEqual => { advance(p); return ast::BinaryOp::Sub; } |
|
| 739 | 739 | case scanner::TokenKind::StarEqual => { advance(p); return ast::BinaryOp::Mul; } |
|
| 740 | 740 | case scanner::TokenKind::SlashEqual => { advance(p); return ast::BinaryOp::Div; } |
| 784 | 784 | throw failParsing(p, "expected assignment after `set`"); |
|
| 785 | 785 | } |
|
| 786 | 786 | ||
| 787 | 787 | /// Parse leading attributes and declaration modifiers. |
|
| 788 | 788 | unsafe fn parseAttributes(p: &mut Parser) -> ?ast::Attributes { |
|
| 789 | - | let mut attrs = ast::nodeSlice(&mut *p.arena, 4); |
|
| 789 | + | let mut attrs = ast::nodeSlice(p.arena, 4); |
|
| 790 | 790 | ||
| 791 | 791 | if let attr = tryParseAnnotation(p) { |
|
| 792 | 792 | attrs.append(attr, p.allocator); |
|
| 793 | 793 | } |
|
| 794 | 794 | if consume(p, scanner::TokenKind::Export) { |
| 810 | 810 | unsafe fn tryParseAnnotation(p: &mut Parser) -> ?*ast::Node { |
|
| 811 | 811 | if not check(p, scanner::TokenKind::AtIdent) { |
|
| 812 | 812 | return nil; |
|
| 813 | 813 | } |
|
| 814 | 814 | // Token is @identifier, skip the '@' to get the name. |
|
| 815 | - | let ident = &p.current.source[..]; |
|
| 815 | + | let ident = p.current.source; |
|
| 816 | 816 | if ident == "@default" { |
|
| 817 | 817 | advance(p); // Consume `@default`. |
|
| 818 | 818 | return nodeAttribute(p, ast::Attribute::Default); |
|
| 819 | 819 | } |
|
| 820 | 820 | if ident == "@test" { |
| 954 | 954 | } |
|
| 955 | 955 | } |
|
| 956 | 956 | ||
| 957 | 957 | /// Parse statements until the specified ending token is encountered. |
|
| 958 | 958 | /// |
|
| 959 | - | /// Adds each parsed statement to the given block's statement list. |
|
| 960 | - | export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, blk: &mut ast::Block) |
|
| 959 | + | /// Returns the completed immutable statement list. |
|
| 960 | + | export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, capacity: u32) -> *[*ast::Node] |
|
| 961 | 961 | throws (ParseError) |
|
| 962 | 962 | { |
|
| 963 | + | let mut statements = ast::nodeSlice(p.arena, capacity); |
|
| 963 | 964 | while not check(p, end) { |
|
| 964 | 965 | let stmt = try parseStmt(p); |
|
| 965 | - | blk.statements.append(stmt, p.allocator); |
|
| 966 | + | statements.append(stmt, p.allocator); |
|
| 966 | 967 | ||
| 967 | 968 | if check(p, end) or check(p, scanner::TokenKind::Eof) { |
|
| 968 | 969 | break; |
|
| 969 | 970 | } |
|
| 970 | 971 | if not consume(p, scanner::TokenKind::Semicolon) { |
| 972 | 973 | if expectsSemicolon(stmt) { |
|
| 973 | 974 | throw failParsing(p, "expected `;` after statement"); |
|
| 974 | 975 | } |
|
| 975 | 976 | } |
|
| 976 | 977 | } |
|
| 978 | + | return statements; |
|
| 977 | 979 | } |
|
| 978 | 980 | ||
| 979 | 981 | /// Parse a block of statements enclosed in curly braces. |
|
| 980 | 982 | export unsafe fn parseBlock(p: &mut Parser) -> *ast::Node |
|
| 981 | 983 | throws (ParseError) |
| 986 | 988 | /// Parse a statement block with the specified unsafe permission. |
|
| 987 | 989 | unsafe fn parseBlockBody(p: &mut Parser, isUnsafe: bool) -> *ast::Node |
|
| 988 | 990 | throws (ParseError) |
|
| 989 | 991 | { |
|
| 990 | 992 | let start = p.current; |
|
| 991 | - | let mut blk = mkBlock(p, 64); |
|
| 992 | - | set blk.isUnsafe = isUnsafe; |
|
| 993 | 993 | ||
| 994 | 994 | if not consume(p, scanner::TokenKind::LBrace) { |
|
| 995 | 995 | throw failParsing(p, "expected `{`"); |
|
| 996 | 996 | } |
|
| 997 | - | try parseStmtsUntil(p, scanner::TokenKind::RBrace, &mut blk); |
|
| 997 | + | let statements = try parseStmtsUntil(p, scanner::TokenKind::RBrace, 64); |
|
| 998 | + | let blk = ast::Block { statements, isUnsafe }; |
|
| 998 | 999 | try expect(p, scanner::TokenKind::RBrace, "expected `}`"); |
|
| 999 | 1000 | ||
| 1000 | 1001 | return node(p, ast::NodeValue::Block(blk)); |
|
| 1001 | 1002 | } |
|
| 1002 | 1003 | ||
| 1003 | - | /// Create an empty block with no statements. |
|
| 1004 | - | unsafe fn mkBlock(p: &mut Parser, cap: u32) -> ast::Block { |
|
| 1005 | - | return ast::Block { statements: ast::nodeSlice(&mut *p.arena, cap), isUnsafe: false }; |
|
| 1006 | - | } |
|
| 1007 | - | ||
| 1008 | 1004 | /// Create a block containing a single statement node. |
|
| 1009 | 1005 | unsafe fn mkBlockWith(p: &mut Parser, node: *ast::Node) -> ast::Block { |
|
| 1010 | - | let stmts = ast::nodeSlice(&mut *p.arena, 1).append(node, p.allocator); |
|
| 1006 | + | let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator); |
|
| 1011 | 1007 | return ast::Block { statements: stmts, isUnsafe: false }; |
|
| 1012 | 1008 | } |
|
| 1013 | 1009 | ||
| 1014 | 1010 | /// Parse the branch that follows `else` in let-else style constructs. |
|
| 1015 | 1011 | /// |
| 1033 | 1029 | unsafe fn node(p: &mut Parser, value: ast::NodeValue) -> *mut ast::Node { |
|
| 1034 | 1030 | let span = ast::Span { |
|
| 1035 | 1031 | offset: p.previous.offset, |
|
| 1036 | 1032 | length: p.previous.source.len, |
|
| 1037 | 1033 | }; |
|
| 1038 | - | let n = ast::allocNode(&mut *p.arena, span, value); |
|
| 1034 | + | let n = ast::allocNode(p.arena, span, value); |
|
| 1039 | 1035 | finishSpan(p, n); |
|
| 1040 | 1036 | ||
| 1041 | 1037 | return n; |
|
| 1042 | 1038 | } |
|
| 1043 | 1039 | ||
| 1044 | 1040 | /// Update the span of `node` using the most recently consumed token. |
|
| 1045 | - | fn finishSpan(p: &mut Parser, node: *mut ast::Node) { |
|
| 1041 | + | fn finishSpan(p: &mut Parser, node: &mut ast::Node) { |
|
| 1046 | 1042 | let start: u32 = node.span.offset; |
|
| 1047 | 1043 | let mut end: u32 = p.previous.offset + p.previous.source.len; |
|
| 1048 | 1044 | ||
| 1049 | 1045 | if end >= start { |
|
| 1050 | 1046 | set node.span.length = end - start; |
| 1123 | 1119 | export fn check(p: &Parser, kind: scanner::TokenKind) -> bool { |
|
| 1124 | 1120 | return p.current.kind == kind; |
|
| 1125 | 1121 | } |
|
| 1126 | 1122 | ||
| 1127 | 1123 | /// Advance the parser by one token. |
|
| 1128 | - | export fn advance(p: &mut Parser) { |
|
| 1124 | + | export unsafe fn advance(p: &mut Parser) { |
|
| 1129 | 1125 | set p.previous = p.current; |
|
| 1130 | 1126 | set p.current = scanner::next(&mut p.scanner); |
|
| 1131 | 1127 | } |
|
| 1132 | 1128 | ||
| 1133 | 1129 | /// Parse an `if let` pattern matching statement. |
| 1352 | 1348 | try expect(p, scanner::TokenKind::Try, "expected `try`"); |
|
| 1353 | 1349 | ||
| 1354 | 1350 | let shouldPanic = consume(p, scanner::TokenKind::Bang); |
|
| 1355 | 1351 | let returnsOptional = consume(p, scanner::TokenKind::Question); |
|
| 1356 | 1352 | let expr = try parseUnaryExpr(p); |
|
| 1357 | - | let mut catches = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1353 | + | let mut catches = ast::nodeSlice(p.arena, 4); |
|
| 1358 | 1354 | ||
| 1359 | 1355 | while consume(p, scanner::TokenKind::Catch) { |
|
| 1360 | 1356 | let mut binding: ?*ast::Node = nil; |
|
| 1361 | 1357 | let mut typeNode: ?*ast::Node = nil; |
|
| 1362 | 1358 |
| 1455 | 1451 | try expect(p, scanner::TokenKind::Match, "expected `match`"); |
|
| 1456 | 1452 | ||
| 1457 | 1453 | let subject = try parseCond(p); |
|
| 1458 | 1454 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs"); |
|
| 1459 | 1455 | ||
| 1460 | - | let mut prongs = ast::nodeSlice(&mut *p.arena, 128); |
|
| 1456 | + | let mut prongs = ast::nodeSlice(p.arena, 128); |
|
| 1461 | 1457 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 1462 | 1458 | not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF. |
|
| 1463 | 1459 | { |
|
| 1464 | 1460 | let prongNode = try parseMatchProng(p); |
|
| 1465 | 1461 | prongs.append(prongNode, p.allocator); |
| 1478 | 1474 | { |
|
| 1479 | 1475 | let mut guard: ?*ast::Node = nil; |
|
| 1480 | 1476 | ||
| 1481 | 1477 | // Case prong: `case <pattern>, ... if <guard> => <body>`. |
|
| 1482 | 1478 | if consume(p, scanner::TokenKind::Case) { |
|
| 1483 | - | let mut patterns = ast::nodeSlice(&mut *p.arena, 16); |
|
| 1479 | + | let mut patterns = ast::nodeSlice(p.arena, 16); |
|
| 1484 | 1480 | loop { |
|
| 1485 | 1481 | let pattern = try parseMatchPattern(p); |
|
| 1486 | 1482 | patterns.append(pattern, p.allocator); |
|
| 1487 | 1483 | ||
| 1488 | 1484 | if not consume(p, scanner::TokenKind::Comma) { |
| 1582 | 1578 | ) -> *mut [*ast::Node] |
|
| 1583 | 1579 | throws (ParseError) |
|
| 1584 | 1580 | { |
|
| 1585 | 1581 | let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled |
|
| 1586 | 1582 | else scanner::TokenKind::RParen; |
|
| 1587 | - | let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS); |
|
| 1583 | + | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1588 | 1584 | while not check(p, terminator) { |
|
| 1589 | 1585 | let mut recordField: ast::NodeValue = undefined; |
|
| 1590 | 1586 | match mode { |
|
| 1591 | 1587 | case RecordFieldMode::Labeled => { |
|
| 1592 | 1588 | // Allow optional `let` keyword before field name. |
| 1624 | 1620 | return fields; |
|
| 1625 | 1621 | } |
|
| 1626 | 1622 | ||
| 1627 | 1623 | /// Parse an optional derives list (`: Trait + Trait`). |
|
| 1628 | 1624 | unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1629 | - | let mut derives = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1625 | + | let mut derives = ast::nodeSlice(p.arena, 4); |
|
| 1630 | 1626 | ||
| 1631 | 1627 | if not consume(p, scanner::TokenKind::Colon) { |
|
| 1632 | 1628 | return derives; |
|
| 1633 | 1629 | } |
|
| 1634 | 1630 | loop { |
| 1665 | 1661 | /// Eg. `{ x: 1, y: 2 }` |
|
| 1666 | 1662 | /// Eg. `{ x: 1, .. }` |
|
| 1667 | 1663 | unsafe fn parseRecordLit(p: &mut Parser, typeName: ?*ast::Node) -> *ast::Node |
|
| 1668 | 1664 | throws (ParseError) |
|
| 1669 | 1665 | { |
|
| 1670 | - | let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS); |
|
| 1666 | + | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1671 | 1667 | let mut ignoreRest = false; |
|
| 1672 | 1668 | try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal"); |
|
| 1673 | 1669 | ||
| 1674 | 1670 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1675 | 1671 | // Check for `..` to ignore remaining fields. |
| 1726 | 1722 | let name = try parseIdent(p, "expected union name"); |
|
| 1727 | 1723 | let derives = try parseDerives(p); |
|
| 1728 | 1724 | ||
| 1729 | 1725 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body"); |
|
| 1730 | 1726 | ||
| 1731 | - | let mut variants = ast::nodeSlice(&mut *p.arena, 128); |
|
| 1727 | + | let mut variants = ast::nodeSlice(p.arena, 128); |
|
| 1732 | 1728 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1733 | 1729 | // Allow optional `case` keyword before variant name. |
|
| 1734 | 1730 | consume(p, scanner::TokenKind::Case); |
|
| 1735 | 1731 | ||
| 1736 | 1732 | let variantName = try parseIdent(p, "expected variant name"); |
| 1789 | 1785 | /// Parse an optional `throws` clause and return the collected type list. |
|
| 1790 | 1786 | unsafe fn parseThrowList(p: &mut Parser) -> *mut [*ast::Node] |
|
| 1791 | 1787 | throws (ParseError) |
|
| 1792 | 1788 | { |
|
| 1793 | 1789 | if not consume(p, scanner::TokenKind::Throws) { |
|
| 1794 | - | return ast::nodeSlice(&mut *p.arena, 0); |
|
| 1790 | + | return ast::nodeSlice(p.arena, 0); |
|
| 1795 | 1791 | } |
|
| 1796 | 1792 | return try parseList( |
|
| 1797 | 1793 | p, |
|
| 1798 | 1794 | scanner::TokenKind::LParen, |
|
| 1799 | 1795 | scanner::TokenKind::RParen, |
| 1828 | 1824 | /// Parse a function signature following the function name. |
|
| 1829 | 1825 | unsafe fn parseFnTypeSig(p: &mut Parser) -> ast::FnSig |
|
| 1830 | 1826 | throws (ParseError) |
|
| 1831 | 1827 | { |
|
| 1832 | 1828 | try expect(p, scanner::TokenKind::LParen, "expected `(` after function name"); |
|
| 1833 | - | let mut params = ast::nodeSlice(&mut *p.arena, 8); |
|
| 1829 | + | let mut params = ast::nodeSlice(p.arena, 8); |
|
| 1834 | 1830 | ||
| 1835 | 1831 | while not check(p, scanner::TokenKind::RParen) { |
|
| 1836 | 1832 | let param = try parseFnParam(p); |
|
| 1837 | 1833 | params.append(param, p.allocator); |
|
| 1838 | 1834 |
| 1868 | 1864 | ||
| 1869 | 1865 | if consume(p, scanner::TokenKind::Semicolon) { |
|
| 1870 | 1866 | if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) { |
|
| 1871 | 1867 | // Keep existing attributes unchanged. |
|
| 1872 | 1868 | } else { |
|
| 1873 | - | let mut list = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1869 | + | let mut list = ast::nodeSlice(p.arena, 4); |
|
| 1874 | 1870 | if let a = attrs { |
|
| 1875 | 1871 | for i in 0..a.list.len { |
|
| 1876 | 1872 | list.append(a.list[i], p.allocator); |
|
| 1877 | 1873 | } |
|
| 1878 | 1874 | } |
| 2202 | 2198 | ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable, |
|
| 2203 | 2199 | })); |
|
| 2204 | 2200 | } |
|
| 2205 | 2201 | ||
| 2206 | 2202 | /// Parse a module from source text using the provided arena for node storage. |
|
| 2207 | - | export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node |
|
| 2203 | + | export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *unsafe mut strings::Pool) -> *mut ast::Node |
|
| 2208 | 2204 | throws (ParseError) |
|
| 2209 | 2205 | { |
|
| 2210 | 2206 | let mut p = mkParser(sourceLoc, input, arena, pool); |
|
| 2211 | 2207 | return try parseModule(&mut p) catch { |
|
| 2212 | 2208 | printErrors(&p); |
| 2221 | 2217 | export unsafe fn parseModule(p: &mut Parser) -> *mut ast::Node |
|
| 2222 | 2218 | throws (ParseError) |
|
| 2223 | 2219 | { |
|
| 2224 | 2220 | advance(p); // Set the parser up with a first token. |
|
| 2225 | 2221 | ||
| 2226 | - | let mut blk = mkBlock(p, 512); |
|
| 2227 | - | try parseStmtsUntil(p, scanner::TokenKind::Eof, &mut blk); |
|
| 2222 | + | let statements = try parseStmtsUntil(p, scanner::TokenKind::Eof, 512); |
|
| 2223 | + | let blk = ast::Block { statements, isUnsafe: false }; |
|
| 2228 | 2224 | consume(p, scanner::TokenKind::Eof); |
|
| 2229 | 2225 | ||
| 2230 | 2226 | return node(p, ast::NodeValue::Block(blk)); |
|
| 2231 | 2227 | } |
|
| 2232 | 2228 | ||
| 2233 | 2229 | /// Consume a token of the given kind if present. |
|
| 2234 | - | export fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2230 | + | export unsafe fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2235 | 2231 | if check(p, kind) { |
|
| 2236 | 2232 | advance(p); |
|
| 2237 | 2233 | return true; |
|
| 2238 | 2234 | } |
|
| 2239 | 2235 | return false; |
|
| 2240 | 2236 | } |
|
| 2241 | 2237 | ||
| 2242 | 2238 | /// Expect a token of the given kind or report an error. |
|
| 2243 | - | export fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2239 | + | export unsafe fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2244 | 2240 | throws (ParseError) |
|
| 2245 | 2241 | { |
|
| 2246 | 2242 | if not consume(p, kind) { |
|
| 2247 | 2243 | let token = p.current; |
|
| 2248 | 2244 | reportError(p, token, message); |
| 2272 | 2268 | try expect(p, scanner::TokenKind::Trait, "expected `trait`"); |
|
| 2273 | 2269 | let name = try parseIdent(p, "expected trait name"); |
|
| 2274 | 2270 | let supertraits = try parseDerives(p); |
|
| 2275 | 2271 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name"); |
|
| 2276 | 2272 | ||
| 2277 | - | let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2273 | + | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2278 | 2274 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2279 | 2275 | not check(p, scanner::TokenKind::Eof) |
|
| 2280 | 2276 | { |
|
| 2281 | 2277 | let method = try parseTraitMethodSig(p); |
|
| 2282 | 2278 | methods.append(method, p.allocator); |
| 2318 | 2314 | let traitName = try parseTypePath(p); |
|
| 2319 | 2315 | try expect(p, scanner::TokenKind::For, "expected `for` after trait name"); |
|
| 2320 | 2316 | let targetType = try parseTypePath(p); |
|
| 2321 | 2317 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type"); |
|
| 2322 | 2318 | ||
| 2323 | - | let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2319 | + | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2324 | 2320 | ||
| 2325 | 2321 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2326 | 2322 | not check(p, scanner::TokenKind::Eof) |
|
| 2327 | 2323 | { |
|
| 2328 | 2324 | let attrs = parseAttributes(p); |
| 2367 | 2363 | open: scanner::TokenKind, |
|
| 2368 | 2364 | close: scanner::TokenKind, |
|
| 2369 | 2365 | parseItem: unsafe fn (&mut Parser) -> *ast::Node throws (ParseError) |
|
| 2370 | 2366 | ) -> *mut [*ast::Node] throws (ParseError) { |
|
| 2371 | 2367 | try expect(p, open, listExpectMessage(open)); |
|
| 2372 | - | let mut items = ast::nodeSlice(&mut *p.arena, 8); |
|
| 2368 | + | let mut items = ast::nodeSlice(p.arena, 8); |
|
| 2373 | 2369 | ||
| 2374 | 2370 | while not check(p, close) { |
|
| 2375 | 2371 | let item = try parseItem(p); |
|
| 2376 | 2372 | items.append(item, p.allocator); |
|
| 2377 | 2373 |
lib/std/lang/parser/tests.rad
+2 -2
| 178 | 178 | try testing::expect(sign == expectedSign); |
|
| 179 | 179 | } |
|
| 180 | 180 | ||
| 181 | 181 | /// Extract a union variant and verify its name and index. |
|
| 182 | 182 | /// Returns the payload's fields slice for further checking with `expectField`. |
|
| 183 | - | fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> ?*mut [*ast::Node] |
|
| 183 | + | fn expectVariant(varNode: *ast::Node, name: *[u8], index: u32) -> ?*[*ast::Node] |
|
| 184 | 184 | throws (testing::TestError) |
|
| 185 | 185 | { |
|
| 186 | 186 | let case ast::NodeValue::UnionDeclVariant(v) = varNode.value |
|
| 187 | 187 | else throw testing::TestError::Failed; |
|
| 188 | 188 | try expectIdent(v.name, name); |
| 198 | 198 | return fields; |
|
| 199 | 199 | } |
|
| 200 | 200 | ||
| 201 | 201 | /// Check a record field has the expected name and type signature. |
|
| 202 | 202 | fn expectFieldSig( |
|
| 203 | - | fields: *mut [*ast::Node], index: u32, name: ?*[u8], sig: ast::TypeSig |
|
| 203 | + | fields: *[*ast::Node], index: u32, name: ?*[u8], sig: ast::TypeSig |
|
| 204 | 204 | ) throws (testing::TestError) { |
|
| 205 | 205 | let fieldNode = fields[index]; |
|
| 206 | 206 | let case ast::NodeValue::RecordField { field, type: fieldType, .. } = fieldNode.value |
|
| 207 | 207 | else throw testing::TestError::Failed; |
|
| 208 | 208 |
lib/std/lang/resolver.rad
+368 -265
| 55 | 55 | /// Trait definition stored in the resolver. |
|
| 56 | 56 | export record TraitType: Copy { |
|
| 57 | 57 | /// Trait name. |
|
| 58 | 58 | name: *[u8], |
|
| 59 | 59 | /// Method signatures, including from supertraits. |
|
| 60 | - | methods: *mut [TraitMethod], |
|
| 60 | + | methods: *unsafe mut [TraitMethod], |
|
| 61 | 61 | /// Supertraits that must also be implemented. |
|
| 62 | - | supertraits: *mut [*TraitType], |
|
| 62 | + | supertraits: *unsafe mut [*unsafe TraitType], |
|
| 63 | 63 | } |
|
| 64 | 64 | ||
| 65 | 65 | /// A single method signature within a trait. |
|
| 66 | 66 | export record TraitMethod: Copy { |
|
| 67 | 67 | /// Method name. |
|
| 68 | 68 | name: *[u8], |
|
| 69 | 69 | /// Function type for the method, excluding the receiver. |
|
| 70 | - | fnType: *mut FnType, |
|
| 70 | + | fnType: *FnType, |
|
| 71 | 71 | /// Whether the receiver is mutable. |
|
| 72 | 72 | mutable: bool, |
|
| 73 | 73 | /// Pointer-like class used by the receiver. |
|
| 74 | 74 | receiverClass: types::PointerClass, |
|
| 75 | 75 | /// V-table slot index. |
| 77 | 77 | } |
|
| 78 | 78 | ||
| 79 | 79 | /// An entry in the trait instance registry. |
|
| 80 | 80 | export record InstanceEntry: Copy { |
|
| 81 | 81 | /// Trait type descriptor. |
|
| 82 | - | traitType: *TraitType, |
|
| 82 | + | traitType: *unsafe TraitType, |
|
| 83 | 83 | /// Concrete type that implements the trait. |
|
| 84 | 84 | concreteType: Type, |
|
| 85 | 85 | /// Name of the concrete type. |
|
| 86 | 86 | concreteTypeName: *[u8], |
|
| 87 | 87 | /// Module where this instance was declared. |
|
| 88 | 88 | moduleId: u16, |
|
| 89 | 89 | /// Method symbols for each trait method, in declaration order. |
|
| 90 | - | methods: *mut [*mut Symbol], |
|
| 90 | + | methods: *unsafe mut [*unsafe mut Symbol], |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// An entry in the method registry. |
|
| 94 | 94 | export record MethodEntry: Copy { |
|
| 95 | 95 | /// Concrete type that owns the method. |
| 97 | 97 | /// Name of the concrete type. |
|
| 98 | 98 | concreteTypeName: *[u8], |
|
| 99 | 99 | /// Method name. |
|
| 100 | 100 | name: *[u8], |
|
| 101 | 101 | /// Function type excluding the receiver. |
|
| 102 | - | fnType: *mut FnType, |
|
| 102 | + | fnType: *FnType, |
|
| 103 | 103 | /// Whether the receiver is mutable. |
|
| 104 | 104 | mutable: bool, |
|
| 105 | 105 | /// Pointer-like class used by the receiver. |
|
| 106 | 106 | receiverClass: types::PointerClass, |
|
| 107 | 107 | /// Symbol for the method. |
|
| 108 | - | symbol: *mut Symbol, |
|
| 108 | + | symbol: *unsafe mut Symbol, |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Identifier for the synthetic `len` field. |
|
| 112 | 112 | export constant LEN_FIELD: *[u8] = "len"; |
|
| 113 | 113 | /// Identifier for the synthetic `ptr` field. |
| 153 | 153 | ||
| 154 | 154 | /// Information about a union variant. |
|
| 155 | 155 | record UnionVariant: Copy { |
|
| 156 | 156 | name: *[u8], |
|
| 157 | 157 | valueType: Type, |
|
| 158 | - | symbol: *mut Symbol, |
|
| 158 | + | symbol: *unsafe mut Symbol, |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Array type payload. |
|
| 162 | 162 | export record ArrayType: Copy { |
|
| 163 | 163 | item: *Type, |
|
| 164 | 164 | length: u32, |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | 167 | /// Record nominal type. |
|
| 168 | 168 | export record RecordType: Copy { |
|
| 169 | - | fields: *[RecordField], |
|
| 169 | + | fields: *unsafe [RecordField], |
|
| 170 | 170 | labeled: bool, |
|
| 171 | 171 | /// Cached layout. |
|
| 172 | 172 | layout: Layout, |
|
| 173 | 173 | /// Whether the declaration explicitly carries the `Once` marker. |
|
| 174 | 174 | declaredLinear: bool, |
| 176 | 176 | declaredCopy: bool, |
|
| 177 | 177 | } |
|
| 178 | 178 | ||
| 179 | 179 | /// Union nominal type. |
|
| 180 | 180 | export record UnionType: Copy { |
|
| 181 | - | variants: *[UnionVariant], |
|
| 181 | + | variants: *unsafe [UnionVariant], |
|
| 182 | 182 | /// Cached layout. |
|
| 183 | 183 | layout: Layout, |
|
| 184 | 184 | /// Cached payload offset within the union aggregate. |
|
| 185 | 185 | valOffset: u32, |
|
| 186 | 186 | /// If all variants have void payloads. |
| 211 | 211 | /// Wrap return value in success variant of result type. |
|
| 212 | 212 | ResultWrap, |
|
| 213 | 213 | /// Coerce a concrete pointer to a trait object. |
|
| 214 | 214 | TraitObject { |
|
| 215 | 215 | /// Trait type information. |
|
| 216 | - | traitInfo: *TraitType, |
|
| 216 | + | traitInfo: *unsafe TraitType, |
|
| 217 | 217 | /// Instance entry for v-table lookup. |
|
| 218 | 218 | inst: *unsafe InstanceEntry, |
|
| 219 | 219 | }, |
|
| 220 | 220 | } |
|
| 221 | 221 | ||
| 222 | 222 | /// Result of resolving a module path. |
|
| 223 | 223 | record ResolvedModule: Copy { |
|
| 224 | 224 | /// Module entry in the graph. |
|
| 225 | 225 | entry: *module::ModuleEntry, |
|
| 226 | 226 | /// Scope containing the module's declarations. |
|
| 227 | - | scope: *mut Scope, |
|
| 227 | + | scope: *unsafe mut Scope, |
|
| 228 | 228 | } |
|
| 229 | 229 | ||
| 230 | 230 | /// Type layout. |
|
| 231 | 231 | export record Layout: Copy { |
|
| 232 | 232 | /// Size in bytes. |
| 272 | 272 | }, |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Resolved function signature details. |
|
| 276 | 276 | export record FnType: Copy { |
|
| 277 | + | /// Parameter types in call order. |
|
| 277 | 278 | paramTypes: *[*Type], |
|
| 279 | + | /// Return value type. |
|
| 278 | 280 | returnType: *Type, |
|
| 281 | + | /// Error types that the function can throw. |
|
| 279 | 282 | throwList: *[*Type], |
|
| 280 | 283 | /// Whether calling this function requires an unsafe context. |
|
| 281 | 284 | isUnsafe: bool, |
|
| 282 | - | localCount: u32, |
|
| 283 | 285 | } |
|
| 284 | 286 | ||
| 285 | 287 | /// Describes a type computed during semantic analysis. |
|
| 286 | 288 | export union Type: Copy { |
|
| 287 | 289 | /// A type that couldn't be decided. |
| 312 | 314 | /// Eg. `[i32; 32]`. |
|
| 313 | 315 | Array(ArrayType), |
|
| 314 | 316 | /// Eg. `?T`. |
|
| 315 | 317 | Optional(*Type), |
|
| 316 | 318 | /// Eg. `fn id(i32) -> i32`. |
|
| 317 | - | Fn(*mut FnType), |
|
| 319 | + | Fn(*FnType), |
|
| 318 | 320 | /// Named, ie. user-defined types, includes union variants. |
|
| 319 | - | Nominal(*NominalType), |
|
| 321 | + | Nominal(*unsafe NominalType), |
|
| 320 | 322 | /// Owning trait object. An erased type with v-table. |
|
| 321 | 323 | TraitObject { |
|
| 322 | 324 | /// Ownership and safety class. |
|
| 323 | 325 | class: types::PointerClass, |
|
| 324 | 326 | /// Trait definition. |
|
| 325 | - | traitInfo: *TraitType, |
|
| 327 | + | traitInfo: *unsafe TraitType, |
|
| 326 | 328 | /// Whether the pointer is mutable. |
|
| 327 | 329 | mutable: bool, |
|
| 328 | 330 | }, |
|
| 329 | 331 | } |
|
| 330 | 332 |
| 381 | 383 | /// Module reference. |
|
| 382 | 384 | Module { |
|
| 383 | 385 | /// Module entry in the graph. |
|
| 384 | 386 | entry: *module::ModuleEntry, |
|
| 385 | 387 | /// Module scope. |
|
| 386 | - | scope: *mut Scope, |
|
| 388 | + | scope: *unsafe mut Scope, |
|
| 387 | 389 | }, |
|
| 388 | 390 | /// Payload describing type symbols with their resolved type. |
|
| 389 | - | Type(*mut NominalType), |
|
| 391 | + | Type(*unsafe mut NominalType), |
|
| 390 | 392 | /// Trait symbol. |
|
| 391 | - | Trait(*mut TraitType), |
|
| 393 | + | Trait(*unsafe mut TraitType), |
|
| 392 | 394 | } |
|
| 393 | 395 | ||
| 394 | 396 | /// Resolved symbol allocated during semantic analysis. |
|
| 395 | 397 | export record Symbol: Copy { |
|
| 396 | 398 | /// Symbol name in source code. |
| 455 | 457 | DuplicateBinding(*[u8]), |
|
| 456 | 458 | /// Identifier referenced before it was declared. |
|
| 457 | 459 | UnresolvedSymbol(*[u8]), |
|
| 458 | 460 | /// Attempted to assign to an immutable binding. |
|
| 459 | 461 | ImmutableBinding, |
|
| 462 | + | /// Slice append requires a valid allocator record and callback. |
|
| 463 | + | InvalidSliceAllocator, |
|
| 460 | 464 | /// Expected a compile-time constant expression. |
|
| 461 | 465 | ConstExprRequired, |
|
| 462 | 466 | /// Symbol arena exhausted while binding identifiers. |
|
| 463 | 467 | SymbolOverflow, |
|
| 464 | 468 | /// Expression has the wrong type. |
| 635 | 639 | Internal, |
|
| 636 | 640 | } |
|
| 637 | 641 | ||
| 638 | 642 | /// Diagnostics returned by the analyzer. |
|
| 639 | 643 | export record Diagnostics: Copy { |
|
| 640 | - | errors: *mut [Error], |
|
| 644 | + | /// Immutable errors captured at the end of an analysis operation. |
|
| 645 | + | errors: *[Error], |
|
| 646 | + | } |
|
| 647 | + | ||
| 648 | + | /// Mutable diagnostic storage owned by a resolver. |
|
| 649 | + | record DiagnosticBuffer { |
|
| 650 | + | /// Backing entries. Only the prefix below `len` is initialized. |
|
| 651 | + | entries: *mut [Error], |
|
| 652 | + | /// Number of recorded errors. |
|
| 653 | + | len: u32, |
|
| 641 | 654 | } |
|
| 642 | 655 | ||
| 643 | 656 | /// Call context. |
|
| 644 | 657 | union CallCtx: Copy { |
|
| 645 | 658 | /// Normal function call. |
| 649 | 662 | } |
|
| 650 | 663 | ||
| 651 | 664 | /// Result of resolving a record literal's type name. |
|
| 652 | 665 | record ResolvedRecordLitType: Copy { |
|
| 653 | 666 | /// The record nominal type to use for field checking. |
|
| 654 | - | recordType: *NominalType, |
|
| 667 | + | recordType: *unsafe NominalType, |
|
| 655 | 668 | /// The result type of the literal (record type or union type for variants). |
|
| 656 | 669 | resultType: Type, |
|
| 657 | 670 | } |
|
| 658 | 671 | ||
| 659 | 672 | /// Result of checking for a `super` path prefix. |
|
| 660 | 673 | record SuperAccessResult: Copy { |
|
| 661 | - | scope: *mut Scope, |
|
| 674 | + | scope: *unsafe mut Scope, |
|
| 662 | 675 | child: *ast::Node, |
|
| 663 | 676 | } |
|
| 664 | 677 | ||
| 665 | 678 | /// Node-specific resolver metadata. |
|
| 666 | 679 | export union NodeExtra: Copy { |
| 679 | 692 | /// For-loop iteration metadata. |
|
| 680 | 693 | ForLoop(ForLoopInfo), |
|
| 681 | 694 | /// Trait method call metadata. |
|
| 682 | 695 | TraitMethodCall { |
|
| 683 | 696 | /// Trait definition. |
|
| 684 | - | traitInfo: *TraitType, |
|
| 697 | + | traitInfo: *unsafe TraitType, |
|
| 685 | 698 | /// Method index in the v-table. |
|
| 686 | 699 | methodIndex: u32, |
|
| 687 | 700 | }, |
|
| 688 | 701 | /// Standalone method call metadata. |
|
| 689 | 702 | MethodCall { method: *unsafe MethodEntry }, |
| 693 | 706 | SliceDelete { elemType: *Type }, |
|
| 694 | 707 | } |
|
| 695 | 708 | ||
| 696 | 709 | /// Combined resolver metadata for a single AST node. |
|
| 697 | 710 | export record NodeData: Copy { |
|
| 711 | + | /// Number of local bindings and internal iteration variables in this function. |
|
| 712 | + | localCount: u32, |
|
| 698 | 713 | /// Resolved type for this node. |
|
| 699 | 714 | ty: Type, |
|
| 700 | 715 | /// Coercion plan applied to this node. |
|
| 701 | 716 | coercion: Coercion, |
|
| 702 | 717 | /// Symbol associated with this node. |
|
| 703 | - | sym: ?*mut Symbol, |
|
| 718 | + | sym: ?*unsafe mut Symbol, |
|
| 704 | 719 | /// Constant value for literal nodes. |
|
| 705 | 720 | constValue: ?ConstValue, |
|
| 706 | 721 | /// Lexical scope owned by this node. |
|
| 707 | - | scope: ?*mut Scope, |
|
| 722 | + | scope: ?*unsafe mut Scope, |
|
| 708 | 723 | /// Node-specific extra data. |
|
| 709 | 724 | extra: NodeExtra, |
|
| 710 | 725 | } |
|
| 711 | 726 | ||
| 712 | 727 | /// Table storing all resolver metadata indexed by node ID. |
|
| 713 | - | record NodeDataTable: Copy { |
|
| 728 | + | record NodeDataTable { |
|
| 729 | + | /// Semantic data indexed by AST node ID. |
|
| 714 | 730 | entries: *mut [NodeData], |
|
| 715 | 731 | } |
|
| 716 | 732 | ||
| 717 | 733 | /// Lexical scope. |
|
| 718 | 734 | export record Scope: Copy { |
|
| 719 | 735 | /// Owning AST node, or `nil` for the root scope. |
|
| 720 | 736 | owner: ?*ast::Node, |
|
| 721 | 737 | /// Parent/enclosing scope. |
|
| 722 | - | parent: ?*mut Scope, |
|
| 738 | + | parent: ?*unsafe mut Scope, |
|
| 723 | 739 | /// Module ID if this is a module scope. |
|
| 724 | 740 | moduleId: ?u16, |
|
| 725 | 741 | /// Symbols introduced inside the scope, allocated from the arena. |
|
| 726 | - | symbols: *mut [*mut Symbol], |
|
| 742 | + | symbols: *unsafe mut [*unsafe mut Symbol], |
|
| 727 | 743 | /// Number of live symbols. |
|
| 728 | 744 | symbolsLen: u32, |
|
| 729 | 745 | } |
|
| 730 | 746 | ||
| 731 | 747 | /// An object used by the enter and exit functions for module scopes. |
| 733 | 749 | /// Module root node. |
|
| 734 | 750 | root: *ast::Node, |
|
| 735 | 751 | /// Module entry in graph. |
|
| 736 | 752 | entry: *module::ModuleEntry, |
|
| 737 | 753 | /// The newly entered scope. |
|
| 738 | - | newScope: *mut Scope, |
|
| 754 | + | newScope: *unsafe mut Scope, |
|
| 739 | 755 | /// The previous scope. |
|
| 740 | - | prevScope: *mut Scope, |
|
| 756 | + | prevScope: *unsafe mut Scope, |
|
| 741 | 757 | /// The previous module. |
|
| 742 | 758 | prevMod: u16, |
|
| 743 | 759 | } |
|
| 744 | 760 | ||
| 745 | 761 | /// Loop context for tracking control flow within loops. |
| 798 | 814 | ||
| 799 | 815 | /// Per-control-flow-path ownership state. |
|
| 800 | 816 | /// Read only the initialized symbol prefix below `len`. |
|
| 801 | 817 | record LinearEnv: Copy { |
|
| 802 | 818 | /// Symbol pointers. Entries below `len` are initialized and not optional. |
|
| 803 | - | symbols: [*mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 819 | + | symbols: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 804 | 820 | /// Bit set for each binding that remains available. |
|
| 805 | 821 | available: u64, |
|
| 806 | 822 | /// Number of initialized entries in `symbols`. |
|
| 807 | 823 | len: u32, |
|
| 808 | 824 | /// Whether this control-flow path has terminated. |
| 814 | 830 | /// `enterLinearLoop` initializes each slot before it increases `loopDepth`. |
|
| 815 | 831 | record LinearChecker: Copy { |
|
| 816 | 832 | /// Resolver that owns the symbols and diagnostics. |
|
| 817 | 833 | resolver: *unsafe mut Resolver, |
|
| 818 | 834 | /// Source symbols protected by active pattern references. |
|
| 819 | - | loans: [*mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 835 | + | loans: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 820 | 836 | /// Number of initialized entries in `loans`. |
|
| 821 | 837 | loanLen: u32, |
|
| 822 | 838 | /// Binding count at entry to each active loop. |
|
| 823 | 839 | loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH], |
|
| 824 | 840 | /// Available bindings at entry to each active loop. |
| 841 | 857 | } |
|
| 842 | 858 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 843 | 859 | } |
|
| 844 | 860 | ||
| 845 | 861 | /// Global resolver state. |
|
| 846 | - | export record Resolver: Copy { |
|
| 862 | + | export record Resolver { |
|
| 847 | 863 | /// Current scope. |
|
| 848 | - | scope: *mut Scope, |
|
| 864 | + | scope: *unsafe mut Scope, |
|
| 849 | 865 | /// Package scope containing package roots and top-level symbols. |
|
| 850 | - | pkgScope: *mut Scope, |
|
| 866 | + | pkgScope: *unsafe mut Scope, |
|
| 851 | 867 | /// Stack of loop contexts for nested loops. |
|
| 852 | 868 | loopStack: [LoopCtx; MAX_LOOP_DEPTH], |
|
| 853 | 869 | /// Current loop depth, indexes into loop stack. |
|
| 854 | 870 | loopDepth: u32, |
|
| 855 | 871 | /// Signature of the function currently being analyzed. |
|
| 856 | - | currentFn: ?*unsafe mut FnType, |
|
| 872 | + | currentFn: ?FnType, |
|
| 873 | + | /// Declaration that owns the active function body and its local bindings. |
|
| 874 | + | currentFnNode: ?*ast::Node, |
|
| 857 | 875 | /// Current module being analyzed. |
|
| 858 | 876 | currentMod: u16, |
|
| 859 | 877 | /// Whether the current lexical context permits unsafe operations. |
|
| 860 | 878 | inUnsafeContext: bool, |
|
| 861 | 879 | /// Configuration for semantic analysis. |
| 865 | 883 | /// Combined semantic metadata table indexed by node ID. |
|
| 866 | 884 | nodeData: NodeDataTable, |
|
| 867 | 885 | /// Linked list of interned types. |
|
| 868 | 886 | types: ?*TypeNode, |
|
| 869 | 887 | /// Diagnostics recorded so far. |
|
| 870 | - | errors: *mut [Error], |
|
| 888 | + | errors: DiagnosticBuffer, |
|
| 871 | 889 | /// Module graph for the current package. |
|
| 872 | 890 | moduleGraph: *unsafe module::ModuleGraph, |
|
| 873 | 891 | /// Cache of module scopes indexed by module ID. |
|
| 874 | - | moduleScopes: [?*mut Scope; module::MAX_MODULES], |
|
| 892 | + | moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES], |
|
| 875 | 893 | /// Trait instance registry. |
|
| 876 | 894 | instances: [InstanceEntry; MAX_INSTANCES], |
|
| 877 | 895 | /// Number of registered instances. |
|
| 878 | 896 | instancesLen: u32, |
|
| 879 | 897 | /// Standalone method registry. |
| 907 | 925 | let node = try! alloc::alloc( |
|
| 908 | 926 | &mut self.arena, @sizeOf(TypeNode), @alignOf(TypeNode) |
|
| 909 | 927 | ) as *mut TypeNode; |
|
| 910 | 928 | ||
| 911 | 929 | set *node = TypeNode { ty, next: self.types }; |
|
| 912 | - | set self.types = node; |
|
| 930 | + | let frozen: *TypeNode = node; |
|
| 931 | + | set self.types = frozen; |
|
| 913 | 932 | ||
| 914 | - | return &node.ty; |
|
| 933 | + | return &frozen.ty; |
|
| 915 | 934 | } |
|
| 916 | 935 | ||
| 917 | 936 | /// Allocate a nominal type descriptor and return a pointer to it. |
|
| 918 | - | unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType { |
|
| 937 | + | unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *unsafe mut NominalType { |
|
| 919 | 938 | // Nb. We don't attempt to de-duplicate nominal type entries, |
|
| 920 | 939 | // since they don't carry node information and we create |
|
| 921 | 940 | // placeholder entries when binding symbols. |
|
| 922 | - | let entry = try! alloc::alloc( |
|
| 941 | + | let entry = try! alloc::allocRaw( |
|
| 923 | 942 | &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType) |
|
| 924 | - | ) as *mut NominalType; |
|
| 943 | + | ) as *unsafe mut NominalType; |
|
| 925 | 944 | ||
| 926 | 945 | set *entry = info; |
|
| 927 | 946 | ||
| 928 | 947 | return entry; |
|
| 929 | 948 | } |
|
| 930 | 949 | ||
| 931 | 950 | /// Allocate a function type descriptor and return a pointer to it. |
|
| 932 | - | unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *mut FnType { |
|
| 951 | + | unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType { |
|
| 933 | 952 | let entry = try! alloc::alloc( |
|
| 934 | 953 | &mut self.arena, @sizeOf(FnType), @alignOf(FnType) |
|
| 935 | 954 | ) as *mut FnType; |
|
| 936 | 955 | ||
| 937 | 956 | set *entry = info; |
|
| 938 | 957 | ||
| 939 | 958 | return entry; |
|
| 940 | 959 | } |
|
| 941 | 960 | ||
| 942 | 961 | /// Returns an error, if any, associated with the given node. |
|
| 943 | - | fn errorForNode(self: &Resolver, node: *ast::Node) -> ?*Error { |
|
| 962 | + | fn errorForNode(self: &Resolver, node: *ast::Node) -> ?Error { |
|
| 944 | 963 | for i in 0..self.errors.len { |
|
| 945 | - | let err = &self.errors[i]; |
|
| 964 | + | let err = self.errors.entries[i]; |
|
| 946 | 965 | if err.node == node { |
|
| 947 | 966 | return err; |
|
| 948 | 967 | } |
|
| 949 | 968 | } |
|
| 950 | 969 | return nil; |
|
| 951 | 970 | } |
|
| 952 | 971 | ||
| 953 | 972 | /// Storage buffers used by the analyzer. |
|
| 954 | - | export record ResolverStorage: Copy { |
|
| 973 | + | export record ResolverStorage { |
|
| 955 | 974 | /// Unified arena for symbols, scopes, and nominal type. |
|
| 956 | 975 | arena: alloc::Arena, |
|
| 957 | 976 | /// Node semantic metadata indexed by node ID. |
|
| 958 | 977 | nodeData: *mut [NodeData], |
|
| 959 | 978 | /// Package scope. |
|
| 960 | - | pkgScope: *mut Scope, |
|
| 979 | + | pkgScope: *unsafe mut Scope, |
|
| 961 | 980 | /// Error storage. |
|
| 962 | 981 | errors: *mut [Error], |
|
| 963 | 982 | } |
|
| 964 | 983 | ||
| 965 | 984 | /// Input for resolving a single package. |
| 973 | 992 | /// Construct a resolver with module context and backing storage. |
|
| 974 | 993 | export unsafe fn resolver( |
|
| 975 | 994 | storage: ResolverStorage, |
|
| 976 | 995 | config: Config |
|
| 977 | 996 | ) -> Resolver { |
|
| 978 | - | let mut arena = storage.arena; |
|
| 979 | - | let symbols = try! alloc::allocSlice( |
|
| 980 | - | &mut arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), MAX_MODULE_SYMBOLS |
|
| 981 | - | ) as *mut [*mut Symbol]; |
|
| 997 | + | let case ResolverStorage { arena: initialArena, nodeData, pkgScope, errors } = storage else panic "expected resolver storage"; |
|
| 998 | + | let mut arena = initialArena; |
|
| 999 | + | let symbols = try! alloc::allocRawSlice( |
|
| 1000 | + | &mut arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS |
|
| 1001 | + | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 982 | 1002 | ||
| 983 | 1003 | // Initialize the root scope. |
|
| 984 | 1004 | // TODO: Set this up when declaring `PKG_SCOPE`, not here. |
|
| 985 | - | set *storage.pkgScope = Scope { |
|
| 1005 | + | set *pkgScope = Scope { |
|
| 986 | 1006 | owner: nil, |
|
| 987 | 1007 | parent: nil, |
|
| 988 | 1008 | moduleId: nil, |
|
| 989 | 1009 | symbols, |
|
| 990 | 1010 | symbolsLen: 0, |
|
| 991 | 1011 | }; |
|
| 992 | 1012 | ||
| 993 | 1013 | // Clear all node semantic metadata to sentinel values. |
|
| 994 | 1014 | // TODO: Use array repeat literal? |
|
| 995 | - | for i in 0..storage.nodeData.len { |
|
| 996 | - | set storage.nodeData[i] = NodeData { |
|
| 1015 | + | for i in 0..nodeData.len { |
|
| 1016 | + | set nodeData[i] = NodeData { |
|
| 1017 | + | localCount: 0, |
|
| 997 | 1018 | ty: Type::Unknown, |
|
| 998 | 1019 | coercion: Coercion::Identity, |
|
| 999 | 1020 | sym: nil, |
|
| 1000 | 1021 | constValue: nil, |
|
| 1001 | 1022 | scope: nil, |
|
| 1002 | 1023 | extra: NodeExtra::None, |
|
| 1003 | 1024 | }; |
|
| 1004 | 1025 | } |
|
| 1005 | 1026 | ||
| 1006 | - | let mut moduleScopes: [?*mut Scope; module::MAX_MODULES] = undefined; |
|
| 1027 | + | let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined; |
|
| 1007 | 1028 | // TODO: Simplify. |
|
| 1008 | 1029 | for i in 0..moduleScopes.len { |
|
| 1009 | 1030 | set moduleScopes[i] = nil; |
|
| 1010 | 1031 | } |
|
| 1011 | 1032 | return Resolver { |
|
| 1012 | - | scope: storage.pkgScope, |
|
| 1013 | - | pkgScope: storage.pkgScope, |
|
| 1033 | + | scope: pkgScope, |
|
| 1034 | + | pkgScope: pkgScope, |
|
| 1014 | 1035 | loopStack: undefined, |
|
| 1015 | 1036 | loopDepth: 0, |
|
| 1016 | 1037 | currentFn: nil, |
|
| 1038 | + | currentFnNode: nil, |
|
| 1017 | 1039 | currentMod: 0, |
|
| 1018 | 1040 | inUnsafeContext: false, |
|
| 1019 | 1041 | config, |
|
| 1020 | 1042 | arena, |
|
| 1021 | - | nodeData: NodeDataTable { entries: storage.nodeData }, |
|
| 1043 | + | nodeData: NodeDataTable { entries: nodeData }, |
|
| 1022 | 1044 | types: nil, |
|
| 1023 | - | errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len), |
|
| 1045 | + | errors: DiagnosticBuffer { entries: errors, len: 0 }, |
|
| 1024 | 1046 | // TODO: Shouldn't be undefined. |
|
| 1025 | 1047 | moduleGraph: undefined, |
|
| 1026 | 1048 | moduleScopes, |
|
| 1027 | 1049 | instances: undefined, |
|
| 1028 | 1050 | instancesLen: 0, |
|
| 1029 | 1051 | methods: undefined, |
|
| 1030 | 1052 | methodsLen: 0, |
|
| 1031 | 1053 | }; |
|
| 1032 | 1054 | } |
|
| 1033 | 1055 | ||
| 1056 | + | /// Capture the current errors in an immutable arena allocation. |
|
| 1057 | + | /// The allocation must remain valid while the diagnostics are used. |
|
| 1058 | + | export unsafe fn diagnostics(self: &mut Resolver) -> Diagnostics { |
|
| 1059 | + | let count = self.errors.len; |
|
| 1060 | + | let entries = try! alloc::allocSlice( |
|
| 1061 | + | &mut self.arena, @sizeOf(Error), @alignOf(Error), count |
|
| 1062 | + | ) as *mut [Error]; |
|
| 1063 | + | for i in 0..self.errors.len { |
|
| 1064 | + | set entries[i] = self.errors.entries[i]; |
|
| 1065 | + | } |
|
| 1066 | + | return Diagnostics { errors: entries }; |
|
| 1067 | + | } |
|
| 1068 | + | ||
| 1034 | 1069 | /// Return `true` if there are no errors in the diagnostics. |
|
| 1035 | 1070 | export fn success(diag: &Diagnostics) -> bool { |
|
| 1036 | 1071 | return diag.errors.len == 0; |
|
| 1037 | 1072 | } |
|
| 1038 | 1073 | ||
| 1039 | 1074 | /// Retrieve an error diagnostic by index, if present. |
|
| 1040 | - | export fn errorAt(errs: *[Error], index: u32) -> ?*Error { |
|
| 1075 | + | export fn errorAt(errs: &[Error], index: u32) -> ?Error { |
|
| 1041 | 1076 | if index >= errs.len { |
|
| 1042 | 1077 | return nil; |
|
| 1043 | 1078 | } |
|
| 1044 | - | return &errs[index]; |
|
| 1079 | + | return errs[index]; |
|
| 1045 | 1080 | } |
|
| 1046 | 1081 | ||
| 1047 | 1082 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
|
| 1048 | - | unsafe fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1083 | + | fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1049 | 1084 | // If our error list is full, just return an error without recording it. |
|
| 1050 | - | if self.errors.len >= self.errors.cap { |
|
| 1085 | + | if self.errors.len >= self.errors.entries.len { |
|
| 1051 | 1086 | return ResolveError::Failure; |
|
| 1052 | 1087 | } |
|
| 1053 | 1088 | // Don't record more than one error per node. |
|
| 1054 | 1089 | if let n = node; errorForNode(self, n) <> nil { |
|
| 1055 | 1090 | return ResolveError::Failure; |
|
| 1056 | 1091 | } |
|
| 1057 | 1092 | let idx = self.errors.len; |
|
| 1058 | - | set self.errors = @sliceOf(self.errors.ptr, idx + 1, self.errors.cap); |
|
| 1059 | - | set self.errors[idx] = Error { kind, node, moduleId: self.currentMod }; |
|
| 1093 | + | set self.errors.entries[idx] = Error { kind, node, moduleId: self.currentMod }; |
|
| 1094 | + | set self.errors.len = idx + 1; |
|
| 1060 | 1095 | ||
| 1061 | 1096 | return ResolveError::Failure; |
|
| 1062 | 1097 | } |
|
| 1063 | 1098 | ||
| 1064 | 1099 | /// Like [`emitError`], but for type mismatches specifically. |
|
| 1065 | 1100 | unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1066 | 1101 | return emitError(self, node, ErrorKind::TypeMismatch(mismatch)); |
|
| 1067 | 1102 | } |
|
| 1068 | 1103 | ||
| 1069 | 1104 | /// Allocate a scope object with the given symbol capacity. |
|
| 1070 | - | unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope { |
|
| 1105 | + | unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope { |
|
| 1071 | 1106 | // Check for an existing scope for this node, and don't allocate a new |
|
| 1072 | 1107 | // one in that case. |
|
| 1073 | 1108 | if let scope = scopeFor(self, owner) { |
|
| 1074 | 1109 | return scope; |
|
| 1075 | 1110 | } |
|
| 1076 | 1111 | assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds"; |
|
| 1077 | - | let p = try! alloc::alloc(&mut self.arena, @sizeOf(Scope), @alignOf(Scope)); |
|
| 1078 | - | let entry = p as *mut Scope; |
|
| 1112 | + | let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(Scope), @alignOf(Scope)); |
|
| 1113 | + | let entry = p as *unsafe mut Scope; |
|
| 1079 | 1114 | ||
| 1080 | 1115 | // Allocate symbols from the arena. |
|
| 1081 | - | let symbols = try! alloc::allocSlice( |
|
| 1082 | - | &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), capacity |
|
| 1083 | - | ) as *mut [*mut Symbol]; |
|
| 1116 | + | let symbols = try! alloc::allocRawSlice( |
|
| 1117 | + | &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity |
|
| 1118 | + | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 1084 | 1119 | ||
| 1085 | 1120 | set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 }; |
|
| 1086 | 1121 | set self.nodeData.entries[owner.id].scope = entry; |
|
| 1087 | 1122 | ||
| 1088 | 1123 | return entry; |
|
| 1089 | 1124 | } |
|
| 1090 | 1125 | ||
| 1091 | 1126 | /// Enter a new local scope that is the child of the current scope. |
|
| 1092 | 1127 | /// This creates a parent/child relationship that means that lookups in the |
|
| 1093 | 1128 | /// child scope can recurse upwards. |
|
| 1094 | - | export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope { |
|
| 1129 | + | export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *unsafe Scope { |
|
| 1095 | 1130 | let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS); |
|
| 1096 | 1131 | set scope.parent = self.scope; |
|
| 1097 | 1132 | set self.scope = scope; |
|
| 1098 | 1133 | return scope; |
|
| 1099 | 1134 | } |
| 1113 | 1148 | return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod }; |
|
| 1114 | 1149 | } |
|
| 1115 | 1150 | ||
| 1116 | 1151 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
|
| 1117 | 1152 | unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1118 | - | let modEntry = module::findChild(&*self.moduleGraph, name, self.currentMod) |
|
| 1153 | + | let modEntry = module::findChild(self.moduleGraph, name, self.currentMod) |
|
| 1119 | 1154 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 1120 | 1155 | let modRoot = modEntry.ast |
|
| 1121 | 1156 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
|
| 1122 | 1157 | ||
| 1123 | 1158 | return enterModuleScope(self, modRoot, modEntry); |
| 1128 | 1163 | set self.scope = entry.prevScope; |
|
| 1129 | 1164 | set self.currentMod = entry.prevMod; |
|
| 1130 | 1165 | } |
|
| 1131 | 1166 | ||
| 1132 | 1167 | /// Exit the most recent scope. |
|
| 1133 | - | export fn exitScope(self: &mut Resolver) { |
|
| 1168 | + | export unsafe fn exitScope(self: &mut Resolver) { |
|
| 1134 | 1169 | let parent = self.scope.parent else { |
|
| 1135 | 1170 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1136 | 1171 | // clause, which might be a bug in the generator. |
|
| 1137 | 1172 | return; |
|
| 1138 | 1173 | }; |
| 1182 | 1217 | } |
|
| 1183 | 1218 | } |
|
| 1184 | 1219 | } |
|
| 1185 | 1220 | ||
| 1186 | 1221 | /// Set the expected return type for a new function body. |
|
| 1187 | - | unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &mut FnType) { |
|
| 1222 | + | unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) { |
|
| 1188 | 1223 | assert self.currentFn == nil, "enterFn: already in a function"; |
|
| 1189 | - | set self.currentFn = ty as *unsafe mut FnType; |
|
| 1224 | + | set self.currentFn = *ty; |
|
| 1225 | + | set self.currentFnNode = node; |
|
| 1190 | 1226 | enterScope(self, node); |
|
| 1191 | 1227 | } |
|
| 1192 | 1228 | ||
| 1193 | 1229 | /// Clear the expected return type when leaving a function body. |
|
| 1194 | - | fn exitFn(self: &mut Resolver) { |
|
| 1230 | + | unsafe fn exitFn(self: &mut Resolver) { |
|
| 1195 | 1231 | if self.currentFn == nil { |
|
| 1196 | 1232 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1197 | 1233 | // clause, which might be a bug in the generator. |
|
| 1198 | 1234 | return; |
|
| 1199 | 1235 | } |
|
| 1200 | 1236 | set self.currentFn = nil; |
|
| 1237 | + | set self.currentFnNode = nil; |
|
| 1201 | 1238 | exitScope(self); |
|
| 1202 | 1239 | } |
|
| 1203 | 1240 | ||
| 1204 | 1241 | /// Extract the identifier text from a node. |
|
| 1205 | 1242 | unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8] |
| 1209 | 1246 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 1210 | 1247 | return name; |
|
| 1211 | 1248 | } |
|
| 1212 | 1249 | ||
| 1213 | 1250 | /// Associate a resolved symbol with an AST node. |
|
| 1214 | - | fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *mut Symbol) { |
|
| 1251 | + | fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *unsafe mut Symbol) { |
|
| 1215 | 1252 | if let existingSym = self.nodeData.entries[node.id].sym { |
|
| 1216 | 1253 | panic "setNodeSymbol: a symbol is already associated with this node"; |
|
| 1217 | 1254 | } |
|
| 1218 | 1255 | set self.nodeData.entries[node.id].sym = symbol; |
|
| 1219 | 1256 | } |
| 1270 | 1307 | fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) { |
|
| 1271 | 1308 | set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag }; |
|
| 1272 | 1309 | } |
|
| 1273 | 1310 | ||
| 1274 | 1311 | /// Associate trait method call metadata with a call node. |
|
| 1275 | - | fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) { |
|
| 1312 | + | fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) { |
|
| 1276 | 1313 | set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex }; |
|
| 1277 | 1314 | } |
|
| 1278 | 1315 | ||
| 1279 | 1316 | /// Associate for-loop metadata with a for-loop node. |
|
| 1280 | 1317 | fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) { |
| 1335 | 1372 | } |
|
| 1336 | 1373 | return false; |
|
| 1337 | 1374 | } |
|
| 1338 | 1375 | ||
| 1339 | 1376 | /// Get the resolver metadata for a node. |
|
| 1340 | - | export fn nodeData(self: &Resolver, node: *ast::Node) -> *NodeData { |
|
| 1341 | - | return &self.nodeData.entries[node.id]; |
|
| 1377 | + | export fn nodeData(self: &Resolver, node: *ast::Node) -> NodeData { |
|
| 1378 | + | return self.nodeData.entries[node.id]; |
|
| 1342 | 1379 | } |
|
| 1343 | 1380 | ||
| 1344 | 1381 | /// Get the type for a node, or `nil` if unknown. |
|
| 1345 | 1382 | export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type { |
|
| 1346 | 1383 | let ty = self.nodeData.entries[node.id].ty; |
| 1349 | 1386 | } |
|
| 1350 | 1387 | return ty; |
|
| 1351 | 1388 | } |
|
| 1352 | 1389 | ||
| 1353 | 1390 | /// Get the scope associated with a node. |
|
| 1354 | - | export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*mut Scope { |
|
| 1391 | + | export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Scope { |
|
| 1355 | 1392 | return self.nodeData.entries[node.id].scope; |
|
| 1356 | 1393 | } |
|
| 1357 | 1394 | ||
| 1358 | 1395 | /// Get the symbol bound to a node. |
|
| 1359 | - | export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 1396 | + | export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 1360 | 1397 | return self.nodeData.entries[node.id].sym; |
|
| 1361 | 1398 | } |
|
| 1362 | 1399 | ||
| 1363 | 1400 | /// Get the coercion plan associated with a node, if any. |
|
| 1364 | 1401 | export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion { |
| 1368 | 1405 | } |
|
| 1369 | 1406 | return c; |
|
| 1370 | 1407 | } |
|
| 1371 | 1408 | ||
| 1372 | 1409 | /// Get the module ID for a symbol by walking up its scope chain. |
|
| 1373 | - | export fn moduleIdForSymbol(self: &Resolver, sym: *Symbol) -> ?u16 { |
|
| 1410 | + | export unsafe fn moduleIdForSymbol(self: &Resolver, sym: *unsafe Symbol) -> ?u16 { |
|
| 1374 | 1411 | // For module-level symbols, return the cached module ID. |
|
| 1375 | 1412 | if let id = sym.moduleId { |
|
| 1376 | 1413 | return id; |
|
| 1377 | 1414 | } |
|
| 1378 | 1415 | // For module symbols, return the module ID directly. |
| 1386 | 1423 | return nil; |
|
| 1387 | 1424 | } |
|
| 1388 | 1425 | ||
| 1389 | 1426 | /// Get the binding node for a variant pattern. |
|
| 1390 | 1427 | /// Returns the argument node if this is a variant constructor with a non-placeholder binding. |
|
| 1391 | - | export fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1428 | + | export unsafe fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1392 | 1429 | let case ast::NodeValue::Call(call) = pattern.value |
|
| 1393 | 1430 | else return nil; |
|
| 1394 | 1431 | let sym = symbolFor(self, call.callee) |
|
| 1395 | 1432 | else return nil; |
|
| 1396 | 1433 | let case SymbolData::Variant { .. } = sym.data |
| 1406 | 1443 | } |
|
| 1407 | 1444 | return arg; |
|
| 1408 | 1445 | } |
|
| 1409 | 1446 | ||
| 1410 | 1447 | /// Allocate a new symbol, and return a reference to it. |
|
| 1411 | - | unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol { |
|
| 1412 | - | let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol; |
|
| 1448 | + | unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol { |
|
| 1449 | + | let sym = try! alloc::allocRaw(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol; |
|
| 1413 | 1450 | set *sym = Symbol { name, data, attrs, node, moduleId: nil }; |
|
| 1414 | 1451 | ||
| 1415 | 1452 | return sym; |
|
| 1416 | 1453 | } |
|
| 1417 | 1454 |
| 1454 | 1491 | } |
|
| 1455 | 1492 | return b; |
|
| 1456 | 1493 | } |
|
| 1457 | 1494 | ||
| 1458 | 1495 | /// Get the layout of a type. |
|
| 1459 | - | export fn getTypeLayout(ty: Type) -> Layout { |
|
| 1496 | + | export unsafe fn getTypeLayout(ty: Type) -> Layout { |
|
| 1460 | 1497 | match ty { |
|
| 1461 | 1498 | case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 1462 | 1499 | case Type::Slice { .. }, Type::TraitObject { .. } => |
|
| 1463 | 1500 | return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE }, |
|
| 1464 | 1501 | case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 }, |
| 1476 | 1513 | } |
|
| 1477 | 1514 | } |
|
| 1478 | 1515 | } |
|
| 1479 | 1516 | ||
| 1480 | 1517 | /// Get the layout of a type or value. |
|
| 1481 | - | export fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1518 | + | export unsafe fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1482 | 1519 | let mut layout = getTypeLayout(ty); |
|
| 1483 | 1520 | // Check for symbol-specific alignment override. |
|
| 1484 | 1521 | if let sym = symbolFor(self, node) { |
|
| 1485 | 1522 | if let case SymbolData::Value { alignment, .. } = sym.data { |
|
| 1486 | 1523 | if alignment > 0 { |
| 1490 | 1527 | } |
|
| 1491 | 1528 | return layout; |
|
| 1492 | 1529 | } |
|
| 1493 | 1530 | ||
| 1494 | 1531 | /// Get the layout of an array type. |
|
| 1495 | - | export fn getArrayLayout(arr: ArrayType) -> Layout { |
|
| 1532 | + | export unsafe fn getArrayLayout(arr: ArrayType) -> Layout { |
|
| 1496 | 1533 | let itemLayout = getTypeLayout(*arr.item); |
|
| 1497 | 1534 | return Layout { |
|
| 1498 | 1535 | size: itemLayout.size * arr.length, |
|
| 1499 | 1536 | alignment: itemLayout.alignment, |
|
| 1500 | 1537 | }; |
|
| 1501 | 1538 | } |
|
| 1502 | 1539 | ||
| 1503 | 1540 | /// Get the layout of an optional type. |
|
| 1504 | - | export fn getOptionalLayout(inner: Type) -> Layout { |
|
| 1541 | + | export unsafe fn getOptionalLayout(inner: Type) -> Layout { |
|
| 1505 | 1542 | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 1506 | 1543 | if isNullableType(inner) { |
|
| 1507 | 1544 | return getTypeLayout(inner); |
|
| 1508 | 1545 | } |
|
| 1509 | 1546 | let innerLayout = getTypeLayout(inner); |
| 1516 | 1553 | alignment, |
|
| 1517 | 1554 | }; |
|
| 1518 | 1555 | } |
|
| 1519 | 1556 | ||
| 1520 | 1557 | /// Get the payload offset within an optional aggregate. |
|
| 1521 | - | export fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 1558 | + | export unsafe fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 1522 | 1559 | let innerLayout = getTypeLayout(inner); |
|
| 1523 | 1560 | return mem::alignUp(1, innerLayout.alignment); |
|
| 1524 | 1561 | } |
|
| 1525 | 1562 | ||
| 1526 | 1563 | /// Check if a type is optional. |
| 1572 | 1609 | } |
|
| 1573 | 1610 | } |
|
| 1574 | 1611 | } |
|
| 1575 | 1612 | ||
| 1576 | 1613 | /// Get the layout of a result aggregate with a tag and the larger payload. |
|
| 1577 | - | export fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
|
| 1614 | + | export unsafe fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
|
| 1578 | 1615 | let payloadLayout = getTypeLayout(payload); |
|
| 1579 | 1616 | let mut maxSize = payloadLayout.size; |
|
| 1580 | 1617 | let mut maxAlign = payloadLayout.alignment; |
|
| 1581 | 1618 | ||
| 1582 | 1619 | for errType in throwList { |
| 1589 | 1626 | alignment: max(PTR_SIZE, maxAlign), |
|
| 1590 | 1627 | }; |
|
| 1591 | 1628 | } |
|
| 1592 | 1629 | ||
| 1593 | 1630 | /// Compute the layout for a union given its resolved variants. |
|
| 1594 | - | fn computeUnionLayout(variants: *[UnionVariant]) -> UnionLayoutInfo { |
|
| 1631 | + | unsafe fn computeUnionLayout(variants: *unsafe [UnionVariant]) -> UnionLayoutInfo { |
|
| 1595 | 1632 | let tagSize: u32 = 1; |
|
| 1596 | 1633 | let mut maxVarSize: u32 = 0; |
|
| 1597 | 1634 | let mut maxVarAlign: u32 = 1; |
|
| 1598 | 1635 | let mut isAllVoid: bool = true; |
|
| 1599 | 1636 |
| 1626 | 1663 | set *iota = tag + 1; |
|
| 1627 | 1664 | return tag; |
|
| 1628 | 1665 | } |
|
| 1629 | 1666 | ||
| 1630 | 1667 | /// Check if a type is a union without payloads. |
|
| 1631 | - | export fn isVoidUnion(ty: Type) -> bool { |
|
| 1668 | + | export unsafe fn isVoidUnion(ty: Type) -> bool { |
|
| 1632 | 1669 | let case Type::Nominal(NominalType::Union(unionType)) = ty |
|
| 1633 | 1670 | else return false; |
|
| 1634 | 1671 | return unionType.isAllVoid; |
|
| 1635 | 1672 | } |
|
| 1636 | 1673 |
| 1720 | 1757 | else => {}, |
|
| 1721 | 1758 | } |
|
| 1722 | 1759 | } |
|
| 1723 | 1760 | ||
| 1724 | 1761 | /// Ensure a nominal type has its body resolved. |
|
| 1725 | - | unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *NominalType, site: *ast::Node) |
|
| 1762 | + | unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *unsafe NominalType, site: *ast::Node) |
|
| 1726 | 1763 | throws (ResolveError) |
|
| 1727 | 1764 | { |
|
| 1728 | 1765 | if let case NominalType::Placeholder(declNode) = *tyInfo { |
|
| 1729 | 1766 | // When resolving on-demand (e.g. from a child module), switch to the |
|
| 1730 | 1767 | // declaring module's scope so field type lookups find the right symbols. |
| 1755 | 1792 | set self.currentMod = prevMod; |
|
| 1756 | 1793 | } |
|
| 1757 | 1794 | } |
|
| 1758 | 1795 | ||
| 1759 | 1796 | /// Check if all elements in a node list are assignable to the target type. |
|
| 1760 | - | unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool { |
|
| 1797 | + | unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *[*ast::Node]) -> bool { |
|
| 1761 | 1798 | for itemNode in items { |
|
| 1762 | 1799 | let elemTy = typeFor(self, itemNode) |
|
| 1763 | 1800 | else return false; |
|
| 1764 | 1801 | if let _ = isAssignable(self, targetType, elemTy, itemNode) { |
|
| 1765 | 1802 | // Do nothing. |
| 1958 | 1995 | } |
|
| 1959 | 1996 | return nil; |
|
| 1960 | 1997 | } |
|
| 1961 | 1998 | ||
| 1962 | 1999 | /// Check if two function type descriptors are structurally equivalent. |
|
| 1963 | - | fn fnTypeEqual(a: &FnType, b: *FnType) -> bool { |
|
| 2000 | + | fn fnTypeEqual(a: &FnType, b: &FnType) -> bool { |
|
| 1964 | 2001 | if a.isUnsafe <> b.isUnsafe { |
|
| 1965 | 2002 | return false; |
|
| 1966 | 2003 | } |
|
| 1967 | 2004 | return fnSignatureEqual(a, b); |
|
| 1968 | 2005 | } |
|
| 1969 | 2006 | ||
| 1970 | 2007 | /// Compare parameter, return, and error types of functions. |
|
| 1971 | - | fn fnSignatureEqual(a: &FnType, b: *FnType) -> bool { |
|
| 2008 | + | fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool { |
|
| 1972 | 2009 | if a.paramTypes.len <> b.paramTypes.len { |
|
| 1973 | 2010 | return false; |
|
| 1974 | 2011 | } |
|
| 1975 | 2012 | if a.throwList.len <> b.throwList.len { |
|
| 1976 | 2013 | return false; |
| 1991 | 2028 | return true; |
|
| 1992 | 2029 | } |
|
| 1993 | 2030 | ||
| 1994 | 2031 | /// Check if two types are structurally equal. |
|
| 1995 | 2032 | export fn typesEqual(a: Type, b: Type) -> bool { |
|
| 2033 | + | // Nominal and trait types compare by descriptor identity. |
|
| 1996 | 2034 | if a == b { |
|
| 1997 | 2035 | return true; |
|
| 1998 | 2036 | } |
|
| 1999 | 2037 | if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a { |
|
| 2000 | 2038 | let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b |
| 2006 | 2044 | let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b |
|
| 2007 | 2045 | else return false; |
|
| 2008 | 2046 | return aClass == bClass and aMutable == bMutable |
|
| 2009 | 2047 | and typesEqual(*aItem, *bItem); |
|
| 2010 | 2048 | } |
|
| 2011 | - | if let case Type::TraitObject { class: aClass, traitInfo: aTraitInfo, mutable: aMutable } = a { |
|
| 2012 | - | let case Type::TraitObject { class: bClass, traitInfo: bTraitInfo, mutable: bMutable } = b |
|
| 2013 | - | else return false; |
|
| 2014 | - | return aClass == bClass and aMutable == bMutable |
|
| 2015 | - | and aTraitInfo == bTraitInfo; |
|
| 2016 | - | } |
|
| 2017 | 2049 | match a { |
|
| 2018 | 2050 | case Type::Array(aa) => { |
|
| 2019 | 2051 | let case Type::Array(ab) = b else return false; |
|
| 2020 | 2052 | return aa.length == ab.length and typesEqual(*aa.item, *ab.item); |
|
| 2021 | 2053 | } |
| 2061 | 2093 | else => return false, |
|
| 2062 | 2094 | } |
|
| 2063 | 2095 | } |
|
| 2064 | 2096 | ||
| 2065 | 2097 | /// Return whether `ty` may be duplicated implicitly. |
|
| 2066 | - | export fn isCopy(ty: Type) -> bool { |
|
| 2098 | + | export unsafe fn isCopy(ty: Type) -> bool { |
|
| 2067 | 2099 | match ty { |
|
| 2100 | + | case Type::Pointer { class, mutable, .. } => |
|
| 2101 | + | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2102 | + | case Type::Slice { class, mutable, .. } => |
|
| 2103 | + | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2104 | + | case Type::TraitObject { class, mutable, .. } => |
|
| 2105 | + | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2068 | 2106 | case Type::Array(array) => return isCopy(*array.item), |
|
| 2069 | 2107 | case Type::Optional(inner) => return isCopy(*inner), |
|
| 2070 | 2108 | case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy, |
|
| 2071 | 2109 | case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy, |
|
| 2072 | 2110 | case Type::Nominal(NominalType::Placeholder(_)) => return false, |
|
| 2073 | 2111 | else => return true, |
|
| 2074 | 2112 | } |
|
| 2075 | 2113 | } |
|
| 2076 | 2114 | ||
| 2077 | 2115 | /// Return whether a type must be consumed exactly once. |
|
| 2078 | - | export fn isLinear(ty: Type) -> bool { |
|
| 2116 | + | export unsafe fn isLinear(ty: Type) -> bool { |
|
| 2079 | 2117 | match ty { |
|
| 2080 | 2118 | case Type::Array(array) => return isLinear(*array.item), |
|
| 2081 | 2119 | case Type::Optional(inner) => return isLinear(*inner), |
|
| 2082 | 2120 | case Type::Nominal(NominalType::Record(recInfo)) => { |
|
| 2083 | 2121 | if recInfo.declaredLinear { |
| 2104 | 2142 | else => return false, |
|
| 2105 | 2143 | } |
|
| 2106 | 2144 | } |
|
| 2107 | 2145 | ||
| 2108 | 2146 | /// Return whether a by-value use moves `ty`. |
|
| 2109 | - | fn isMoveOnly(ty: Type) -> bool { |
|
| 2147 | + | unsafe fn isMoveOnly(ty: Type) -> bool { |
|
| 2110 | 2148 | return not isCopy(ty); |
|
| 2111 | 2149 | } |
|
| 2112 | 2150 | ||
| 2113 | 2151 | /// Return whether `ty` is a direct unsafe pointer-like value. |
|
| 2114 | 2152 | fn isUnsafePointerType(ty: Type) -> bool { |
| 2119 | 2157 | else => return false, |
|
| 2120 | 2158 | } |
|
| 2121 | 2159 | } |
|
| 2122 | 2160 | ||
| 2123 | 2161 | /// Get the record info from a record type. |
|
| 2124 | - | export fn getRecord(ty: Type) -> ?RecordType { |
|
| 2162 | + | export unsafe fn getRecord(ty: Type) -> ?RecordType { |
|
| 2125 | 2163 | let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil; |
|
| 2126 | 2164 | return recInfo; |
|
| 2127 | 2165 | } |
|
| 2128 | 2166 | ||
| 2129 | 2167 | /// Auto-dereference a type: if it's a pointer, return the target type. |
| 2133 | 2171 | } |
|
| 2134 | 2172 | return ty; |
|
| 2135 | 2173 | } |
|
| 2136 | 2174 | ||
| 2137 | 2175 | /// Get field info for a record-like type (records, slices) by field index. |
|
| 2138 | - | export fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 2176 | + | export unsafe fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 2139 | 2177 | if let case Type::Slice { class, item, mutable } = ty { |
|
| 2140 | 2178 | match index { |
|
| 2141 | 2179 | case 0 => return RecordField { |
|
| 2142 | 2180 | name: PTR_FIELD, |
|
| 2143 | 2181 | fieldType: Type::Pointer { class, target: item, mutable }, |
| 2163 | 2201 | } |
|
| 2164 | 2202 | return nil; |
|
| 2165 | 2203 | } |
|
| 2166 | 2204 | ||
| 2167 | 2205 | /// Check if the two types can be compared for equality. |
|
| 2168 | - | fn isComparable(left: Type, right: Type) -> bool { |
|
| 2206 | + | unsafe fn isComparable(left: Type, right: Type) -> bool { |
|
| 2169 | 2207 | if left == Type::Unknown or right == Type::Unknown { |
|
| 2170 | 2208 | return false; |
|
| 2171 | 2209 | } |
|
| 2172 | 2210 | if left == right { |
|
| 2173 | 2211 | return true; |
| 2239 | 2277 | self: &mut Resolver, |
|
| 2240 | 2278 | name: *[u8], |
|
| 2241 | 2279 | owner: *ast::Node, |
|
| 2242 | 2280 | data: SymbolData, |
|
| 2243 | 2281 | attrs: u32, |
|
| 2244 | - | scope: *mut Scope |
|
| 2245 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2282 | + | scope: *unsafe mut Scope |
|
| 2283 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2246 | 2284 | let sym = allocSymbol(self, data, name, owner, attrs); |
|
| 2247 | 2285 | try addSymbolToScope(self, sym, scope, owner); |
|
| 2248 | 2286 | setNodeSymbol(self, owner, sym); |
|
| 2249 | 2287 | ||
| 2250 | 2288 | return sym; |
|
| 2251 | 2289 | } |
|
| 2252 | 2290 | ||
| 2253 | 2291 | /// Add a symbol to the given scope. |
|
| 2254 | - | unsafe fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2292 | + | unsafe fn addSymbolToScope(self: &mut Resolver, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2255 | 2293 | for i in 0..scope.symbolsLen { |
|
| 2256 | 2294 | if scope.symbols[i].name == sym.name { |
|
| 2257 | 2295 | throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name)); |
|
| 2258 | 2296 | } |
|
| 2259 | 2297 | } |
| 2279 | 2317 | owner: *ast::Node, |
|
| 2280 | 2318 | type: Type, |
|
| 2281 | 2319 | mutable: bool, |
|
| 2282 | 2320 | alignment: u32, |
|
| 2283 | 2321 | attrs: u32 |
|
| 2284 | - | ) -> ?*mut Symbol throws (ResolveError) { |
|
| 2322 | + | ) -> ?*unsafe mut Symbol throws (ResolveError) { |
|
| 2285 | 2323 | if let case ast::NodeValue::Placeholder = ident.value { |
|
| 2286 | 2324 | setNodeType(self, owner, type); |
|
| 2287 | 2325 | return nil; |
|
| 2288 | 2326 | } |
|
| 2289 | 2327 | let name = try nodeName(self, ident); |
| 2292 | 2330 | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2293 | 2331 | setNodeType(self, owner, type); |
|
| 2294 | 2332 | setNodeType(self, ident, type); |
|
| 2295 | 2333 | ||
| 2296 | 2334 | // Track number of local bindings for lowering stage. |
|
| 2297 | - | if let mut fnType = self.currentFn { |
|
| 2298 | - | set fnType.localCount += 1; |
|
| 2335 | + | if let owner = self.currentFnNode { |
|
| 2336 | + | set self.nodeData.entries[owner.id].localCount += 1; |
|
| 2299 | 2337 | } |
|
| 2300 | 2338 | return sym; |
|
| 2301 | 2339 | } |
|
| 2302 | 2340 | ||
| 2303 | 2341 | /// Bind a constant identifier in the current scope. |
| 2306 | 2344 | ident: *ast::Node, |
|
| 2307 | 2345 | owner: *ast::Node, |
|
| 2308 | 2346 | type: Type, |
|
| 2309 | 2347 | val: ?ConstValue, |
|
| 2310 | 2348 | attrs: u32 |
|
| 2311 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2349 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2312 | 2350 | let name = try nodeName(self, ident); |
|
| 2313 | 2351 | let data = SymbolData::Constant { type, value: val }; |
|
| 2314 | 2352 | let scope = self.scope; |
|
| 2315 | 2353 | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2316 | 2354 | setNodeType(self, owner, type); |
| 2323 | 2361 | /// This is used when declaring modules with `mod` or |
|
| 2324 | 2362 | /// importing modules with `use`. |
|
| 2325 | 2363 | unsafe fn bindModuleIdent( |
|
| 2326 | 2364 | self: &mut Resolver, |
|
| 2327 | 2365 | entry: *module::ModuleEntry, |
|
| 2328 | - | scope: *mut Scope, |
|
| 2366 | + | scope: *unsafe mut Scope, |
|
| 2329 | 2367 | owner: *ast::Node, |
|
| 2330 | 2368 | attrs: u32, |
|
| 2331 | - | bindingScope: *mut Scope |
|
| 2332 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2369 | + | bindingScope: *unsafe mut Scope |
|
| 2370 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2333 | 2371 | let data = SymbolData::Module { entry, scope }; |
|
| 2334 | 2372 | let name = entry.name; |
|
| 2335 | 2373 | ||
| 2336 | 2374 | return try bindIdent(self, name, owner, data, attrs, bindingScope); |
|
| 2337 | 2375 | } |
| 2339 | 2377 | /// Bind a type identifier in the current scope. |
|
| 2340 | 2378 | unsafe fn bindTypeIdent( |
|
| 2341 | 2379 | self: &mut Resolver, |
|
| 2342 | 2380 | ident: *ast::Node, |
|
| 2343 | 2381 | owner: *ast::Node, |
|
| 2344 | - | type: *mut NominalType, |
|
| 2382 | + | type: *unsafe mut NominalType, |
|
| 2345 | 2383 | attrs: u32 |
|
| 2346 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2384 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2347 | 2385 | let name = try nodeName(self, ident); |
|
| 2348 | 2386 | let data = SymbolData::Type(type); |
|
| 2349 | 2387 | let scope = self.scope; |
|
| 2350 | 2388 | return try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2351 | 2389 | } |
|
| 2352 | 2390 | ||
| 2353 | 2391 | /// Predicate that matches any symbol. |
|
| 2354 | - | fn isAnySymbol(_sym: *mut Symbol) -> bool { |
|
| 2392 | + | fn isAnySymbol(_sym: *unsafe mut Symbol) -> bool { |
|
| 2355 | 2393 | return true; |
|
| 2356 | 2394 | } |
|
| 2357 | 2395 | ||
| 2358 | 2396 | /// Predicate that matches value or constant symbols. |
|
| 2359 | - | fn isValueSymbol(sym: *mut Symbol) -> bool { |
|
| 2397 | + | unsafe fn isValueSymbol(sym: *unsafe mut Symbol) -> bool { |
|
| 2360 | 2398 | if let case SymbolData::Value { .. } = sym.data { |
|
| 2361 | 2399 | return true; |
|
| 2362 | 2400 | } |
|
| 2363 | 2401 | if let case SymbolData::Constant { .. } = sym.data { |
|
| 2364 | 2402 | return true; |
|
| 2365 | 2403 | } |
|
| 2366 | 2404 | return false; |
|
| 2367 | 2405 | } |
|
| 2368 | 2406 | ||
| 2369 | 2407 | /// Predicate that matches type symbols. |
|
| 2370 | - | fn isTypeSymbol(sym: *mut Symbol) -> bool { |
|
| 2408 | + | unsafe fn isTypeSymbol(sym: *unsafe mut Symbol) -> bool { |
|
| 2371 | 2409 | if let case SymbolData::Type(_) = sym.data { |
|
| 2372 | 2410 | return true; |
|
| 2373 | 2411 | } |
|
| 2374 | 2412 | return false; |
|
| 2375 | 2413 | } |
|
| 2376 | 2414 | ||
| 2377 | 2415 | /// Find a symbol by name in a specific scope, filtered by a predicate. |
|
| 2378 | - | fn findInScope(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol { |
|
| 2416 | + | unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol { |
|
| 2379 | 2417 | for i in 0..scope.symbolsLen { |
|
| 2380 | 2418 | let sym = scope.symbols[i]; |
|
| 2381 | 2419 | if sym.name == name and predicate(sym) { |
|
| 2382 | 2420 | return sym; |
|
| 2383 | 2421 | } |
|
| 2384 | 2422 | } |
|
| 2385 | 2423 | return nil; |
|
| 2386 | 2424 | } |
|
| 2387 | 2425 | ||
| 2388 | 2426 | /// Find a symbol by name, traversing scopes upwards, filtered by a predicate. |
|
| 2389 | - | fn findInScopeRecursive(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol { |
|
| 2427 | + | unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol { |
|
| 2390 | 2428 | let mut curr = scope; |
|
| 2391 | 2429 | loop { |
|
| 2392 | 2430 | if let sym = findInScope(curr, name, predicate) { |
|
| 2393 | 2431 | return sym; |
|
| 2394 | 2432 | } |
| 2400 | 2438 | } |
|
| 2401 | 2439 | return nil; |
|
| 2402 | 2440 | } |
|
| 2403 | 2441 | ||
| 2404 | 2442 | /// Find a symbol by name in a specific scope (matches any symbol kind). |
|
| 2405 | - | export fn findSymbolInScope(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2443 | + | export unsafe fn findSymbolInScope(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 2406 | 2444 | return findInScope(scope, name, isAnySymbol); |
|
| 2407 | 2445 | } |
|
| 2408 | 2446 | ||
| 2409 | 2447 | /// Look up a value symbol by name, searching from the given scope outward. |
|
| 2410 | - | fn findValueSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2448 | + | unsafe fn findValueSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 2411 | 2449 | return findInScopeRecursive(scope, name, isValueSymbol); |
|
| 2412 | 2450 | } |
|
| 2413 | 2451 | ||
| 2414 | 2452 | /// Look up a type symbol by name, searching from the given scope outward. |
|
| 2415 | - | fn findTypeSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2453 | + | unsafe fn findTypeSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 2416 | 2454 | return findInScopeRecursive(scope, name, isTypeSymbol); |
|
| 2417 | 2455 | } |
|
| 2418 | 2456 | ||
| 2419 | 2457 | /// Like `findValueSymbol`, but finds symbols of any kinds. |
|
| 2420 | - | fn findAnySymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2458 | + | unsafe fn findAnySymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 2421 | 2459 | return findInScopeRecursive(scope, name, isAnySymbol); |
|
| 2422 | 2460 | } |
|
| 2423 | 2461 | ||
| 2424 | 2462 | /// Flatten an identifier or scope access chain into an array of name segments. |
|
| 2425 | 2463 | /// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`. |
| 2461 | 2499 | return out; |
|
| 2462 | 2500 | } |
|
| 2463 | 2501 | ||
| 2464 | 2502 | /// Find the module ID for a given scope by walking up the scope chain until |
|
| 2465 | 2503 | /// we hit the module's scope. |
|
| 2466 | - | fn findModuleForScope(scope: *Scope) -> ?u16 { |
|
| 2504 | + | unsafe fn findModuleForScope(scope: *unsafe Scope) -> ?u16 { |
|
| 2467 | 2505 | let mut s = scope; |
|
| 2468 | 2506 | loop { |
|
| 2469 | 2507 | if let id = s.moduleId { |
|
| 2470 | 2508 | return id; |
|
| 2471 | 2509 | } |
| 2477 | 2515 | } |
|
| 2478 | 2516 | } |
|
| 2479 | 2517 | ||
| 2480 | 2518 | /// Get the parent module scope for the current module. |
|
| 2481 | 2519 | /// Returns the scope of the parent module, or `nil` if this is a root module. |
|
| 2482 | - | unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) { |
|
| 2483 | - | let currentMod = module::get(&*self.moduleGraph, self.currentMod) |
|
| 2520 | + | unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
|
| 2521 | + | let currentMod = module::get(self.moduleGraph, self.currentMod) |
|
| 2484 | 2522 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2485 | 2523 | let parentId = currentMod.parent |
|
| 2486 | 2524 | else return nil; // No parent module. |
|
| 2487 | 2525 | ||
| 2488 | 2526 | return self.moduleScopes[parentId as u32]; |
| 2514 | 2552 | ||
| 2515 | 2553 | /// Check if a symbol is accessible from the given scope. |
|
| 2516 | 2554 | /// A symbol is accessible if: |
|
| 2517 | 2555 | /// * It has the `export` attribute, OR |
|
| 2518 | 2556 | /// * It's being accessed from within the module where it was defined. |
|
| 2519 | - | fn isSymbolVisible(sym: *Symbol, symScope: *Scope, fromScope: *Scope) -> bool { |
|
| 2557 | + | unsafe fn isSymbolVisible(sym: *unsafe Symbol, symScope: *unsafe Scope, fromScope: *unsafe Scope) -> bool { |
|
| 2520 | 2558 | // Public symbols are visible from anywhere. |
|
| 2521 | 2559 | if ast::hasAttribute(sym.attrs, ast::Attribute::Export) { |
|
| 2522 | 2560 | return true; |
|
| 2523 | 2561 | } |
|
| 2524 | 2562 | // In test mode, @test symbols are visible from anywhere |
| 2537 | 2575 | /// starting from the given scope. |
|
| 2538 | 2576 | unsafe fn resolveAccess( |
|
| 2539 | 2577 | self: &mut Resolver, |
|
| 2540 | 2578 | node: *ast::Node, |
|
| 2541 | 2579 | access: ast::Access, |
|
| 2542 | - | scope: *Scope |
|
| 2543 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2580 | + | scope: *unsafe Scope |
|
| 2581 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2544 | 2582 | // Handle `super` access by adjusting scope and node. |
|
| 2545 | 2583 | let mut startScope = scope; |
|
| 2546 | 2584 | let mut pathNode = node; |
|
| 2547 | 2585 | if let superAccess = try checkSuperAccess(self, node) { |
|
| 2548 | 2586 | set startScope = superAccess.scope; |
| 2561 | 2599 | unsafe fn resolvePath( |
|
| 2562 | 2600 | self: &mut Resolver, |
|
| 2563 | 2601 | node: *ast::Node, |
|
| 2564 | 2602 | access: ast::Access, |
|
| 2565 | 2603 | path: &[*[u8]], |
|
| 2566 | - | scope: *Scope |
|
| 2567 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 2604 | + | scope: *unsafe Scope |
|
| 2605 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 2568 | 2606 | assert path.len <> 0, "resolvePath: empty path"; |
|
| 2569 | 2607 | // Start by finding the root of the path. |
|
| 2570 | 2608 | let root = path[0]; |
|
| 2571 | 2609 | let sym = findInScopeRecursive(scope, root, isAnySymbol) |
|
| 2572 | 2610 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root)); |
| 2642 | 2680 | /// Recursively resolve the remaining path segments by traversing child modules. |
|
| 2643 | 2681 | unsafe fn resolveModulePathRecursive( |
|
| 2644 | 2682 | self: &mut Resolver, |
|
| 2645 | 2683 | node: *ast::Node, |
|
| 2646 | 2684 | path: &[*[u8]], |
|
| 2647 | - | sym: *Symbol |
|
| 2685 | + | sym: *unsafe Symbol |
|
| 2648 | 2686 | ) -> ResolvedModule throws (ResolveError) { |
|
| 2649 | 2687 | let case SymbolData::Module { entry, scope } = sym.data |
|
| 2650 | 2688 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2651 | 2689 | ||
| 2652 | 2690 | if path.len == 0 { |
| 2666 | 2704 | childSym |
|
| 2667 | 2705 | ); |
|
| 2668 | 2706 | } |
|
| 2669 | 2707 | ||
| 2670 | 2708 | /// Resolve a type name, which could be an identifier or scoped path. |
|
| 2671 | - | unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) { |
|
| 2709 | + | unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) { |
|
| 2672 | 2710 | match node.value { |
|
| 2673 | 2711 | case ast::NodeValue::Ident(name) => { |
|
| 2674 | 2712 | let sym = findTypeSymbol(self.scope, name) |
|
| 2675 | 2713 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 2676 | 2714 | let case SymbolData::Type(ty) = sym.data |
| 3014 | 3052 | } |
|
| 3015 | 3053 | return nil; |
|
| 3016 | 3054 | } |
|
| 3017 | 3055 | ||
| 3018 | 3056 | /// Visit every node contained in a list, returning the last resolved type. |
|
| 3019 | - | unsafe fn visitList(self: &mut Resolver, list: *mut [*ast::Node]) -> Type |
|
| 3057 | + | unsafe fn visitList(self: &mut Resolver, list: *[*ast::Node]) -> Type |
|
| 3020 | 3058 | throws (ResolveError) |
|
| 3021 | 3059 | { |
|
| 3022 | 3060 | let mut diverges = false; |
|
| 3023 | 3061 | for item in list { |
|
| 3024 | 3062 | if try infer(self, item) == Type::Never { |
| 3136 | 3174 | else => return false, |
|
| 3137 | 3175 | } |
|
| 3138 | 3176 | } |
|
| 3139 | 3177 | ||
| 3140 | 3178 | /// Determine whether a node represents a compile-time constant expression. |
|
| 3141 | - | export fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool { |
|
| 3179 | + | export unsafe fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool { |
|
| 3142 | 3180 | match node.value { |
|
| 3143 | 3181 | case ast::NodeValue::Bool(_), |
|
| 3144 | 3182 | ast::NodeValue::Char(_), |
|
| 3145 | 3183 | ast::NodeValue::Number(_), |
|
| 3146 | 3184 | ast::NodeValue::String(_), |
| 3301 | 3339 | ||
| 3302 | 3340 | /// Check that constructor arguments match record fields. |
|
| 3303 | 3341 | /// |
|
| 3304 | 3342 | /// Verifies argument count matches field count, and that each argument is |
|
| 3305 | 3343 | /// assignable to its corresponding field type. |
|
| 3306 | - | unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType) |
|
| 3344 | + | unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType) |
|
| 3307 | 3345 | throws (ResolveError) |
|
| 3308 | 3346 | { |
|
| 3309 | 3347 | try checkRecordArity(self, args, recInfo, node); |
|
| 3310 | 3348 | for arg, i in args { |
|
| 3311 | 3349 | let fieldType = recInfo.fields[i].fieldType; |
|
| 3312 | 3350 | try checkAssignable(self, arg, fieldType); |
|
| 3313 | 3351 | } |
|
| 3314 | 3352 | } |
|
| 3315 | 3353 | ||
| 3316 | 3354 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
|
| 3317 | - | unsafe fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3355 | + | unsafe fn checkRecordArity(self: &mut Resolver, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3318 | 3356 | if args.len <> recInfo.fields.len { |
|
| 3319 | 3357 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 3320 | 3358 | expected: recInfo.fields.len as u32, |
|
| 3321 | 3359 | actual: args.len, |
|
| 3322 | 3360 | })); |
| 3384 | 3422 | let mut fnType = FnType { |
|
| 3385 | 3423 | paramTypes: &[], |
|
| 3386 | 3424 | returnType: allocType(self, retTy), |
|
| 3387 | 3425 | throwList: &[], |
|
| 3388 | 3426 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
|
| 3389 | - | localCount: 0, |
|
| 3390 | 3427 | }; |
|
| 3391 | 3428 | // Enter the function scope to process parameters. |
|
| 3392 | - | enterFn(self, node, &mut fnType); |
|
| 3429 | + | enterFn(self, node, &fnType); |
|
| 3393 | 3430 | ||
| 3394 | 3431 | if decl.sig.params.len > MAX_FN_PARAMS { |
|
| 3395 | 3432 | exitFn(self); |
|
| 3396 | 3433 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
|
| 3397 | 3434 | expected: MAX_FN_PARAMS, |
| 3461 | 3498 | ||
| 3462 | 3499 | /// Resolve a function or method body and restore the enclosing context. |
|
| 3463 | 3500 | unsafe fn resolveExecutableBody( |
|
| 3464 | 3501 | self: &mut Resolver, |
|
| 3465 | 3502 | node: *ast::Node, |
|
| 3466 | - | fnType: *mut FnType, |
|
| 3503 | + | fnType: *FnType, |
|
| 3467 | 3504 | receiverName: ?*ast::Node, |
|
| 3468 | - | params: *mut [*ast::Node], |
|
| 3505 | + | params: *[*ast::Node], |
|
| 3469 | 3506 | body: *ast::Node, |
|
| 3470 | 3507 | ) throws (ResolveError) { |
|
| 3471 | 3508 | let wasUnsafe = self.inUnsafeContext; |
|
| 3472 | 3509 | set self.inUnsafeContext = fnType.isUnsafe; |
|
| 3473 | 3510 | // Enter function scope. |
| 3489 | 3526 | /// Return whether a required return is missing. |
|
| 3490 | 3527 | unsafe fn checkExecutableBody( |
|
| 3491 | 3528 | self: &mut Resolver, |
|
| 3492 | 3529 | fnType: *FnType, |
|
| 3493 | 3530 | receiverName: ?*ast::Node, |
|
| 3494 | - | params: *mut [*ast::Node], |
|
| 3531 | + | params: *[*ast::Node], |
|
| 3495 | 3532 | body: *ast::Node, |
|
| 3496 | 3533 | ) -> bool throws (ResolveError) { |
|
| 3497 | 3534 | if let receiver = receiverName { |
|
| 3498 | 3535 | // Bind the receiver parameter. |
|
| 3499 | 3536 | let receiverTy = *fnType.paramTypes[0]; |
| 3530 | 3567 | /// The declaration permits implicit copies. |
|
| 3531 | 3568 | copy: bool, |
|
| 3532 | 3569 | } |
|
| 3533 | 3570 | ||
| 3534 | 3571 | /// Resolve compiler-known ownership markers from a derive list. |
|
| 3535 | - | unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers |
|
| 3572 | + | unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *[*ast::Node]) -> OwnershipMarkers |
|
| 3536 | 3573 | throws (ResolveError) |
|
| 3537 | 3574 | { |
|
| 3538 | 3575 | let mut result = OwnershipMarkers { linear: false, copy: false }; |
|
| 3539 | 3576 | for derive in derives { |
|
| 3540 | 3577 | let name = try nodeName(self, derive); |
| 3561 | 3598 | } |
|
| 3562 | 3599 | return result; |
|
| 3563 | 3600 | } |
|
| 3564 | 3601 | ||
| 3565 | 3602 | /// Resolve record fields from a node list. |
|
| 3566 | - | unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType |
|
| 3603 | + | unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType |
|
| 3567 | 3604 | throws (ResolveError) |
|
| 3568 | 3605 | { |
|
| 3569 | 3606 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3570 | - | let mut result: *mut [RecordField] = &mut []; |
|
| 3607 | + | let mut result: *unsafe mut [RecordField] = &mut []; |
|
| 3571 | 3608 | let mut currentOffset: u32 = 0; |
|
| 3572 | 3609 | let mut maxAlignment: u32 = 1; |
|
| 3573 | 3610 | ||
| 3574 | 3611 | if fields.len > parser::MAX_RECORD_FIELDS { |
|
| 3575 | 3612 | throw emitError(self, node, ErrorKind::Internal); |
| 3655 | 3692 | ||
| 3656 | 3693 | set *nominalTy = NominalType::Record(recordType); |
|
| 3657 | 3694 | } |
|
| 3658 | 3695 | ||
| 3659 | 3696 | /// Bind a type name. |
|
| 3660 | - | unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3697 | + | unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 3661 | 3698 | throws (ResolveError) |
|
| 3662 | 3699 | { |
|
| 3663 | 3700 | let attrMask = resolveAttributes(self, attrs); |
|
| 3664 | 3701 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3665 | 3702 |
| 3669 | 3706 | ||
| 3670 | 3707 | return try bindTypeIdent(self, name, node, nominalTy, attrMask); |
|
| 3671 | 3708 | } |
|
| 3672 | 3709 | ||
| 3673 | 3710 | /// Allocate a trait type descriptor and return a pointer to it. |
|
| 3674 | - | unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType { |
|
| 3675 | - | let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 3676 | - | let entry = p as *mut TraitType; |
|
| 3711 | + | unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *unsafe mut TraitType { |
|
| 3712 | + | let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 3713 | + | let entry = p as *unsafe mut TraitType; |
|
| 3677 | 3714 | set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] }; |
|
| 3678 | 3715 | ||
| 3679 | 3716 | return entry; |
|
| 3680 | 3717 | } |
|
| 3681 | 3718 | ||
| 3682 | 3719 | /// Bind a trait name in the current scope. |
|
| 3683 | - | unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3720 | + | unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 3684 | 3721 | throws (ResolveError) |
|
| 3685 | 3722 | { |
|
| 3686 | 3723 | let attrMask = resolveAttributes(self, attrs); |
|
| 3687 | 3724 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3688 | 3725 |
| 3697 | 3734 | ||
| 3698 | 3735 | return sym; |
|
| 3699 | 3736 | } |
|
| 3700 | 3737 | ||
| 3701 | 3738 | /// Find a trait method by name. |
|
| 3702 | - | export fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod { |
|
| 3739 | + | export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod { |
|
| 3703 | 3740 | for i in 0..traitType.methods.len { |
|
| 3704 | 3741 | if traitType.methods[i].name == name { |
|
| 3705 | 3742 | return &traitType.methods[i]; |
|
| 3706 | 3743 | } |
|
| 3707 | 3744 | } |
|
| 3708 | 3745 | return nil; |
|
| 3709 | 3746 | } |
|
| 3710 | 3747 | ||
| 3711 | 3748 | /// Resolve a trait declaration body: supertrait methods, then own methods. |
|
| 3712 | - | unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node]) |
|
| 3749 | + | unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node]) |
|
| 3713 | 3750 | throws (ResolveError) |
|
| 3714 | 3751 | { |
|
| 3715 | 3752 | let sym = symbolFor(self, node) |
|
| 3716 | 3753 | else return; |
|
| 3717 | 3754 | let case SymbolData::Trait(traitType) = sym.data |
| 3826 | 3863 | let fnType = FnType { |
|
| 3827 | 3864 | paramTypes: ¶mTypes[..], |
|
| 3828 | 3865 | returnType: retType, |
|
| 3829 | 3866 | throwList: &throwList[..], |
|
| 3830 | 3867 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
|
| 3831 | - | localCount: 0, |
|
| 3832 | 3868 | }; |
|
| 3833 | 3869 | traitType.methods.append(TraitMethod { |
|
| 3834 | 3870 | name: methodName, |
|
| 3835 | 3871 | fnType: allocFnType(self, fnType), |
|
| 3836 | 3872 | mutable, |
| 3842 | 3878 | } |
|
| 3843 | 3879 | } |
|
| 3844 | 3880 | ||
| 3845 | 3881 | /// Resolve a name path node to a symbol. |
|
| 3846 | 3882 | /// Used for trait and type references in instance declarations and trait objects. |
|
| 3847 | - | unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *mut Symbol |
|
| 3883 | + | unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *unsafe mut Symbol |
|
| 3848 | 3884 | throws (ResolveError) |
|
| 3849 | 3885 | { |
|
| 3850 | 3886 | match node.value { |
|
| 3851 | 3887 | case ast::NodeValue::Ident(name) => { |
|
| 3852 | 3888 | let sym = findAnySymbol(self.scope, name) |
| 3869 | 3905 | unsafe fn resolveInstanceDecl( |
|
| 3870 | 3906 | self: &mut Resolver, |
|
| 3871 | 3907 | node: *ast::Node, |
|
| 3872 | 3908 | traitName: *ast::Node, |
|
| 3873 | 3909 | targetType: *ast::Node, |
|
| 3874 | - | methods: *mut [*ast::Node] |
|
| 3910 | + | methods: *[*ast::Node] |
|
| 3875 | 3911 | ) throws (ResolveError) { |
|
| 3876 | 3912 | // Look up the trait. |
|
| 3877 | 3913 | let traitSym = try resolveNamePath(self, traitName); |
|
| 3878 | 3914 | let case SymbolData::Trait(traitInfo) = traitSym.data |
|
| 3879 | 3915 | else throw emitError(self, traitName, ErrorKind::Internal); |
| 3896 | 3932 | ||
| 3897 | 3933 | // Build the instance entry. |
|
| 3898 | 3934 | if self.instancesLen >= MAX_INSTANCES { |
|
| 3899 | 3935 | throw emitError(self, node, ErrorKind::Internal); |
|
| 3900 | 3936 | } |
|
| 3901 | - | let methodSlice = try! alloc::allocSlice( |
|
| 3902 | - | &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), traitInfo.methods.len as u32 |
|
| 3903 | - | ) as *mut [*mut Symbol]; |
|
| 3937 | + | let methodSlice = try! alloc::allocRawSlice( |
|
| 3938 | + | &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32 |
|
| 3939 | + | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 3904 | 3940 | let mut entry = InstanceEntry { |
|
| 3905 | 3941 | traitType: traitInfo, |
|
| 3906 | 3942 | concreteType, |
|
| 3907 | 3943 | concreteTypeName: typeSym.name, |
|
| 3908 | 3944 | moduleId: self.currentMod, |
| 4023 | 4059 | let fnType = FnType { |
|
| 4024 | 4060 | paramTypes: ¶mTypes[..], |
|
| 4025 | 4061 | returnType: tm.fnType.returnType, |
|
| 4026 | 4062 | throwList: tm.fnType.throwList, |
|
| 4027 | 4063 | isUnsafe: tm.fnType.isUnsafe, |
|
| 4028 | - | localCount: 0, |
|
| 4029 | 4064 | }; |
|
| 4030 | 4065 | ||
| 4031 | 4066 | // Create a symbol for the instance method without binding it into the |
|
| 4032 | 4067 | // module scope. Instance methods are dispatched via v-table, so they |
|
| 4033 | 4068 | // must not pollute the enclosing scope. |
| 4071 | 4106 | ||
| 4072 | 4107 | setNodeType(self, node, Type::Void); |
|
| 4073 | 4108 | } |
|
| 4074 | 4109 | ||
| 4075 | 4110 | /// Resolve instance method bodies. |
|
| 4076 | - | unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *mut [*ast::Node]) |
|
| 4111 | + | unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *[*ast::Node]) |
|
| 4077 | 4112 | throws (ResolveError) |
|
| 4078 | 4113 | { |
|
| 4079 | 4114 | for methodNode in methods { |
|
| 4080 | 4115 | let case ast::NodeValue::MethodDecl { |
|
| 4081 | 4116 | name, receiverName, receiverType, sig, body, .. |
| 4197 | 4232 | let fullFnType = FnType { |
|
| 4198 | 4233 | paramTypes: ¶mTypes[..], |
|
| 4199 | 4234 | returnType: retTypePtr, |
|
| 4200 | 4235 | throwList, |
|
| 4201 | 4236 | isUnsafe, |
|
| 4202 | - | localCount: 0, |
|
| 4203 | 4237 | }; |
|
| 4204 | 4238 | let fnTy = Type::Fn(allocFnType(self, fullFnType)); |
|
| 4205 | 4239 | ||
| 4206 | 4240 | // Function type excluding receiver, for call arg checking. |
|
| 4207 | 4241 | let checkFnType = FnType { |
|
| 4208 | 4242 | paramTypes: ¶mTypes[1..], |
|
| 4209 | 4243 | returnType: retTypePtr, |
|
| 4210 | 4244 | throwList, |
|
| 4211 | 4245 | isUnsafe, |
|
| 4212 | - | localCount: 0, |
|
| 4213 | 4246 | }; |
|
| 4214 | 4247 | ||
| 4215 | 4248 | // Create a symbol for the method without binding it into the module scope. |
|
| 4216 | 4249 | let sym = allocSymbol(self, SymbolData::Value { |
|
| 4217 | 4250 | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
| 4236 | 4269 | }; |
|
| 4237 | 4270 | set self.methodsLen += 1; |
|
| 4238 | 4271 | } |
|
| 4239 | 4272 | ||
| 4240 | 4273 | /// Look up an instance entry by trait and concrete type. |
|
| 4241 | - | unsafe fn findInstance(self: &Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
|
| 4274 | + | unsafe fn findInstance(self: &Resolver, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
|
| 4242 | 4275 | for i in 0..self.instancesLen { |
|
| 4243 | 4276 | let entry: *unsafe InstanceEntry = &self.instances[i]; |
|
| 4244 | 4277 | if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) { |
|
| 4245 | 4278 | return entry; |
|
| 4246 | 4279 | } |
| 4258 | 4291 | } |
|
| 4259 | 4292 | return nil; |
|
| 4260 | 4293 | } |
|
| 4261 | 4294 | ||
| 4262 | 4295 | /// Look up a standalone method entry by its symbol. |
|
| 4263 | - | export unsafe fn findMethodBySymbol(self: &Resolver, sym: *mut Symbol) -> ?*unsafe MethodEntry { |
|
| 4296 | + | export unsafe fn findMethodBySymbol(self: &Resolver, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry { |
|
| 4264 | 4297 | for i in 0..self.methodsLen { |
|
| 4265 | 4298 | let entry: *unsafe MethodEntry = &self.methods[i]; |
|
| 4266 | 4299 | if entry.symbol == sym { |
|
| 4267 | 4300 | return entry; |
|
| 4268 | 4301 | } |
| 4285 | 4318 | // do it again. |
|
| 4286 | 4319 | if let case NominalType::Union(_) = *nominalTy { |
|
| 4287 | 4320 | return; |
|
| 4288 | 4321 | } |
|
| 4289 | 4322 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4290 | - | let mut variants: *mut [UnionVariant] = &mut []; |
|
| 4323 | + | let mut variants: *unsafe mut [UnionVariant] = &mut []; |
|
| 4291 | 4324 | ||
| 4292 | 4325 | // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion |
|
| 4293 | 4326 | // when a variant references this union type (e.g. record payloads with `*[Self]`). |
|
| 4294 | 4327 | // TODO: It would be best to have a resolving state eg. `Visiting` for this situation. |
|
| 4295 | 4328 | let markers = try resolveOwnershipMarkers(self, decl.derives); |
| 4678 | 4711 | if let pat = forStmt.index { |
|
| 4679 | 4712 | try bindForLoopPattern(self, pat, Type::U32, false); |
|
| 4680 | 4713 | } |
|
| 4681 | 4714 | // The lowerer always creates at least one internal variable for iteration, |
|
| 4682 | 4715 | // even when the binding is a placeholder or no explicit index is given. |
|
| 4683 | - | if let mut fnType = self.currentFn { |
|
| 4684 | - | set fnType.localCount += 1; |
|
| 4716 | + | if let owner = self.currentFnNode { |
|
| 4717 | + | set self.nodeData.entries[owner.id].localCount += 1; |
|
| 4685 | 4718 | } |
|
| 4686 | 4719 | try visitLoop(self, forStmt.body); |
|
| 4687 | 4720 | exitScope(self); |
|
| 4688 | 4721 | ||
| 4689 | 4722 | try visitOptional(self, forStmt.elseBranch, Type::Void); |
| 4784 | 4817 | ||
| 4785 | 4818 | /// Check whether any pattern in a case prong matches unconditionally. |
|
| 4786 | 4819 | /// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies. |
|
| 4787 | 4820 | /// Note: top-level identifiers in `case` are comparisons, not bindings, |
|
| 4788 | 4821 | /// so they do not count as wildcards. |
|
| 4789 | - | fn hasWildcardPattern(patterns: *mut [*ast::Node]) -> bool { |
|
| 4822 | + | fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool { |
|
| 4790 | 4823 | for pattern in patterns { |
|
| 4791 | 4824 | match pattern.value { |
|
| 4792 | 4825 | case ast::NodeValue::Placeholder => return true, |
|
| 4793 | 4826 | case ast::NodeValue::ArrayLit(items) => { |
|
| 4794 | 4827 | if isIrrefutableArrayPattern(items) { |
| 4801 | 4834 | return false; |
|
| 4802 | 4835 | } |
|
| 4803 | 4836 | ||
| 4804 | 4837 | /// Check whether all elements of an array pattern are irrefutable. |
|
| 4805 | 4838 | /// Inside array patterns, identifiers are bindings, not comparisons. |
|
| 4806 | - | fn isIrrefutableArrayPattern(items: *mut [*ast::Node]) -> bool { |
|
| 4839 | + | fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool { |
|
| 4807 | 4840 | for item in items { |
|
| 4808 | 4841 | match item.value { |
|
| 4809 | 4842 | case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {} |
|
| 4810 | 4843 | case ast::NodeValue::ArrayLit(inner) => { |
|
| 4811 | 4844 | if not isIrrefutableArrayPattern(inner) { |
| 5420 | 5453 | /// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`. |
|
| 5421 | 5454 | unsafe fn resolveBuiltinCall( |
|
| 5422 | 5455 | self: &mut Resolver, |
|
| 5423 | 5456 | node: *ast::Node, |
|
| 5424 | 5457 | kind: ast::Builtin, |
|
| 5425 | - | args: *mut [*ast::Node] |
|
| 5458 | + | args: *[*ast::Node] |
|
| 5426 | 5459 | ) -> Type throws (ResolveError) { |
|
| 5427 | 5460 | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
|
| 5428 | 5461 | if kind == ast::Builtin::SliceOf { |
|
| 5429 | 5462 | if args.len <> 2 and args.len <> 3 { |
|
| 5430 | 5463 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
| 5612 | 5645 | ||
| 5613 | 5646 | // Associate return type to call. |
|
| 5614 | 5647 | return setNodeType(self, node, *info.returnType); |
|
| 5615 | 5648 | } |
|
| 5616 | 5649 | ||
| 5650 | + | /// Check the allocator layout and the callback ABI used by slice append. |
|
| 5651 | + | unsafe fn isSliceAllocator(ty: Type) -> bool { |
|
| 5652 | + | let case Type::Nominal(NominalType::Record(rec)) = ty else return false; |
|
| 5653 | + | if rec.fields.len <> 2 or not rec.labeled { |
|
| 5654 | + | return false; |
|
| 5655 | + | } |
|
| 5656 | + | let func = rec.fields[0]; |
|
| 5657 | + | let ctx = rec.fields[1]; |
|
| 5658 | + | let funcName = func.name else return false; |
|
| 5659 | + | let ctxName = ctx.name else return false; |
|
| 5660 | + | if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or |
|
| 5661 | + | func.offset <> 0 or ctx.offset <> 8 |
|
| 5662 | + | { |
|
| 5663 | + | return false; |
|
| 5664 | + | } |
|
| 5665 | + | let case Type::Fn(callback) = func.fieldType else return false; |
|
| 5666 | + | let case Type::Pointer { target, .. } = ctx.fieldType else return false; |
|
| 5667 | + | if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 { |
|
| 5668 | + | return false; |
|
| 5669 | + | } |
|
| 5670 | + | if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or |
|
| 5671 | + | *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32 |
|
| 5672 | + | { |
|
| 5673 | + | return false; |
|
| 5674 | + | } |
|
| 5675 | + | let case Type::Pointer { class, target: result, mutable } = *callback.returnType |
|
| 5676 | + | else return false; |
|
| 5677 | + | return class == types::PointerClass::Owned and mutable and *result == Type::Opaque; |
|
| 5678 | + | } |
|
| 5679 | + | ||
| 5617 | 5680 | /// Resolve `slice.append(val, allocator)`. |
|
| 5618 | 5681 | unsafe fn resolveSliceAppend( |
|
| 5619 | 5682 | self: &mut Resolver, |
|
| 5620 | 5683 | node: *ast::Node, |
|
| 5621 | 5684 | parent: *ast::Node, |
|
| 5622 | 5685 | parentType: Type, |
|
| 5623 | - | args: *mut [*ast::Node], |
|
| 5686 | + | args: *[*ast::Node], |
|
| 5624 | 5687 | elemType: *Type, |
|
| 5625 | 5688 | mutable: bool |
|
| 5626 | 5689 | ) -> Type throws (ResolveError) { |
|
| 5627 | 5690 | if not mutable { |
|
| 5628 | 5691 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 5633 | 5696 | actual: args.len as u32, |
|
| 5634 | 5697 | })); |
|
| 5635 | 5698 | } |
|
| 5636 | 5699 | // First argument must be assignable to the element type. |
|
| 5637 | 5700 | try checkAssignable(self, args[0], *elemType); |
|
| 5638 | - | // Second argument: the allocator. We accept any type -- the lowerer |
|
| 5639 | - | // reads `.func` and `.ctx` at fixed offsets. |
|
| 5640 | - | try visit(self, args[1], Type::Unknown); |
|
| 5701 | + | // The allocator stores its callback and context at fixed offsets. |
|
| 5702 | + | let allocatorTy = try infer(self, args[1]); |
|
| 5703 | + | if let case Type::Nominal(info) = allocatorTy { |
|
| 5704 | + | try ensureNominalResolved(self, info, args[1]); |
|
| 5705 | + | } |
|
| 5706 | + | if not isSliceAllocator(allocatorTy) { |
|
| 5707 | + | throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator); |
|
| 5708 | + | } |
|
| 5641 | 5709 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
|
| 5642 | 5710 | ||
| 5643 | 5711 | // Return the parent's type so the caller can rebind: |
|
| 5644 | 5712 | return setNodeType(self, node, parentType); |
|
| 5645 | 5713 | } |
| 5647 | 5715 | /// Resolve `slice.delete(index)`. |
|
| 5648 | 5716 | unsafe fn resolveSliceDelete( |
|
| 5649 | 5717 | self: &mut Resolver, |
|
| 5650 | 5718 | node: *ast::Node, |
|
| 5651 | 5719 | parent: *ast::Node, |
|
| 5652 | - | args: *mut [*ast::Node], |
|
| 5720 | + | args: *[*ast::Node], |
|
| 5653 | 5721 | elemType: *Type, |
|
| 5654 | 5722 | mutable: bool |
|
| 5655 | 5723 | ) -> Type throws (ResolveError) { |
|
| 5656 | 5724 | if not mutable { |
|
| 5657 | 5725 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 5835 | 5903 | } |
|
| 5836 | 5904 | return nil; |
|
| 5837 | 5905 | } |
|
| 5838 | 5906 | ||
| 5839 | 5907 | /// Analyze a union constructor call with payload. |
|
| 5840 | - | unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type |
|
| 5908 | + | unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type |
|
| 5841 | 5909 | throws (ResolveError) |
|
| 5842 | 5910 | { |
|
| 5843 | 5911 | // Get the union nominal type. |
|
| 5844 | 5912 | let case NominalType::Union(unionType) = *unionNominal |
|
| 5845 | 5913 | else panic "resolveUnionConstructorCall: not a union type"; |
| 5870 | 5938 | /// Analyze an unlabeled record constructor call. |
|
| 5871 | 5939 | /// |
|
| 5872 | 5940 | /// Handles the syntax `R(a, b)` for unlabeled records, checking that the |
|
| 5873 | 5941 | /// number of arguments matches the record's field count and that each argument |
|
| 5874 | 5942 | /// is assignable to its corresponding field type. |
|
| 5875 | - | unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type |
|
| 5943 | + | unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type |
|
| 5876 | 5944 | throws (ResolveError) |
|
| 5877 | 5945 | { |
|
| 5878 | 5946 | let case NominalType::Record(recInfo) = *recordType |
|
| 5879 | 5947 | else panic "resolveRecordConstructorCall: not a record type"; |
|
| 5880 | 5948 |
| 6044 | 6112 | } |
|
| 6045 | 6113 | return setNodeType(self, node, innerHint); |
|
| 6046 | 6114 | } |
|
| 6047 | 6115 | ||
| 6048 | 6116 | /// Analyze an array literal expression. |
|
| 6049 | - | unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type |
|
| 6117 | + | unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type |
|
| 6050 | 6118 | throws (ResolveError) |
|
| 6051 | 6119 | { |
|
| 6052 | 6120 | let length = items.len; |
|
| 6053 | 6121 | let mut expectedTy: Type = Type::Unknown; |
|
| 6054 | 6122 |
| 6103 | 6171 | self: &mut Resolver, |
|
| 6104 | 6172 | node: *ast::Node, |
|
| 6105 | 6173 | access: ast::Access, |
|
| 6106 | 6174 | unionType: UnionType, |
|
| 6107 | 6175 | variantName: *[u8] |
|
| 6108 | - | ) -> *mut Symbol throws (ResolveError) { |
|
| 6176 | + | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 6109 | 6177 | // Look up the variant in the union's nominal type. |
|
| 6110 | 6178 | for i in 0..unionType.variants.len { |
|
| 6111 | 6179 | let variant = &unionType.variants[i]; |
|
| 6112 | 6180 | if variant.name == variantName { |
|
| 6113 | 6181 | let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data |
| 6351 | 6419 | } |
|
| 6352 | 6420 | } |
|
| 6353 | 6421 | } |
|
| 6354 | 6422 | ||
| 6355 | 6423 | /// Return the storage class of an addressed location. |
|
| 6356 | - | fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass { |
|
| 6424 | + | unsafe fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass { |
|
| 6357 | 6425 | match node.value { |
|
| 6358 | 6426 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
|
| 6359 | 6427 | if let sym = symbolFor(self, node) { |
|
| 6360 | 6428 | match sym.node.value { |
|
| 6361 | 6429 | case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) => |
| 6545 | 6613 | } |
|
| 6546 | 6614 | return false; |
|
| 6547 | 6615 | } |
|
| 6548 | 6616 | ||
| 6549 | 6617 | /// Check if an `as` cast between two types is valid. |
|
| 6550 | - | fn isValidCast(source: Type, target: Type) -> bool { |
|
| 6618 | + | unsafe fn isValidCast(source: Type, target: Type) -> bool { |
|
| 6551 | 6619 | // Allow identity casts. |
|
| 6552 | 6620 | if source == target { |
|
| 6553 | 6621 | return true; |
|
| 6554 | 6622 | } |
|
| 6555 | 6623 | // Allow numeric to numeric. |
| 6796 | 6864 | /// body and returns the result type. Multi-error callees with inferred bindings |
|
| 6797 | 6865 | /// are rejected; you must use typed catches. |
|
| 6798 | 6866 | unsafe fn resolveTryCatches( |
|
| 6799 | 6867 | self: &mut Resolver, |
|
| 6800 | 6868 | node: *ast::Node, |
|
| 6801 | - | catches: *mut [*ast::Node], |
|
| 6869 | + | catches: *[*ast::Node], |
|
| 6802 | 6870 | calleeInfo: *FnType, |
|
| 6803 | 6871 | resultTy: Type, |
|
| 6804 | 6872 | hint: Type |
|
| 6805 | 6873 | ) -> Type throws (ResolveError) { |
|
| 6806 | 6874 | let firstNode = catches[0]; |
| 6836 | 6904 | /// Validates that each type annotation is in the callee's throw list, that |
|
| 6837 | 6905 | /// there are no duplicate catch types, and that the clauses are exhaustive. |
|
| 6838 | 6906 | unsafe fn resolveTypedCatches( |
|
| 6839 | 6907 | self: &mut Resolver, |
|
| 6840 | 6908 | node: *ast::Node, |
|
| 6841 | - | catches: *mut [*ast::Node], |
|
| 6909 | + | catches: *[*ast::Node], |
|
| 6842 | 6910 | calleeInfo: *FnType, |
|
| 6843 | 6911 | resultTy: Type, |
|
| 6844 | 6912 | hint: Type |
|
| 6845 | 6913 | ) -> Type throws (ResolveError) { |
|
| 6846 | 6914 | // Track which of the callee's throw types have been covered. |
| 7364 | 7432 | let fnType = FnType { |
|
| 7365 | 7433 | paramTypes: ¶mTypes[..], |
|
| 7366 | 7434 | returnType: retType, |
|
| 7367 | 7435 | throwList: &throwList[..], |
|
| 7368 | 7436 | isUnsafe, |
|
| 7369 | - | localCount: 0, |
|
| 7370 | 7437 | }; |
|
| 7371 | 7438 | return Type::Fn(allocFnType(self, fnType)); |
|
| 7372 | 7439 | } |
|
| 7373 | 7440 | // Resolve an opaque trait object signature. |
|
| 7374 | 7441 | case ast::TypeSig::TraitObject { class, traitName, mutable } => { |
| 7406 | 7473 | ||
| 7407 | 7474 | let case ast::NodeValue::Block(block) = module.modBody.value |
|
| 7408 | 7475 | else panic "resolveExpr: expected block for module body"; |
|
| 7409 | 7476 | enterScope(self, module.modBody); |
|
| 7410 | 7477 | try resolveModuleDecls(self, &block) catch { |
|
| 7411 | - | return Diagnostics { errors: self.errors }; |
|
| 7478 | + | return diagnostics(self); |
|
| 7412 | 7479 | }; |
|
| 7413 | 7480 | try resolveModuleDefs(self, &block) catch { |
|
| 7414 | - | return Diagnostics { errors: self.errors }; |
|
| 7481 | + | return diagnostics(self); |
|
| 7415 | 7482 | }; |
|
| 7416 | 7483 | exitScope(self); |
|
| 7417 | 7484 | ||
| 7418 | - | return Diagnostics { errors: self.errors }; |
|
| 7485 | + | return diagnostics(self); |
|
| 7419 | 7486 | } |
|
| 7420 | 7487 | ||
| 7421 | 7488 | /// Analyze a parsed module root, ie. a block of top-level statements. |
|
| 7422 | 7489 | export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 7423 | 7490 | let case ast::NodeValue::Block(block) = root.value |
|
| 7424 | 7491 | else panic "resolveModuleRoot: expected block for module root"; |
|
| 7425 | 7492 | ||
| 7426 | 7493 | enterScope(self, root); |
|
| 7427 | 7494 | try resolveModuleDecls(self, &block) catch { |
|
| 7428 | - | return Diagnostics { errors: self.errors }; |
|
| 7495 | + | return diagnostics(self); |
|
| 7429 | 7496 | }; |
|
| 7430 | 7497 | try resolveModuleDefs(self, &block) catch { |
|
| 7431 | - | return Diagnostics { errors: self.errors }; |
|
| 7498 | + | return diagnostics(self); |
|
| 7432 | 7499 | }; |
|
| 7433 | 7500 | exitScope(self); |
|
| 7434 | 7501 | setNodeType(self, root, Type::Void); |
|
| 7435 | 7502 | ||
| 7436 | - | return Diagnostics { errors: self.errors }; |
|
| 7503 | + | return diagnostics(self); |
|
| 7437 | 7504 | } |
|
| 7438 | 7505 | ||
| 7439 | 7506 | /// Analyze the module graph. This pass processes `mod` statements, creating symbols |
|
| 7440 | 7507 | /// and scopes for them, and also binds type names in each module so that cross-module |
|
| 7441 | 7508 | /// type references work regardless of declaration order. |
| 7557 | 7624 | try visitDecl(res, stmt); |
|
| 7558 | 7625 | } |
|
| 7559 | 7626 | } |
|
| 7560 | 7627 | ||
| 7561 | 7628 | /// Find a tracked binding by symbol identity. |
|
| 7562 | - | fn findLinearBinding(env: &LinearEnv, sym: *mut Symbol) -> ?u32 { |
|
| 7629 | + | unsafe fn findLinearBinding(env: &LinearEnv, sym: *unsafe mut Symbol) -> ?u32 { |
|
| 7563 | 7630 | for i in 0..env.len { |
|
| 7564 | 7631 | if env.symbols[i] == sym { |
|
| 7565 | 7632 | return i; |
|
| 7566 | 7633 | } |
|
| 7567 | 7634 | } |
| 7575 | 7642 | ||
| 7576 | 7643 | /// Add a local binding when its resolved type moves by value. |
|
| 7577 | 7644 | unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node) |
|
| 7578 | 7645 | throws (ResolveError) |
|
| 7579 | 7646 | { |
|
| 7580 | - | let sym = symbolFor(&mut *checker.resolver, node) else return; |
|
| 7647 | + | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7581 | 7648 | let case SymbolData::Value { type: ty, .. } = sym.data else return; |
|
| 7582 | 7649 | if not isMoveOnly(ty) { |
|
| 7583 | 7650 | return; |
|
| 7584 | 7651 | } |
|
| 7585 | 7652 | if env.len >= MAX_LINEAR_BINDINGS { |
|
| 7586 | - | throw emitError(&mut *checker.resolver, node, ErrorKind::Internal); |
|
| 7653 | + | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 7587 | 7654 | } |
|
| 7588 | 7655 | set env.symbols[env.len] = sym; |
|
| 7589 | 7656 | set env.available |= (1 as u64) << (env.len as u64); |
|
| 7590 | 7657 | set env.len += 1; |
|
| 7591 | 7658 | } |
|
| 7592 | 7659 | ||
| 7593 | 7660 | /// Mark a tracked binding as uninitialized. |
|
| 7594 | - | fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) { |
|
| 7661 | + | unsafe fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) { |
|
| 7595 | 7662 | let sym = symbolFor(self, node) else return; |
|
| 7596 | 7663 | let index = findLinearBinding(env, sym) else return; |
|
| 7597 | 7664 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7598 | 7665 | } |
|
| 7599 | 7666 |
| 7609 | 7676 | let sym = env.symbols[i]; |
|
| 7610 | 7677 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7611 | 7678 | else panic "finishLinearScope: expected value symbol"; |
|
| 7612 | 7679 | if isLinear(ty) { |
|
| 7613 | 7680 | throw emitError( |
|
| 7614 | - | &mut *checker.resolver, |
|
| 7681 | + | checker.resolver, |
|
| 7615 | 7682 | sym.node, |
|
| 7616 | 7683 | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7617 | 7684 | ); |
|
| 7618 | 7685 | } |
|
| 7619 | 7686 | } |
|
| 7620 | 7687 | } |
|
| 7621 | 7688 | } |
|
| 7622 | 7689 | set env.len = start; |
|
| 7623 | 7690 | } |
|
| 7624 | 7691 | ||
| 7625 | - | /// Move or consume a tracked identifier once. |
|
| 7626 | - | unsafe fn consumeLinearIdent( |
|
| 7692 | + | /// Require a tracked identifier to remain available for any access. |
|
| 7693 | + | unsafe fn checkLinearIdent( |
|
| 7627 | 7694 | checker: &mut LinearChecker, |
|
| 7628 | 7695 | env: &mut LinearEnv, |
|
| 7629 | 7696 | node: *ast::Node, |
|
| 7630 | 7697 | ) throws (ResolveError) { |
|
| 7631 | - | let sym = symbolFor(&mut *checker.resolver, node) else return; |
|
| 7698 | + | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7632 | 7699 | let index = findLinearBinding(env, sym) else return; |
|
| 7633 | 7700 | if not linearBindingAvailable(env, index) { |
|
| 7634 | 7701 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7635 | 7702 | else panic "consumeLinearIdent: expected value symbol"; |
|
| 7636 | 7703 | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty) |
|
| 7637 | 7704 | else ErrorKind::AffineUseAfterMove(sym.name); |
|
| 7638 | - | throw emitError(&mut *checker.resolver, node, kind); |
|
| 7705 | + | throw emitError(checker.resolver, node, kind); |
|
| 7639 | 7706 | } |
|
| 7707 | + | } |
|
| 7708 | + | ||
| 7709 | + | /// Move or consume a tracked identifier once. |
|
| 7710 | + | unsafe fn consumeLinearIdent( |
|
| 7711 | + | checker: &mut LinearChecker, |
|
| 7712 | + | env: &mut LinearEnv, |
|
| 7713 | + | node: *ast::Node, |
|
| 7714 | + | ) throws (ResolveError) { |
|
| 7715 | + | try checkLinearIdent(checker, env, node); |
|
| 7716 | + | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7717 | + | let index = findLinearBinding(env, sym) else return; |
|
| 7640 | 7718 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7641 | 7719 | } |
|
| 7642 | 7720 | ||
| 7643 | 7721 | /// Merge ownership availability across two live branches. |
|
| 7644 | 7722 | /// Validate both inputs before writing to an output that can alias either input. |
| 7669 | 7747 | let sym = left.symbols[i]; |
|
| 7670 | 7748 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7671 | 7749 | else panic "joinLinearBranches: expected value symbol"; |
|
| 7672 | 7750 | if isLinear(ty) { |
|
| 7673 | 7751 | throw emitError( |
|
| 7674 | - | &mut *checker.resolver, |
|
| 7752 | + | checker.resolver, |
|
| 7675 | 7753 | node, |
|
| 7676 | 7754 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7677 | 7755 | ); |
|
| 7678 | 7756 | } |
|
| 7679 | 7757 | set available &= ~((1 as u64) << (i as u64)); |
| 7693 | 7771 | let sym = env.symbols[i]; |
|
| 7694 | 7772 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7695 | 7773 | else panic "finishLinearExit: expected value symbol"; |
|
| 7696 | 7774 | if isLinear(ty) { |
|
| 7697 | 7775 | throw emitError( |
|
| 7698 | - | &mut *checker.resolver, |
|
| 7776 | + | checker.resolver, |
|
| 7699 | 7777 | sym.node, |
|
| 7700 | 7778 | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7701 | 7779 | ); |
|
| 7702 | 7780 | } |
|
| 7703 | 7781 | } |
|
| 7704 | 7782 | } |
|
| 7705 | 7783 | set env.terminated = true; |
|
| 7706 | 7784 | } |
|
| 7707 | 7785 | ||
| 7708 | 7786 | /// Find the local root borrowed or consumed by an argument expression. |
|
| 7709 | - | fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 7787 | + | fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 7710 | 7788 | match node.value { |
|
| 7711 | 7789 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => |
|
| 7712 | 7790 | return symbolFor(self, node), |
|
| 7713 | 7791 | case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value), |
|
| 7714 | 7792 | case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target), |
| 7723 | 7801 | ||
| 7724 | 7802 | /// Protect a pattern source until its reference bindings leave scope. |
|
| 7725 | 7803 | unsafe fn addPatternLoan(checker: &mut LinearChecker, subject: *ast::Node) |
|
| 7726 | 7804 | throws (ResolveError) |
|
| 7727 | 7805 | { |
|
| 7728 | - | let root = linearRootSymbol(&mut *checker.resolver, subject) else return; |
|
| 7806 | + | let root = linearRootSymbol(checker.resolver, subject) else return; |
|
| 7729 | 7807 | if checker.loanLen >= MAX_LINEAR_BINDINGS { |
|
| 7730 | - | throw emitError(&mut *checker.resolver, subject, ErrorKind::Internal); |
|
| 7808 | + | throw emitError(checker.resolver, subject, ErrorKind::Internal); |
|
| 7731 | 7809 | } |
|
| 7732 | 7810 | set checker.loans[checker.loanLen] = root; |
|
| 7733 | 7811 | set checker.loanLen += 1; |
|
| 7734 | 7812 | } |
|
| 7735 | 7813 | ||
| 7736 | 7814 | /// Reject a write, mutable loan, or ownership transfer of a pattern source. |
|
| 7737 | 7815 | unsafe fn checkPatternLoan(checker: &mut LinearChecker, node: *ast::Node) |
|
| 7738 | 7816 | throws (ResolveError) |
|
| 7739 | 7817 | { |
|
| 7740 | - | let root = linearRootSymbol(&mut *checker.resolver, node) else return; |
|
| 7818 | + | let root = linearRootSymbol(checker.resolver, node) else return; |
|
| 7741 | 7819 | for i in 0..checker.loanLen { |
|
| 7742 | 7820 | if checker.loans[i] == root { |
|
| 7743 | - | throw emitError(&mut *checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
|
| 7821 | + | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
|
| 7744 | 7822 | } |
|
| 7745 | 7823 | } |
|
| 7746 | 7824 | } |
|
| 7747 | 7825 | ||
| 7748 | 7826 | /// Return whether a parameter can mutate or consume its argument's storage. |
|
| 7749 | - | fn isExclusiveArgument(ty: Type) -> bool { |
|
| 7827 | + | unsafe fn isExclusiveArgument(ty: Type) -> bool { |
|
| 7750 | 7828 | match ty { |
|
| 7751 | 7829 | case Type::Pointer { mutable, .. } => return mutable, |
|
| 7752 | 7830 | case Type::Slice { mutable, .. } => return mutable, |
|
| 7753 | 7831 | case Type::TraitObject { mutable, .. } => return mutable, |
|
| 7754 | 7832 | else => return isMoveOnly(ty), |
| 7764 | 7842 | ) -> bool throws (ResolveError) { |
|
| 7765 | 7843 | let mut hasReferences = false; |
|
| 7766 | 7844 | match pattern.value { |
|
| 7767 | 7845 | case ast::NodeValue::Ident(_) => { |
|
| 7768 | 7846 | try addLinearBinding(checker, env, pattern); |
|
| 7769 | - | if let ty = typeFor(&mut *checker.resolver, pattern) { |
|
| 7847 | + | if let ty = typeFor(checker.resolver, pattern) { |
|
| 7770 | 7848 | return isRefType(ty); |
|
| 7771 | 7849 | } |
|
| 7772 | 7850 | } |
|
| 7773 | 7851 | case ast::NodeValue::Call(call) => { |
|
| 7774 | 7852 | for arg in call.args { |
| 7845 | 7923 | for i in 0..mark { |
|
| 7846 | 7924 | let bit = (1 as u64) << (i as u64); |
|
| 7847 | 7925 | if (env.available & bit) <> (entryAvailable & bit) { |
|
| 7848 | 7926 | let sym = env.symbols[i]; |
|
| 7849 | 7927 | throw emitError( |
|
| 7850 | - | &mut *checker.resolver, |
|
| 7928 | + | checker.resolver, |
|
| 7851 | 7929 | node, |
|
| 7852 | 7930 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7853 | 7931 | ); |
|
| 7854 | 7932 | } |
|
| 7855 | 7933 | } |
| 7877 | 7955 | for i in 0..mark { |
|
| 7878 | 7956 | let bit = (1 as u64) << (i as u64); |
|
| 7879 | 7957 | if (env.available & bit) <> (expected & bit) { |
|
| 7880 | 7958 | let sym = env.symbols[i]; |
|
| 7881 | 7959 | throw emitError( |
|
| 7882 | - | &mut *checker.resolver, |
|
| 7960 | + | checker.resolver, |
|
| 7883 | 7961 | node, |
|
| 7884 | 7962 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7885 | 7963 | ); |
|
| 7886 | 7964 | } |
|
| 7887 | 7965 | } |
| 7930 | 8008 | let mut elseEnv = base; |
|
| 7931 | 8009 | try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage); |
|
| 7932 | 8010 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 7933 | 8011 | } |
|
| 7934 | 8012 | ||
| 8013 | + | /// Pointer patterns borrow their subject; value patterns consume it. |
|
| 8014 | + | unsafe fn patternSubjectUse(self: &Resolver, subject: *ast::Node) -> LinearUse { |
|
| 8015 | + | if let ty = typeFor(self, subject) { |
|
| 8016 | + | if let case Type::Pointer { .. } = ty { |
|
| 8017 | + | return LinearUse::Borrow; |
|
| 8018 | + | } |
|
| 8019 | + | } |
|
| 8020 | + | return LinearUse::Consume; |
|
| 8021 | + | } |
|
| 8022 | + | ||
| 7935 | 8023 | /// Check a match expression, including ownership transferred into patterns. |
|
| 7936 | 8024 | unsafe fn checkLinearMatch( |
|
| 7937 | 8025 | checker: &mut LinearChecker, |
|
| 7938 | 8026 | env: &mut LinearEnv, |
|
| 7939 | 8027 | node: *ast::Node, |
|
| 7940 | 8028 | matchExpr: ast::Match, |
|
| 7941 | 8029 | ) throws (ResolveError) { |
|
| 7942 | - | try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume); |
|
| 8030 | + | try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject)); |
|
| 7943 | 8031 | let base = *env; |
|
| 7944 | 8032 | let mut haveResult = false; |
|
| 7945 | 8033 | let mut result = base; |
|
| 7946 | 8034 | for prongNode in matchExpr.prongs { |
|
| 7947 | 8035 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
| 7968 | 8056 | for i in bindingsStart..branch.len { |
|
| 7969 | 8057 | let sym = branch.symbols[i]; |
|
| 7970 | 8058 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7971 | 8059 | else panic "checkLinearMatch: expected value symbol"; |
|
| 7972 | 8060 | if isLinear(ty) { |
|
| 7973 | - | throw emitError(&mut *checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 8061 | + | throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 7974 | 8062 | } |
|
| 7975 | 8063 | } |
|
| 7976 | 8064 | } |
|
| 7977 | 8065 | if let guard = prong.guard { |
|
| 7978 | 8066 | try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume); |
| 8005 | 8093 | match checker.resolver.nodeData.entries[node.id].extra { |
|
| 8006 | 8094 | case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => |
|
| 8007 | 8095 | set fnInfo = traitInfo.methods[methodIndex].fnType, |
|
| 8008 | 8096 | case NodeExtra::MethodCall { method } => set fnInfo = method.fnType, |
|
| 8009 | 8097 | else => { |
|
| 8010 | - | if let calleeTy = typeFor(&mut *checker.resolver, call.callee) { |
|
| 8098 | + | if let calleeTy = typeFor(checker.resolver, call.callee) { |
|
| 8011 | 8099 | if let case Type::Fn(info) = calleeTy { |
|
| 8012 | 8100 | set fnInfo = info; |
|
| 8013 | 8101 | } |
|
| 8014 | 8102 | } |
|
| 8015 | 8103 | } |
| 8018 | 8106 | for arg in call.args { |
|
| 8019 | 8107 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 8020 | 8108 | } |
|
| 8021 | 8109 | return; |
|
| 8022 | 8110 | }; |
|
| 8023 | - | let mut roots: [?*mut Symbol; MAX_FN_PARAMS + 1] = undefined; |
|
| 8111 | + | let mut roots: [?*unsafe mut Symbol; MAX_FN_PARAMS + 1] = undefined; |
|
| 8024 | 8112 | let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined; |
|
| 8025 | 8113 | let mut rootsLen: u32 = 0; |
|
| 8026 | 8114 | ||
| 8027 | 8115 | // Method function types exclude their implicit receiver. Account for it |
|
| 8028 | 8116 | // explicitly so owning receivers are consumed and reference receivers |
| 8048 | 8136 | if haveReceiver { |
|
| 8049 | 8137 | if receiverMutable or receiverClass == types::PointerClass::Owned { |
|
| 8050 | 8138 | try checkPatternLoan(checker, access.parent); |
|
| 8051 | 8139 | } |
|
| 8052 | 8140 | if receiverClass <> types::PointerClass::Unsafe { |
|
| 8053 | - | let root = linearRootSymbol(&mut *checker.resolver, access.parent); |
|
| 8141 | + | let root = linearRootSymbol(checker.resolver, access.parent); |
|
| 8054 | 8142 | if let rootSym = root { |
|
| 8055 | 8143 | set roots[rootsLen] = rootSym; |
|
| 8056 | 8144 | set exclusive[rootsLen] = |
|
| 8057 | 8145 | receiverClass == types::PointerClass::Owned or receiverMutable; |
|
| 8058 | 8146 | set rootsLen += 1; |
| 8069 | 8157 | for arg, i in call.args { |
|
| 8070 | 8158 | let expected = *info.paramTypes[i]; |
|
| 8071 | 8159 | if isExclusiveArgument(expected) { |
|
| 8072 | 8160 | try checkPatternLoan(checker, arg); |
|
| 8073 | 8161 | } |
|
| 8074 | - | let root = linearRootSymbol(&mut *checker.resolver, arg); |
|
| 8162 | + | let root = linearRootSymbol(checker.resolver, arg); |
|
| 8075 | 8163 | let mut argExclusive = isMoveOnly(expected); |
|
| 8076 | 8164 | if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 8077 | 8165 | set argExclusive = mutable; |
|
| 8078 | 8166 | } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 8079 | 8167 | set argExclusive = mutable; |
| 8086 | 8174 | if let rootSym = root { |
|
| 8087 | 8175 | for j in 0..rootsLen { |
|
| 8088 | 8176 | if let previous = roots[j] { |
|
| 8089 | 8177 | if previous == rootSym and (exclusive[j] or argExclusive) { |
|
| 8090 | 8178 | throw emitError( |
|
| 8091 | - | &mut *checker.resolver, |
|
| 8179 | + | checker.resolver, |
|
| 8092 | 8180 | arg, |
|
| 8093 | 8181 | ErrorKind::BorrowConflict(rootSym.name), |
|
| 8094 | 8182 | ); |
|
| 8095 | 8183 | } |
|
| 8096 | 8184 | } |
| 8113 | 8201 | checker: &mut LinearChecker, |
|
| 8114 | 8202 | env: &mut LinearEnv, |
|
| 8115 | 8203 | node: *ast::Node, |
|
| 8116 | 8204 | conditional: ast::IfLet, |
|
| 8117 | 8205 | ) throws (ResolveError) { |
|
| 8118 | - | if let subjectTy = typeFor(&mut *checker.resolver, conditional.pattern.scrutinee); |
|
| 8206 | + | if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee); |
|
| 8119 | 8207 | isLinear(subjectTy) |
|
| 8120 | 8208 | { |
|
| 8121 | 8209 | throw emitError( |
|
| 8122 | - | &mut *checker.resolver, |
|
| 8210 | + | checker.resolver, |
|
| 8123 | 8211 | conditional.pattern.scrutinee, |
|
| 8124 | 8212 | ErrorKind::LinearPartialMove, |
|
| 8125 | 8213 | ); |
|
| 8126 | 8214 | } |
|
| 8127 | 8215 | try checkLinearNode( |
|
| 8128 | 8216 | checker, |
|
| 8129 | 8217 | env, |
|
| 8130 | 8218 | conditional.pattern.scrutinee, |
|
| 8131 | - | LinearUse::Consume, |
|
| 8219 | + | patternSubjectUse(checker.resolver, conditional.pattern.scrutinee), |
|
| 8132 | 8220 | ); |
|
| 8133 | 8221 | let base = *env; |
|
| 8134 | 8222 | let mut thenEnv = base; |
|
| 8135 | 8223 | let bindingsStart = thenEnv.len; |
|
| 8136 | 8224 | let loanStart = checker.loanLen; |
| 8160 | 8248 | if env.terminated { |
|
| 8161 | 8249 | return; |
|
| 8162 | 8250 | } |
|
| 8163 | 8251 | match node.value { |
|
| 8164 | 8252 | case ast::NodeValue::Ident(_) => { |
|
| 8253 | + | if usage <> LinearUse::Place { |
|
| 8254 | + | try checkLinearIdent(checker, env, node); |
|
| 8255 | + | } |
|
| 8165 | 8256 | if usage == LinearUse::Consume { |
|
| 8166 | - | if let ty = typeFor(&mut *checker.resolver, node); isExclusiveArgument(ty) { |
|
| 8257 | + | if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) { |
|
| 8167 | 8258 | try checkPatternLoan(checker, node); |
|
| 8168 | 8259 | } |
|
| 8169 | 8260 | try consumeLinearIdent(checker, env, node); |
|
| 8170 | 8261 | } |
|
| 8171 | 8262 | } |
|
| 8172 | 8263 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 8173 | - | if let exprTy = typeFor(&mut *checker.resolver, expr) { |
|
| 8264 | + | if let exprTy = typeFor(checker.resolver, expr) { |
|
| 8174 | 8265 | if isLinear(exprTy) { |
|
| 8175 | - | throw emitError(&mut *checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 8266 | + | throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 8176 | 8267 | } |
|
| 8177 | 8268 | } |
|
| 8178 | 8269 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
|
| 8179 | 8270 | } |
|
| 8180 | 8271 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
| 8182 | 8273 | let mut isUndefined = false; |
|
| 8183 | 8274 | if let case ast::NodeValue::Undef = binding.value.value { |
|
| 8184 | 8275 | set isUndefined = true; |
|
| 8185 | 8276 | } |
|
| 8186 | 8277 | if isUndefined { |
|
| 8187 | - | if let bindingTy = typeFor(&mut *checker.resolver, binding.ident); |
|
| 8278 | + | if let bindingTy = typeFor(checker.resolver, binding.ident); |
|
| 8188 | 8279 | isLinear(bindingTy) |
|
| 8189 | 8280 | { |
|
| 8190 | 8281 | throw emitError( |
|
| 8191 | - | &mut *checker.resolver, |
|
| 8282 | + | checker.resolver, |
|
| 8192 | 8283 | binding.value, |
|
| 8193 | 8284 | ErrorKind::LinearUndefined, |
|
| 8194 | 8285 | ); |
|
| 8195 | 8286 | } |
|
| 8196 | 8287 | } |
|
| 8197 | 8288 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
|
| 8198 | 8289 | try addLinearBinding(checker, env, node); |
|
| 8199 | 8290 | if isUndefined { |
|
| 8200 | - | markLinearBindingUnavailable(&mut *checker.resolver, env, node); |
|
| 8291 | + | markLinearBindingUnavailable(checker.resolver, env, node); |
|
| 8201 | 8292 | } |
|
| 8202 | 8293 | } |
|
| 8203 | 8294 | case ast::NodeValue::Assign(assign) => { |
|
| 8204 | 8295 | try checkPatternLoan(checker, assign.left); |
|
| 8205 | 8296 | let mut target: ?u32 = nil; |
|
| 8206 | 8297 | let mut targetLinear = false; |
|
| 8207 | - | if let leftTy = typeFor(&mut *checker.resolver, assign.left) { |
|
| 8298 | + | if let leftTy = typeFor(checker.resolver, assign.left) { |
|
| 8208 | 8299 | if isMoveOnly(leftTy) { |
|
| 8209 | 8300 | set targetLinear = isLinear(leftTy); |
|
| 8210 | 8301 | if let case ast::NodeValue::Ident(_) = assign.left.value { |
|
| 8211 | - | if let sym = symbolFor(&mut *checker.resolver, assign.left) { |
|
| 8302 | + | if let sym = symbolFor(checker.resolver, assign.left) { |
|
| 8212 | 8303 | set target = findLinearBinding(env, sym); |
|
| 8213 | 8304 | } |
|
| 8214 | 8305 | } |
|
| 8215 | - | if target == nil { |
|
| 8306 | + | if targetLinear and target == nil { |
|
| 8216 | 8307 | throw emitError( |
|
| 8217 | - | &mut *checker.resolver, |
|
| 8308 | + | checker.resolver, |
|
| 8218 | 8309 | assign.left, |
|
| 8219 | 8310 | ErrorKind::LinearOverwrite, |
|
| 8220 | 8311 | ); |
|
| 8221 | 8312 | } |
|
| 8222 | 8313 | } |
| 8224 | 8315 | try checkLinearNode(checker, env, assign.left, LinearUse::Place); |
|
| 8225 | 8316 | try checkLinearNode(checker, env, assign.right, LinearUse::Consume); |
|
| 8226 | 8317 | if let index = target { |
|
| 8227 | 8318 | if targetLinear and linearBindingAvailable(env, index) { |
|
| 8228 | 8319 | throw emitError( |
|
| 8229 | - | &mut *checker.resolver, |
|
| 8320 | + | checker.resolver, |
|
| 8230 | 8321 | assign.left, |
|
| 8231 | 8322 | ErrorKind::LinearOverwrite, |
|
| 8232 | 8323 | ); |
|
| 8233 | 8324 | } |
|
| 8234 | 8325 | set env.available |= (1 as u64) << (index as u64); |
| 8240 | 8331 | try checkPatternLoan(checker, addr.target); |
|
| 8241 | 8332 | } |
|
| 8242 | 8333 | try checkLinearNode(checker, env, addr.target, LinearUse::Borrow); |
|
| 8243 | 8334 | } |
|
| 8244 | 8335 | case ast::NodeValue::Deref(target) => { |
|
| 8245 | - | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8336 | + | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8246 | 8337 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8247 | - | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8338 | + | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8248 | 8339 | } |
|
| 8249 | 8340 | } |
|
| 8250 | 8341 | try checkLinearNode(checker, env, target, LinearUse::Observe); |
|
| 8251 | 8342 | } |
|
| 8252 | 8343 | case ast::NodeValue::FieldAccess(access) => { |
|
| 8253 | - | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8344 | + | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8254 | 8345 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8255 | - | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8346 | + | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8256 | 8347 | } |
|
| 8257 | 8348 | } |
|
| 8258 | 8349 | try checkLinearNode(checker, env, access.parent, LinearUse::Observe); |
|
| 8259 | 8350 | } |
|
| 8260 | 8351 | case ast::NodeValue::ScopeAccess(_) => {} |
|
| 8261 | 8352 | case ast::NodeValue::Subscript { container, index } => { |
|
| 8262 | - | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8353 | + | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8263 | 8354 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8264 | - | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8355 | + | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8265 | 8356 | } |
|
| 8266 | 8357 | } |
|
| 8267 | 8358 | try checkLinearNode(checker, env, container, LinearUse::Observe); |
|
| 8268 | 8359 | try checkLinearNode(checker, env, index, LinearUse::Consume); |
|
| 8269 | 8360 | } |
| 8278 | 8369 | for item in items { |
|
| 8279 | 8370 | try checkLinearNode(checker, env, item, LinearUse::Consume); |
|
| 8280 | 8371 | } |
|
| 8281 | 8372 | } |
|
| 8282 | 8373 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 8283 | - | if let itemTy = typeFor(&mut *checker.resolver, repeat.item) { |
|
| 8374 | + | if let itemTy = typeFor(checker.resolver, repeat.item) { |
|
| 8284 | 8375 | if not isCopy(itemTy) { |
|
| 8285 | 8376 | throw emitError( |
|
| 8286 | - | &mut *checker.resolver, |
|
| 8377 | + | checker.resolver, |
|
| 8287 | 8378 | repeat.item, |
|
| 8288 | 8379 | ErrorKind::LinearDiscard, |
|
| 8289 | 8380 | ); |
|
| 8290 | 8381 | } |
|
| 8291 | 8382 | } |
|
| 8292 | 8383 | try checkLinearNode(checker, env, repeat.item, LinearUse::Consume); |
|
| 8293 | 8384 | try checkLinearNode(checker, env, repeat.count, LinearUse::Consume); |
|
| 8294 | 8385 | } |
|
| 8295 | 8386 | case ast::NodeValue::BinOp(op) => { |
|
| 8296 | - | try checkLinearNode(checker, env, op.left, LinearUse::Consume); |
|
| 8297 | - | try checkLinearNode(checker, env, op.right, LinearUse::Consume); |
|
| 8387 | + | let mut operandUse = LinearUse::Consume; |
|
| 8388 | + | match op.op { |
|
| 8389 | + | case ast::BinaryOp::Eq, ast::BinaryOp::Ne, |
|
| 8390 | + | ast::BinaryOp::Lt, ast::BinaryOp::Gt, |
|
| 8391 | + | ast::BinaryOp::Lte, ast::BinaryOp::Gte => |
|
| 8392 | + | set operandUse = LinearUse::Observe, |
|
| 8393 | + | else => {} |
|
| 8394 | + | } |
|
| 8395 | + | try checkLinearNode(checker, env, op.left, operandUse); |
|
| 8396 | + | try checkLinearNode(checker, env, op.right, operandUse); |
|
| 8298 | 8397 | } |
|
| 8299 | 8398 | case ast::NodeValue::UnOp(op) => { |
|
| 8300 | 8399 | try checkLinearNode(checker, env, op.value, LinearUse::Consume); |
|
| 8301 | 8400 | } |
|
| 8302 | 8401 | case ast::NodeValue::As(expr) => { |
|
| 8303 | - | try checkLinearNode(checker, env, expr.value, LinearUse::Consume); |
|
| 8402 | + | let mut castUse = usage; |
|
| 8403 | + | if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) { |
|
| 8404 | + | set castUse = LinearUse::Observe; |
|
| 8405 | + | } |
|
| 8406 | + | try checkLinearNode(checker, env, expr.value, castUse); |
|
| 8304 | 8407 | } |
|
| 8305 | 8408 | case ast::NodeValue::Range(range) => { |
|
| 8306 | 8409 | if let start = range.start { |
|
| 8307 | 8410 | try checkLinearNode(checker, env, start, LinearUse::Consume); |
|
| 8308 | 8411 | } |
| 8323 | 8426 | } |
|
| 8324 | 8427 | case ast::NodeValue::IfLet(conditional) => { |
|
| 8325 | 8428 | try checkLinearIfLet(checker, env, node, conditional); |
|
| 8326 | 8429 | } |
|
| 8327 | 8430 | case ast::NodeValue::LetElse(binding) => { |
|
| 8328 | - | if let subjectTy = typeFor(&mut *checker.resolver, binding.pattern.scrutinee); |
|
| 8431 | + | if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee); |
|
| 8329 | 8432 | isLinear(subjectTy) |
|
| 8330 | 8433 | { |
|
| 8331 | 8434 | throw emitError( |
|
| 8332 | - | &mut *checker.resolver, |
|
| 8435 | + | checker.resolver, |
|
| 8333 | 8436 | binding.pattern.scrutinee, |
|
| 8334 | 8437 | ErrorKind::LinearPartialMove, |
|
| 8335 | 8438 | ); |
|
| 8336 | 8439 | } |
|
| 8337 | 8440 | try checkLinearNode( |
|
| 8338 | 8441 | checker, |
|
| 8339 | 8442 | env, |
|
| 8340 | 8443 | binding.pattern.scrutinee, |
|
| 8341 | - | LinearUse::Consume, |
|
| 8444 | + | patternSubjectUse(checker.resolver, binding.pattern.scrutinee), |
|
| 8342 | 8445 | ); |
|
| 8343 | 8446 | let base = *env; |
|
| 8344 | 8447 | let mut guardedEnv = base; |
|
| 8345 | 8448 | if let guard = binding.pattern.guard { |
|
| 8346 | 8449 | try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume); |
| 8424 | 8527 | ); |
|
| 8425 | 8528 | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8426 | 8529 | } |
|
| 8427 | 8530 | } |
|
| 8428 | 8531 | case ast::NodeValue::WhileLet(whileStmt) => { |
|
| 8429 | - | if let subjectTy = typeFor(&mut *checker.resolver, whileStmt.pattern.scrutinee); |
|
| 8532 | + | if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee); |
|
| 8430 | 8533 | isLinear(subjectTy) |
|
| 8431 | 8534 | { |
|
| 8432 | 8535 | throw emitError( |
|
| 8433 | - | &mut *checker.resolver, |
|
| 8536 | + | checker.resolver, |
|
| 8434 | 8537 | whileStmt.pattern.scrutinee, |
|
| 8435 | 8538 | ErrorKind::LinearPartialMove, |
|
| 8436 | 8539 | ); |
|
| 8437 | 8540 | } |
|
| 8438 | 8541 | let base = *env; |
| 8440 | 8543 | let mut bodyEnv = base; |
|
| 8441 | 8544 | try checkLinearNode( |
|
| 8442 | 8545 | checker, |
|
| 8443 | 8546 | &mut bodyEnv, |
|
| 8444 | 8547 | whileStmt.pattern.scrutinee, |
|
| 8445 | - | LinearUse::Consume, |
|
| 8548 | + | patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee), |
|
| 8446 | 8549 | ); |
|
| 8447 | 8550 | let mut conditionExit = bodyEnv; |
|
| 8448 | 8551 | let start = bodyEnv.len; |
|
| 8449 | 8552 | let loanStart = checker.loanLen; |
|
| 8450 | 8553 | if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) { |
| 8480 | 8583 | ); |
|
| 8481 | 8584 | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8482 | 8585 | } |
|
| 8483 | 8586 | } |
|
| 8484 | 8587 | case ast::NodeValue::For(forStmt) => { |
|
| 8485 | - | if let iterableTy = typeFor(&mut *checker.resolver, forStmt.iterable) { |
|
| 8588 | + | if let iterableTy = typeFor(checker.resolver, forStmt.iterable) { |
|
| 8486 | 8589 | if isLinear(iterableTy) { |
|
| 8487 | 8590 | throw emitError( |
|
| 8488 | - | &mut *checker.resolver, |
|
| 8591 | + | checker.resolver, |
|
| 8489 | 8592 | forStmt.iterable, |
|
| 8490 | 8593 | ErrorKind::LinearPartialMove, |
|
| 8491 | 8594 | ); |
|
| 8492 | 8595 | } |
|
| 8493 | 8596 | } |
| 8576 | 8679 | ||
| 8577 | 8680 | /// Check exact-use ownership for one resolved function. |
|
| 8578 | 8681 | unsafe fn checkLinearFn( |
|
| 8579 | 8682 | self: &mut Resolver, |
|
| 8580 | 8683 | receiver: ?*ast::Node, |
|
| 8581 | - | params: *mut [*ast::Node], |
|
| 8684 | + | params: *[*ast::Node], |
|
| 8582 | 8685 | body: *ast::Node, |
|
| 8583 | 8686 | ) throws (ResolveError) { |
|
| 8584 | 8687 | let mut checker = LinearChecker { |
|
| 8585 | 8688 | resolver: self as *unsafe mut Resolver, |
|
| 8586 | 8689 | loans: undefined, |
| 8639 | 8742 | let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst); |
|
| 8640 | 8743 | if not success(&diags) { |
|
| 8641 | 8744 | return diags; |
|
| 8642 | 8745 | } |
|
| 8643 | 8746 | } |
|
| 8644 | - | return Diagnostics { errors: self.errors }; |
|
| 8747 | + | return diagnostics(self); |
|
| 8645 | 8748 | } |
|
| 8646 | 8749 | ||
| 8647 | 8750 | /// Resolve a package. |
|
| 8648 | 8751 | unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 8649 | 8752 | let rootId = rootEntry.id; |
| 8658 | 8761 | else panic "resolvePackage: expected block for module root"; |
|
| 8659 | 8762 | ||
| 8660 | 8763 | // Module graph analysis phase: bind all module name symbols and scopes. |
|
| 8661 | 8764 | try resolveModuleGraph(self, &block) catch { |
|
| 8662 | 8765 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 8663 | - | return Diagnostics { errors: self.errors }; |
|
| 8766 | + | return diagnostics(self); |
|
| 8664 | 8767 | }; |
|
| 8665 | 8768 | ||
| 8666 | 8769 | // Declaration phase: bind all names and analyze top-level declarations. |
|
| 8667 | 8770 | try resolveModuleDecls(self, &block) catch { |
|
| 8668 | 8771 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 8669 | 8772 | }; |
|
| 8670 | 8773 | if self.errors.len > 0 { |
|
| 8671 | - | return Diagnostics { errors: self.errors }; |
|
| 8774 | + | return diagnostics(self); |
|
| 8672 | 8775 | } |
|
| 8673 | 8776 | ||
| 8674 | 8777 | // Definition phase: analyze function bodies and sub-module definitions. |
|
| 8675 | 8778 | try resolveModuleDefs(self, &block) catch { |
|
| 8676 | 8779 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
|
| 8677 | 8780 | }; |
|
| 8678 | 8781 | setNodeType(self, node, Type::Void); |
|
| 8679 | 8782 | ||
| 8680 | - | return Diagnostics { errors: self.errors }; |
|
| 8783 | + | return diagnostics(self); |
|
| 8681 | 8784 | } |
lib/std/lang/resolver/printer.rad
+10 -7
| 42 | 42 | io::print("mut "); |
|
| 43 | 43 | } |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Print a resolved type in a textual form. |
|
| 47 | - | fn printType(ty: super::Type) { |
|
| 47 | + | unsafe fn printType(ty: super::Type) { |
|
| 48 | 48 | printTypeBody(ty, false); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Print just the type name without detailed structure info. |
|
| 52 | - | fn printTypeName(ty: super::Type) { |
|
| 52 | + | unsafe fn printTypeName(ty: super::Type) { |
|
| 53 | 53 | printTypeBody(ty, true); |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Print a resolved type, optionally abbreviated for function signatures. |
|
| 57 | - | fn printTypeBody(ty: super::Type, brief: bool) { |
|
| 57 | + | unsafe fn printTypeBody(ty: super::Type, brief: bool) { |
|
| 58 | 58 | match ty { |
|
| 59 | 59 | case super::Type::Unknown => { |
|
| 60 | 60 | io::print("<unknown>"); |
|
| 61 | 61 | } |
|
| 62 | 62 | case super::Type::Undefined => { |
| 172 | 172 | } |
|
| 173 | 173 | } |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Print detailed information about a nominal type (record or union). |
|
| 177 | - | fn printNominalType(info: *super::NominalType) { |
|
| 177 | + | unsafe fn printNominalType(info: *unsafe super::NominalType) { |
|
| 178 | 178 | match *info { |
|
| 179 | 179 | case super::NominalType::Placeholder(_) => { |
|
| 180 | 180 | io::print("<placeholder>"); |
|
| 181 | 181 | } |
|
| 182 | 182 | case super::NominalType::Record(recordType) => { |
| 227 | 227 | } |
|
| 228 | 228 | } |
|
| 229 | 229 | } |
|
| 230 | 230 | ||
| 231 | 231 | /// Print just the type kind for a nominal type, without detailed info. |
|
| 232 | - | fn printNominalTypeName(info: *super::NominalType) { |
|
| 232 | + | unsafe fn printNominalTypeName(info: *unsafe super::NominalType) { |
|
| 233 | 233 | match *info { |
|
| 234 | 234 | case super::NominalType::Placeholder(_) => { |
|
| 235 | 235 | io::print("<placeholder>"); |
|
| 236 | 236 | } |
|
| 237 | 237 | case super::NominalType::Record(_) => { |
| 242 | 242 | } |
|
| 243 | 243 | } |
|
| 244 | 244 | } |
|
| 245 | 245 | ||
| 246 | 246 | /// Print a single diagnostic entry. |
|
| 247 | - | unsafe fn printError(err: *super::Error, res: &super::Resolver) { |
|
| 247 | + | unsafe fn printError(err: &super::Error, res: &super::Resolver) { |
|
| 248 | 248 | if let node = err.node { |
|
| 249 | 249 | // Find the module containing this error. |
|
| 250 | - | if let moduleEntry = module::get(&*res.moduleGraph, err.moduleId) { |
|
| 250 | + | if let moduleEntry = module::get(res.moduleGraph, err.moduleId) { |
|
| 251 | 251 | // Get the source text if available. |
|
| 252 | 252 | if let source = moduleEntry.source { |
|
| 253 | 253 | // Convert offset to location. |
|
| 254 | 254 | if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) { |
|
| 255 | 255 | // Print: filename:line:col: error: message |
| 369 | 369 | io::print("expected numeric type"); |
|
| 370 | 370 | } |
|
| 371 | 371 | case super::ErrorKind::ExpectedPointer => { |
|
| 372 | 372 | io::print("expected pointer type"); |
|
| 373 | 373 | } |
|
| 374 | + | case super::ErrorKind::InvalidSliceAllocator => { |
|
| 375 | + | io::print("expected allocator with func callback and matching opaque ctx pointer"); |
|
| 376 | + | } |
|
| 374 | 377 | case super::ErrorKind::ExpectedRecord => { |
|
| 375 | 378 | io::print("expected record type"); |
|
| 376 | 379 | } |
|
| 377 | 380 | case super::ErrorKind::ExpectedIndexable => { |
|
| 378 | 381 | io::print("expected array or slice"); |
lib/std/lang/resolver/tests.rad
+138 -12
| 75 | 75 | strings::intern(&mut STRING_POOL, LITERALS[i]); |
|
| 76 | 76 | } |
|
| 77 | 77 | // TODO: Use local static for this. |
|
| 78 | 78 | // Reset the module graph for each test. |
|
| 79 | 79 | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
|
| 80 | - | set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut STRING_POOL, &mut MODULE_ARENA); |
|
| 80 | + | set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA); |
|
| 81 | 81 | let config = super::Config { buildTest: true }; |
|
| 82 | 82 | let res = super::resolver(testStorage(), config); |
|
| 83 | 83 | ||
| 84 | 84 | return res; |
|
| 85 | 85 | } |
| 88 | 88 | unsafe fn resolveStatements( |
|
| 89 | 89 | self: &mut super::Resolver, block: ast::Block, arena: &mut ast::NodeArena |
|
| 90 | 90 | ) -> TestResult throws (super::ResolveError) { |
|
| 91 | 91 | let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements); |
|
| 92 | 92 | let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch { |
|
| 93 | - | return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, root: module.modBody }; |
|
| 93 | + | return TestResult { diagnostics: super::diagnostics(self), root: module.modBody }; |
|
| 94 | 94 | }; |
|
| 95 | 95 | return TestResult { diagnostics, root: module.fnBody }; |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Parse and analyze an expression string for testing. |
| 112 | 112 | ||
| 113 | 113 | /// Parse and analyze a module string for testing. |
|
| 114 | 114 | /// Use this for code with `fn`, `record`, `union`, etc. at the top level. |
|
| 115 | 115 | unsafe fn resolveProgramStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 116 | 116 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 117 | - | let stmt = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 117 | + | let stmt: *ast::Node = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 118 | 118 | panic "resolveProgramStr: parsing failed"; |
|
| 119 | 119 | }; |
|
| 120 | 120 | let diagnostics = try super::resolveModuleRoot(self, stmt) catch { |
|
| 121 | 121 | throw testing::TestError::Failed; |
|
| 122 | 122 | }; |
| 171 | 171 | arena: &mut ast::NodeArena |
|
| 172 | 172 | ) -> u16 throws (testing::TestError) { |
|
| 173 | 173 | let filePath = "<test>"; |
|
| 174 | 174 | let mut modId: u16 = undefined; |
|
| 175 | 175 | if let parent = parentId { |
|
| 176 | - | set modId = try module::registerChild(graph, parent, name, filePath) catch { |
|
| 176 | + | set modId = try module::registerChild(graph, &mut STRING_POOL, parent, name, filePath) catch { |
|
| 177 | 177 | throw testing::TestError::Failed; |
|
| 178 | 178 | }; |
|
| 179 | 179 | } else { |
|
| 180 | - | set modId = try module::registerRootWithName(graph, 0, name, filePath) catch { |
|
| 180 | + | set modId = try module::registerRootWithName(graph, &mut STRING_POOL, 0, name, filePath) catch { |
|
| 181 | 181 | throw testing::TestError::Failed; |
|
| 182 | 182 | }; |
|
| 183 | 183 | } |
|
| 184 | 184 | let root = try parser::parse(scanner::SourceLoc::String, code, arena, &mut STRING_POOL) catch { |
|
| 185 | 185 | panic "registerModule: parsing failed"; |
| 205 | 205 | fn expectNoErrors(r: &TestResult) throws (testing::TestError) { |
|
| 206 | 206 | try testing::expect(super::success(&r.diagnostics)); |
|
| 207 | 207 | } |
|
| 208 | 208 | ||
| 209 | 209 | /// Extract the first error from a test result, failing if none exists. |
|
| 210 | - | fn expectError(result: &TestResult) -> *super::Error throws (testing::TestError) { |
|
| 210 | + | fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) { |
|
| 211 | 211 | let err = super::errorAt(&result.diagnostics.errors[..], 0) |
|
| 212 | 212 | else throw testing::TestError::Failed; |
|
| 213 | 213 | return err; |
|
| 214 | 214 | } |
|
| 215 | 215 |
| 313 | 313 | } |
|
| 314 | 314 | return *actual == expected; |
|
| 315 | 315 | } |
|
| 316 | 316 | ||
| 317 | 317 | /// Extract the first error and ensure it has the expected kind. |
|
| 318 | - | fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> *super::Error |
|
| 318 | + | fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error |
|
| 319 | 319 | throws (testing::TestError) |
|
| 320 | 320 | { |
|
| 321 | 321 | let err = try expectError(result); |
|
| 322 | 322 | try testing::expect(errorKindMatches(&err.kind, kind)); |
|
| 323 | 323 | return err; |
| 334 | 334 | throw testing::TestError::Failed; |
|
| 335 | 335 | } |
|
| 336 | 336 | } |
|
| 337 | 337 | ||
| 338 | 338 | /// Verify that an error represents a specific type mismatch. |
|
| 339 | - | fn expectTypeMismatch(err: &super::Error, expected: super::Type, actual: super::Type) |
|
| 339 | + | fn expectTypeMismatch(err: super::Error, expected: super::Type, actual: super::Type) |
|
| 340 | 340 | throws (testing::TestError) |
|
| 341 | 341 | { |
|
| 342 | 342 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 343 | 343 | else throw testing::TestError::Failed; |
|
| 344 | 344 | try testing::expect(mismatch.expected == expected); |
| 374 | 374 | } |
|
| 375 | 375 | return body.statements[index]; |
|
| 376 | 376 | } |
|
| 377 | 377 | ||
| 378 | 378 | /// Retrieve a function body block by function name from the program scope. |
|
| 379 | - | fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 379 | + | unsafe fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 380 | 380 | throws (testing::TestError) |
|
| 381 | 381 | { |
|
| 382 | 382 | let scope = super::scopeFor(a, root) |
|
| 383 | 383 | else throw testing::TestError::Failed; |
|
| 384 | 384 | let sym = super::findSymbolInScope(scope, name) |
| 398 | 398 | return blk; |
|
| 399 | 399 | } |
|
| 400 | 400 | ||
| 401 | 401 | /// Get the payload type of a union variant, if it has one. |
|
| 402 | 402 | /// For single-field unlabeled variants like `Variant(i32)`, unwraps to return the inner type. |
|
| 403 | - | fn getUnionVariantPayload(nominalTy: *super::NominalType, variantName: *[u8]) -> super::Type { |
|
| 403 | + | unsafe fn getUnionVariantPayload(nominalTy: *unsafe super::NominalType, variantName: *[u8]) -> super::Type { |
|
| 404 | 404 | let case super::NominalType::Union(unionType) = *nominalTy |
|
| 405 | 405 | else panic "getUnionVariantPayload: not a union"; |
|
| 406 | 406 | for i in 0..unionType.variants.len { |
|
| 407 | 407 | if mem::eq(unionType.variants[i].name, variantName) { |
|
| 408 | 408 | let payloadType = unionType.variants[i].valueType; |
| 417 | 417 | } |
|
| 418 | 418 | panic "getUnionVariantPayload: variant not found"; |
|
| 419 | 419 | } |
|
| 420 | 420 | ||
| 421 | 421 | /// Get a nominal type by name, in the scope of the given block node. |
|
| 422 | - | fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *super::NominalType |
|
| 422 | + | unsafe fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType |
|
| 423 | 423 | throws (testing::TestError) |
|
| 424 | 424 | { |
|
| 425 | 425 | let scope = super::scopeFor(a, blk) |
|
| 426 | 426 | else throw testing::TestError::Failed; |
|
| 427 | 427 | let sym = super::findSymbolInScope(scope, name) |
| 4922 | 4922 | } |
|
| 4923 | 4923 | ||
| 4924 | 4924 | /// Both mutable and immutable methods on a mutable trait object should work. |
|
| 4925 | 4925 | @test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
|
| 4926 | 4926 | let mut a = testResolver(); |
|
| 4927 | - | let program = "record Counter { value: i32 } trait Ops { fn (*mut Ops) inc(); fn (*Ops) get() -> i32; } instance Ops for Counter { fn (c: *mut Counter) inc() { set c.value = c.value + 1; } fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
|
| 4927 | + | let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
|
| 4928 | 4928 | let result = try resolveProgramStr(&mut a, program); |
|
| 4929 | 4929 | try expectNoErrors(&result); |
|
| 4930 | 4930 | } |
|
| 4931 | 4931 | ||
| 4932 | 4932 | /// Instance method body type must match the trait return type. |
| 6033 | 6033 | /// Unsafe contexts permit pattern access through raw pointers. |
|
| 6034 | 6034 | @test unsafe fn testRawPointerPatternsAllowed() throws (testing::TestError) { |
|
| 6035 | 6035 | try expectAnalyzeOk("union U: Copy { A(u8), B } unsafe fn run(p: *unsafe U) { match p { case U::A(n) => { *n; } else => {} } if let case U::A(n) = p { *n; } while let case U::A(n) = p { *n; break; } }"); |
|
| 6036 | 6036 | try expectAnalyzeOk("record R: Copy { n: u8 } unsafe fn run(p: *unsafe R) { let case R { n } = p else return; }"); |
|
| 6037 | 6037 | } |
|
| 6038 | + | ||
| 6039 | + | /// Slice append requires the allocator layout and callback signature. |
|
| 6040 | + | @test unsafe fn testSliceAppendAllocatorRejected() throws (testing::TestError) { |
|
| 6041 | + | let programs = &[ |
|
| 6042 | + | "fn run(s: *mut [u8]) { s.append(1, 0); }", |
|
| 6043 | + | "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6044 | + | "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6045 | + | "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6046 | + | "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6047 | + | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6048 | + | "union E: Copy { Bad } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6049 | + | ]; |
|
| 6050 | + | for program in programs { |
|
| 6051 | + | let mut a = testResolver(); |
|
| 6052 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6053 | + | try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator); |
|
| 6054 | + | } |
|
| 6055 | + | } |
|
| 6056 | + | ||
| 6057 | + | /// Slice allocators can use raw context pointers. |
|
| 6058 | + | @test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) { |
|
| 6059 | + | try expectAnalyzeOk("record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }"); |
|
| 6060 | + | } |
|
| 6061 | + | ||
| 6062 | + | /// A live pattern reference protects its payload from writes through copied pointers. |
|
| 6063 | + | @test unsafe fn testPatternPayloadPointerAliasesRejected() throws (testing::TestError) { |
|
| 6064 | + | let programs = &[ |
|
| 6065 | + | "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; match p { case U::A(n) => { set *q = U::B; *n; } else => {} } }", |
|
| 6066 | + | "union U: Copy { A(*u8), B(u64) } fn run(p: *mut U) -> u8 { let q = p; match p { case U::A(n) => { set *q = U::B(1); return **n; } else => return 0, } }", |
|
| 6067 | + | "union U: Copy { A(u8), B } fn change(p: &mut U) { set *p = U::B; } fn run(p: *mut U) { let q = p; if let case U::A(n) = p { change(q); *n; } }", |
|
| 6068 | + | "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; let r = q; while let case U::A(n) = p { set *r = U::B; *n; break; } }", |
|
| 6069 | + | ]; |
|
| 6070 | + | for program in programs { |
|
| 6071 | + | let mut a = testResolver(); |
|
| 6072 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6073 | + | let _ = try expectError(&result); |
|
| 6074 | + | } |
|
| 6075 | + | } |
|
| 6076 | + | ||
| 6077 | + | /// Every access to a moved safe mutable pointer is rejected. |
|
| 6078 | + | @test unsafe fn testMutablePointerMovesRejected() throws (testing::TestError) { |
|
| 6079 | + | let programs = &[ |
|
| 6080 | + | "fn run(p: *mut u8) { let q = p; *p; }", |
|
| 6081 | + | "fn run(p: *mut u8) { let q = p; set *p = 1; }", |
|
| 6082 | + | "fn run(p: *mut u8) { let q = p; let r = &*p; }", |
|
| 6083 | + | "fn read(p: &u8) {} fn run(p: *mut u8) { let q = p; read(p); }", |
|
| 6084 | + | "fn run(p: &mut u8) { let q = p; *p; }", |
|
| 6085 | + | "fn run(p: *mut [u8]) { let q = p; p.len; }", |
|
| 6086 | + | "fn run(p: &mut [u8]) { let q = p; p[0]; }", |
|
| 6087 | + | "record R { value: u8 } fn run(p: *mut R) { let q = p; p.value; }", |
|
| 6088 | + | "trait T { fn (&T) read(); } fn run(p: *mut opaque T) { let q = p; p.read(); }", |
|
| 6089 | + | "fn run(p: *mut u8, flag: bool) { if flag { let q = p; } *p; }", |
|
| 6090 | + | "fn run(p: *mut u8) { loop { let q = p; } }", |
|
| 6091 | + | ]; |
|
| 6092 | + | for program in programs { |
|
| 6093 | + | let mut a = testResolver(); |
|
| 6094 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6095 | + | let _ = try expectError(&result); |
|
| 6096 | + | } |
|
| 6097 | + | } |
|
| 6098 | + | ||
| 6099 | + | /// Copy composites cannot contain safe mutable owners or their containers. |
|
| 6100 | + | @test unsafe fn testCopyMutablePointerFieldsRejected() throws (testing::TestError) { |
|
| 6101 | + | let programs = &[ |
|
| 6102 | + | "record R: Copy { p: *mut u8 }", |
|
| 6103 | + | "union U: Copy { A(*mut u8), B }", |
|
| 6104 | + | "record R: Copy { p: ?*mut u8 }", |
|
| 6105 | + | "record R: Copy { p: [*mut u8; 2] }", |
|
| 6106 | + | "record R: Copy { p: *mut [u8] }", |
|
| 6107 | + | "trait T {} record R: Copy { p: *mut opaque T }", |
|
| 6108 | + | "record Inner { p: *mut u8 } record Outer: Copy { inner: Inner }", |
|
| 6109 | + | ]; |
|
| 6110 | + | for program in programs { |
|
| 6111 | + | let mut a = testResolver(); |
|
| 6112 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6113 | + | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 6114 | + | } |
|
| 6115 | + | } |
|
| 6116 | + | ||
| 6117 | + | /// Moves transfer safe mutable pointers, and calls can borrow them temporarily. |
|
| 6118 | + | @test unsafe fn testMutablePointerMovesAllowed() throws (testing::TestError) { |
|
| 6119 | + | try expectAnalyzeOk("fn run(p: *mut u8) -> u8 { let q = p; set *q = 1; return *q; }"); |
|
| 6120 | + | try expectAnalyzeOk("fn setValue(p: &mut u8) { set *p = 1; } fn run(p: *mut u8) { setValue(p); setValue(p); *p; }"); |
|
| 6121 | + | try expectAnalyzeOk("fn run(p: *u8) { let q = p; *p; *q; }"); |
|
| 6122 | + | try expectAnalyzeOk("fn run(p: *mut u8, q: *mut u8) { unsafe { p == q; p as u64; } set *p = 1; set *q = 2; }"); |
|
| 6123 | + | try expectAnalyzeOk("record R: Copy { p: *unsafe mut u8 } unsafe fn run(p: *unsafe mut u8) { let q = p; set *p = 1; set *q = 2; }"); |
|
| 6124 | + | try expectAnalyzeOk("record R: Copy { p: *unsafe mut [u8] } unsafe fn run(p: *unsafe mut [u8]) { let q = p; set p[0] = 1; set q[0] = 2; }"); |
|
| 6125 | + | } |
|
| 6126 | + | ||
| 6127 | + | /// Function signature inspection uses immutable descriptors in safe code. |
|
| 6128 | + | fn expectImmutableFunctionSignatures(res: &super::Resolver, root: *ast::Node) throws (testing::TestError) { |
|
| 6129 | + | let first = try typeOf(res, try getBlockStmt(root, 0)); |
|
| 6130 | + | let equivalent = try typeOf(res, try getBlockStmt(root, 1)); |
|
| 6131 | + | let throwing = try typeOf(res, try getBlockStmt(root, 2)); |
|
| 6132 | + | let unsafeCall = try typeOf(res, try getBlockStmt(root, 3)); |
|
| 6133 | + | try testing::expect(super::typesEqual(first, equivalent)); |
|
| 6134 | + | try testing::expect(not super::typesEqual(first, throwing)); |
|
| 6135 | + | try testing::expect(not super::typesEqual(first, unsafeCall)); |
|
| 6136 | + | } |
|
| 6137 | + | ||
| 6138 | + | /// Local analysis state does not change function signature identity. |
|
| 6139 | + | @test unsafe fn testImmutableFunctionSignatures() throws (testing::TestError) { |
|
| 6140 | + | let mut res = testResolver(); |
|
| 6141 | + | let result = try resolveProgramStr(&mut res, |
|
| 6142 | + | "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }"); |
|
| 6143 | + | try expectNoErrors(&result); |
|
| 6144 | + | try expectImmutableFunctionSignatures(&res, result.root); |
|
| 6145 | + | } |
|
| 6146 | + | ||
| 6147 | + | /// A diagnostic snapshot keeps its contents when the resolver buffer changes. |
|
| 6148 | + | @test unsafe fn testDiagnosticSnapshotOwnsItsErrors() throws (testing::TestError) { |
|
| 6149 | + | let mut res = testResolver(); |
|
| 6150 | + | let result = try resolveProgramStr(&mut res, "fn run() { missing; }"); |
|
| 6151 | + | let snapshot = result.diagnostics; |
|
| 6152 | + | let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 6153 | + | set res.errors.entries[0] = super::Error { |
|
| 6154 | + | kind: super::ErrorKind::Internal, node: nil, moduleId: 0, |
|
| 6155 | + | }; |
|
| 6156 | + | let later = super::diagnostics(&mut res); |
|
| 6157 | + | try testing::expect(snapshot.errors.len == 1); |
|
| 6158 | + | try testing::expect(later.errors.len == 1); |
|
| 6159 | + | let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 6160 | + | try testing::expect(first.kind == original.kind); |
|
| 6161 | + | try testing::expect(later.errors[0].kind == super::ErrorKind::Internal); |
|
| 6162 | + | try testing::expect(super::errorAt(snapshot.errors, 1) == nil); |
|
| 6163 | + | } |
lib/std/lang/scanner.rad
+6 -6
| 190 | 190 | source: *[u8], |
|
| 191 | 191 | /// Offset of current token into buffer. |
|
| 192 | 192 | token: u32, |
|
| 193 | 193 | /// Offset of current character being scanned. |
|
| 194 | 194 | cursor: u32, |
|
| 195 | - | /// Interned string pool. |
|
| 196 | - | pool: *mut strings::Pool, |
|
| 195 | + | /// Interned string pool. It must remain valid while scanning. |
|
| 196 | + | pool: *unsafe mut strings::Pool, |
|
| 197 | 197 | } |
|
| 198 | 198 | ||
| 199 | 199 | /// Individual token with kind, source text, and position. |
|
| 200 | 200 | /// |
|
| 201 | 201 | /// Represents a single lexical element extracted from source, |
| 220 | 220 | /// Column number. |
|
| 221 | 221 | col: u16, |
|
| 222 | 222 | } |
|
| 223 | 223 | ||
| 224 | 224 | /// Create a new scanner object. |
|
| 225 | - | export fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *mut strings::Pool) -> Scanner { |
|
| 225 | + | export unsafe fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *unsafe mut strings::Pool) -> Scanner { |
|
| 226 | 226 | // Intern built-in functions and attributes. |
|
| 227 | 227 | strings::intern(pool, "@sizeOf"); |
|
| 228 | 228 | strings::intern(pool, "@alignOf"); |
|
| 229 | 229 | strings::intern(pool, "@sliceOf"); |
|
| 230 | 230 | strings::intern(pool, "@default"); |
| 399 | 399 | } |
|
| 400 | 400 | return TokenKind::Ident; |
|
| 401 | 401 | } |
|
| 402 | 402 | ||
| 403 | 403 | /// Scan an identifier, keyword, or label. |
|
| 404 | - | fn scanIdentifier(s: &mut Scanner) -> Token { |
|
| 404 | + | unsafe fn scanIdentifier(s: &mut Scanner) -> Token { |
|
| 405 | 405 | while let ch = current(s); char::isAlpha(ch) or ch == '_' or char::isDigit(ch) { |
|
| 406 | 406 | advance(s); |
|
| 407 | 407 | } |
|
| 408 | 408 | let ident = &s.source[s.token..s.cursor]; |
|
| 409 | 409 | let kind = keywordOrIdent(ident); |
| 413 | 413 | return Token { kind, source: strings::intern(s.pool, ident), offset: s.token }; |
|
| 414 | 414 | } |
|
| 415 | 415 | return tok(s, kind); |
|
| 416 | 416 | } |
|
| 417 | 417 | ||
| 418 | - | /// Scan the next token. |
|
| 419 | - | export fn next(s: &mut Scanner) -> Token { |
|
| 418 | + | /// Scan the next token. The retained string pool must be valid and writable. |
|
| 419 | + | export unsafe fn next(s: &mut Scanner) -> Token { |
|
| 420 | 420 | skipWhitespace(s); // Skip any whitespace between tokens. |
|
| 421 | 421 | set s.token = s.cursor; // Token starts at current position. |
|
| 422 | 422 | ||
| 423 | 423 | if isEof(s) { |
|
| 424 | 424 | return tok(s, TokenKind::Eof); |
lib/std/lang/scanner/tests.rad
+20 -20
| 10 | 10 | unsafe { |
|
| 11 | 11 | return super::scanner(super::SourceLoc::File("test.r"), source, &mut TEST_STRING_POOL); |
|
| 12 | 12 | } |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | @test fn testScanTokens() throws (testing::TestError) { |
|
| 15 | + | @test unsafe fn testScanTokens() throws (testing::TestError) { |
|
| 16 | 16 | let mut s = testScanner( |
|
| 17 | 17 | "'x' < fnord fnord: 0 => >> mod and nil" |
|
| 18 | 18 | ); |
|
| 19 | 19 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Char); |
|
| 20 | 20 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Lt); |
| 28 | 28 | try testing::expect(super::next(&mut s).kind == super::TokenKind::And); |
|
| 29 | 29 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Nil); |
|
| 30 | 30 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | @test fn testScanChars() throws (testing::TestError) { |
|
| 33 | + | @test unsafe fn testScanChars() throws (testing::TestError) { |
|
| 34 | 34 | let mut s = testScanner( |
|
| 35 | 35 | "'\\0'" |
|
| 36 | 36 | ); |
|
| 37 | 37 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Char); |
|
| 38 | 38 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | - | @test fn testScanWhitespace() throws (testing::TestError) { |
|
| 41 | + | @test unsafe fn testScanWhitespace() throws (testing::TestError) { |
|
| 42 | 42 | let mut s = testScanner( |
|
| 43 | 43 | " X\n\nY //\n Z//1 \n" |
|
| 44 | 44 | ); |
|
| 45 | 45 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 46 | 46 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 47 | 47 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 48 | 48 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | @test fn testScanIdentsAndKeywords() throws (testing::TestError) { |
|
| 51 | + | @test unsafe fn testScanIdentsAndKeywords() throws (testing::TestError) { |
|
| 52 | 52 | let mut s = testScanner( |
|
| 53 | 53 | "m mo mod modo" |
|
| 54 | 54 | ); |
|
| 55 | 55 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 56 | 56 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 57 | 57 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Mod); |
|
| 58 | 58 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 59 | 59 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | - | @test fn testScanIdentifiers() throws (testing::TestError) { |
|
| 62 | + | @test unsafe fn testScanIdentifiers() throws (testing::TestError) { |
|
| 63 | 63 | let mut s = testScanner( |
|
| 64 | 64 | "fnord::yikes no1 4j" |
|
| 65 | 65 | ); |
|
| 66 | 66 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 67 | 67 | try testing::expect(super::next(&mut s).kind == super::TokenKind::ColonColon); |
| 70 | 70 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Number); |
|
| 71 | 71 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 72 | 72 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | @test fn testScanAtIdentifiers() throws (testing::TestError) { |
|
| 75 | + | @test unsafe fn testScanAtIdentifiers() throws (testing::TestError) { |
|
| 76 | 76 | let mut s = testScanner( |
|
| 77 | 77 | "@default @" |
|
| 78 | 78 | ); |
|
| 79 | 79 | try testing::expect(super::next(&mut s).kind == super::TokenKind::AtIdent); |
|
| 80 | 80 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Invalid); |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | - | @test fn testScanStrings() throws (testing::TestError) { |
|
| 83 | + | @test unsafe fn testScanStrings() throws (testing::TestError) { |
|
| 84 | 84 | let mut s = testScanner( |
|
| 85 | 85 | "\"Hello World!\" \"\\\"\"" |
|
| 86 | 86 | ); |
|
| 87 | 87 | let mut t: super::Token = super::next(&mut s); |
|
| 88 | 88 | try testing::expect(t.kind == super::TokenKind::String); |
| 93 | 93 | try testing::expect(t.kind == super::TokenKind::String); |
|
| 94 | 94 | try testing::expect(t.source.len == 4); |
|
| 95 | 95 | try testing::expect(mem::eq(t.source, "\"\\\"\"")); |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | - | @test fn testGetLocation() throws (testing::TestError) { |
|
| 98 | + | @test unsafe fn testGetLocation() throws (testing::TestError) { |
|
| 99 | 99 | let mut s = testScanner( |
|
| 100 | 100 | "abc\n def\n ghi" |
|
| 101 | 101 | ); |
|
| 102 | 102 | let mut tok: super::Token = super::next(&mut s); |
|
| 103 | 103 | try testing::expect(tok.kind == super::TokenKind::Ident); |
| 129 | 129 | } else { |
|
| 130 | 130 | try testing::expect(false); |
|
| 131 | 131 | } |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | - | @test fn testScanEmptyInput() throws (testing::TestError) { |
|
| 134 | + | @test unsafe fn testScanEmptyInput() throws (testing::TestError) { |
|
| 135 | 135 | let mut s = testScanner(""); |
|
| 136 | 136 | let tok: super::Token = super::next(&mut s); |
|
| 137 | 137 | ||
| 138 | 138 | try testing::expect(tok.kind == super::TokenKind::Eof); |
|
| 139 | 139 | try testing::expect(tok.source.len == 0); |
|
| 140 | 140 | try testing::expect(tok.offset == 0); |
|
| 141 | 141 | } |
|
| 142 | 142 | ||
| 143 | - | @test fn testScanSingleCharTokens() throws (testing::TestError) { |
|
| 143 | + | @test unsafe fn testScanSingleCharTokens() throws (testing::TestError) { |
|
| 144 | 144 | let mut s = testScanner( |
|
| 145 | 145 | "(){}[] ,;+-*~|&" |
|
| 146 | 146 | ); |
|
| 147 | 147 | try testing::expect(super::next(&mut s).kind == super::TokenKind::LParen); |
|
| 148 | 148 | try testing::expect(super::next(&mut s).kind == super::TokenKind::RParen); |
| 159 | 159 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Pipe); |
|
| 160 | 160 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Amp); |
|
| 161 | 161 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 162 | 162 | } |
|
| 163 | 163 | ||
| 164 | - | @test fn testScanDoubleCharTokens() throws (testing::TestError) { |
|
| 164 | + | @test unsafe fn testScanDoubleCharTokens() throws (testing::TestError) { |
|
| 165 | 165 | let mut s = testScanner( |
|
| 166 | 166 | "== <> <= >= < > >> << -> =>" |
|
| 167 | 167 | ); |
|
| 168 | 168 | try testing::expect(super::next(&mut s).kind == super::TokenKind::EqualEqual); |
|
| 169 | 169 | try testing::expect(super::next(&mut s).kind == super::TokenKind::LtGt); |
| 176 | 176 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Arrow); |
|
| 177 | 177 | try testing::expect(super::next(&mut s).kind == super::TokenKind::FatArrow); |
|
| 178 | 178 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 179 | 179 | } |
|
| 180 | 180 | ||
| 181 | - | @test fn testScanIdentifierLengths() throws (testing::TestError) { |
|
| 181 | + | @test unsafe fn testScanIdentifierLengths() throws (testing::TestError) { |
|
| 182 | 182 | let mut s = testScanner( |
|
| 183 | 183 | "sho xyz981 lal_lel" |
|
| 184 | 184 | ); |
|
| 185 | 185 | let mut tok: super::Token = super::next(&mut s); |
|
| 186 | 186 | try testing::expect(tok.kind == super::TokenKind::Ident); |
| 193 | 193 | set tok = super::next(&mut s); |
|
| 194 | 194 | try testing::expect(tok.kind == super::TokenKind::Ident); |
|
| 195 | 195 | try testing::expect(tok.source.len == 7); |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | - | @test fn testScanNumbers() throws (testing::TestError) { |
|
| 198 | + | @test unsafe fn testScanNumbers() throws (testing::TestError) { |
|
| 199 | 199 | let mut s = testScanner( |
|
| 200 | 200 | "2 -2 +2 value-2 value+2 value--2 value+-2 value-+2 f(-2)" |
|
| 201 | 201 | ); |
|
| 202 | 202 | let expected: [super::TokenKind; 29] = [ |
|
| 203 | 203 | super::TokenKind::Number, |
| 218 | 218 | for expectedKind in expected { |
|
| 219 | 219 | try testing::expect(super::next(&mut s).kind == expectedKind); |
|
| 220 | 220 | } |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | - | @test fn testScanKeywords() throws (testing::TestError) { |
|
| 223 | + | @test unsafe fn testScanKeywords() throws (testing::TestError) { |
|
| 224 | 224 | let mut s = testScanner("nil mod not static unsafe"); |
|
| 225 | 225 | let tok1: super::Token = super::next(&mut s); |
|
| 226 | 226 | ||
| 227 | 227 | try testing::expect(tok1.kind == super::TokenKind::Nil); |
|
| 228 | 228 | try testing::expect(tok1.source.len == 3); |
| 242 | 242 | let tok5: super::Token = super::next(&mut s); |
|
| 243 | 243 | try testing::expect(tok5.kind == super::TokenKind::Unsafe); |
|
| 244 | 244 | try testing::expect(tok5.source.len == 6); |
|
| 245 | 245 | } |
|
| 246 | 246 | ||
| 247 | - | @test fn testScanVoidAsIdent() throws (testing::TestError) { |
|
| 247 | + | @test unsafe fn testScanVoidAsIdent() throws (testing::TestError) { |
|
| 248 | 248 | let mut s = testScanner("void"); |
|
| 249 | 249 | let tok: super::Token = super::next(&mut s); |
|
| 250 | 250 | try testing::expect(tok.kind == super::TokenKind::Ident); |
|
| 251 | 251 | try testing::expect(tok.source.len == 4); |
|
| 252 | 252 | } |
|
| 253 | 253 | ||
| 254 | - | @test fn testScanWhitespaceAndComments() throws (testing::TestError) { |
|
| 254 | + | @test unsafe fn testScanWhitespaceAndComments() throws (testing::TestError) { |
|
| 255 | 255 | let mut s = testScanner( |
|
| 256 | 256 | " \t\n // This is a comment..\n 42" |
|
| 257 | 257 | ); |
|
| 258 | 258 | let tok: super::Token = super::next(&mut s); |
|
| 259 | 259 | try testing::expect(tok.kind == super::TokenKind::Number); |
|
| 260 | 260 | try testing::expect(tok.source.len == 2); |
|
| 261 | 261 | try testing::expect(super::isEof(&s)); |
|
| 262 | 262 | } |
|
| 263 | 263 | ||
| 264 | - | @test fn testScanInvalidCharacters() throws (testing::TestError) { |
|
| 264 | + | @test unsafe fn testScanInvalidCharacters() throws (testing::TestError) { |
|
| 265 | 265 | let mut s = testScanner( |
|
| 266 | 266 | "`#$" |
|
| 267 | 267 | ); |
|
| 268 | 268 | let mut tok: super::Token = super::next(&mut s); |
|
| 269 | 269 | try testing::expect(tok.kind == super::TokenKind::Invalid); |
| 276 | 276 | set tok = super::next(&mut s); |
|
| 277 | 277 | try testing::expect(tok.kind == super::TokenKind::Invalid); |
|
| 278 | 278 | try testing::expect(tok.offset == 2); |
|
| 279 | 279 | } |
|
| 280 | 280 | ||
| 281 | - | @test fn testScanUnterminatedString() throws (testing::TestError) { |
|
| 281 | + | @test unsafe fn testScanUnterminatedString() throws (testing::TestError) { |
|
| 282 | 282 | let mut s = testScanner("\"Hello World!"); |
|
| 283 | 283 | let tok: super::Token = super::next(&mut s); |
|
| 284 | 284 | ||
| 285 | 285 | try testing::expect(tok.kind == super::TokenKind::Invalid); |
|
| 286 | 286 | try testing::expect(tok.source == "unterminated string"); |
|
| 287 | 287 | try testing::expect(tok.offset == 0); |
|
| 288 | 288 | } |
|
| 289 | 289 | ||
| 290 | - | @test fn testScanUnderminatedStringEscape() throws (testing::TestError) { |
|
| 290 | + | @test unsafe fn testScanUnderminatedStringEscape() throws (testing::TestError) { |
|
| 291 | 291 | let mut s = testScanner("\"\\"); |
|
| 292 | 292 | let tok: super::Token = super::next(&mut s); |
|
| 293 | 293 | ||
| 294 | 294 | try testing::expect(tok.kind == super::TokenKind::Invalid); |
|
| 295 | 295 | } |
|
| 296 | 296 | ||
| 297 | - | @test fn testScanFunctionDefinition() throws (testing::TestError) { |
|
| 297 | + | @test unsafe fn testScanFunctionDefinition() throws (testing::TestError) { |
|
| 298 | 298 | let mut s = testScanner( |
|
| 299 | 299 | "fn add(a: i32, b: i32) -> i32 { return a + b; }" |
|
| 300 | 300 | ); |
|
| 301 | 301 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Fn); |
|
| 302 | 302 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
lib/std/lang/sexpr.rad
+1 -1
| 9 | 9 | /// Output target for S-expression printing. |
|
| 10 | 10 | export union Output: Copy { |
|
| 11 | 11 | /// Print to stdout. |
|
| 12 | 12 | Stdout, |
|
| 13 | 13 | /// Write to a buffer, tracking position. |
|
| 14 | - | Buffer { buf: *mut [u8], pos: *unsafe mut u32 }, |
|
| 14 | + | Buffer { buf: *unsafe mut [u8], pos: *unsafe mut u32 }, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// An S-expression element. |
|
| 18 | 18 | export union Expr: Copy { |
|
| 19 | 19 | /// An empty expression. |
lib/std/lang/strings.rad
+3 -3
| 30 | 30 | /// Not found, contains empty slot index. |
|
| 31 | 31 | Empty(u32), |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Look up a string, returning either the found entry or the empty slot index. |
|
| 35 | - | fn lookup(sp: *Pool, str: *[u8]) -> Lookup { |
|
| 35 | + | fn lookup(sp: &Pool, str: *[u8]) -> Lookup { |
|
| 36 | 36 | let mut idx = dict::hash(str) & (TABLE_SIZE - 1); |
|
| 37 | 37 | ||
| 38 | 38 | loop { |
|
| 39 | 39 | let entry = sp.table[idx]; |
|
| 40 | 40 | if entry.len > 0 { |
| 48 | 48 | } |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Look up a string in the pool. |
|
| 52 | 52 | /// Returns the canonical interned pointer if the string exists. |
|
| 53 | - | export fn find(sp: *Pool, str: *[u8]) -> ?*[u8] { |
|
| 53 | + | export fn find(sp: &Pool, str: *[u8]) -> ?*[u8] { |
|
| 54 | 54 | match lookup(sp, str) { |
|
| 55 | 55 | case Lookup::Found(entry) => return entry, |
|
| 56 | 56 | case Lookup::Empty(_) => return nil, |
|
| 57 | 57 | } |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Intern a string, returning a canonical pointer for equality comparison. |
|
| 61 | 61 | /// |
|
| 62 | 62 | /// If the string content already exists in the pool, returns the existing pointer. |
|
| 63 | 63 | /// Otherwise, adds the string pointer to the pool and returns it. |
|
| 64 | - | export fn intern(sp: *mut Pool, str: *[u8]) -> *[u8] { |
|
| 64 | + | export fn intern(sp: &mut Pool, str: *[u8]) -> *[u8] { |
|
| 65 | 65 | match lookup(sp, str) { |
|
| 66 | 66 | case Lookup::Found(entry) => return entry, |
|
| 67 | 67 | case Lookup::Empty(idx) => { |
|
| 68 | 68 | assert sp.count < TABLE_SIZE / 2, "intern: string pool is full"; |
|
| 69 | 69 | set sp.table[idx] = str; |
test/runner.rad
+4 -4
| 171 | 171 | } |
|
| 172 | 172 | ||
| 173 | 173 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 174 | 174 | unsafe fn writeImage( |
|
| 175 | 175 | code: *[u32], |
|
| 176 | - | roData: *[u8], |
|
| 176 | + | roData: &[u8], |
|
| 177 | 177 | rwData: *[u8], |
|
| 178 | 178 | path: *[u8] |
|
| 179 | 179 | ) -> bool { |
|
| 180 | 180 | let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len); |
|
| 181 | 181 | let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32); |
|
| 182 | 182 | let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32); |
|
| 183 | 183 | ||
| 184 | 184 | let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
|
| 185 | 185 | if fd < 0 { return false; } |
|
| 186 | - | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes) |
|
| 186 | + | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, &codeBytes[..]) |
|
| 187 | 187 | and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData); |
|
| 188 | 188 | let closed = unix::close(fd) == 0; |
|
| 189 | 189 | return written and closed; |
|
| 190 | 190 | } |
|
| 191 | 191 |
| 212 | 212 | io::printError(sourcePath); |
|
| 213 | 213 | io::printError("\n"); |
|
| 214 | 214 | return false; |
|
| 215 | 215 | }; |
|
| 216 | 216 | ||
| 217 | - | if not writeImage(program.text, program.data, &[], outputPath) { |
|
| 217 | + | if not writeImage(program.text, &program.data[..], &[], outputPath) { |
|
| 218 | 218 | io::printError("error: could not write output: "); |
|
| 219 | 219 | io::printError(outputPath); |
|
| 220 | 220 | io::printError("\n"); |
|
| 221 | 221 | return false; |
|
| 222 | 222 | } |
| 253 | 253 | let expected = &EXPECTED_BUF[..expectedLen]; |
|
| 254 | 254 | let source = &SOURCE_BUF[..sourceLen]; |
|
| 255 | 255 | ||
| 256 | 256 | // Parse source. |
|
| 257 | 257 | let mut astArena = ast::nodeArena(&mut AST_ARENA_STORAGE[..]); |
|
| 258 | - | let root = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch { |
|
| 258 | + | let root: *ast::Node = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch { |
|
| 259 | 259 | io::printLn("error: parsing failed"); |
|
| 260 | 260 | return false; |
|
| 261 | 261 | }; |
|
| 262 | 262 | ||
| 263 | 263 | // Run resolver. |
test/tests/edge.cases.6.rad
+2 -2
| 22 | 22 | r: u32, |
|
| 23 | 23 | s: u32, |
|
| 24 | 24 | t: u32, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | record Analyzer: Copy { |
|
| 27 | + | record Analyzer { |
|
| 28 | 28 | pad0: u32, |
|
| 29 | 29 | pad1: u32, |
|
| 30 | 30 | entries: *mut [Entry], |
|
| 31 | 31 | len: u32, |
|
| 32 | 32 | } |
| 115 | 115 | ||
| 116 | 116 | @default unsafe fn main() -> i32 { |
|
| 117 | 117 | let target = &mut STORAGE[..]; |
|
| 118 | 118 | init(target); |
|
| 119 | 119 | add(); |
|
| 120 | - | return checkHeader(target); |
|
| 120 | + | return checkHeader(&STORAGE[..]); |
|
| 121 | 121 | } |
test/tests/edge.cases.7.addr.bug.rad
+1 -1
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | record PtrHolder: Copy { |
|
| 3 | + | record PtrHolder { |
|
| 4 | 4 | ptr: *mut i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | static target: i32 = 10; |
|
| 8 | 8 | unsafe static holder: PtrHolder = undefined; |
test/tests/pointer.arithmetic.unsafe.rad
+1 -1
| 3 | 3 | /// Backing storage for pointer offsets. |
|
| 4 | 4 | static DATA: [u8; 3] = [11, 22, 33]; |
|
| 5 | 5 | ||
| 6 | 6 | /// Read and write valid offsets in an unsafe function. |
|
| 7 | 7 | @default unsafe fn main() -> i32 { |
|
| 8 | - | let p = &mut DATA[0]; |
|
| 8 | + | let p: *unsafe mut u8 = &mut DATA[0]; |
|
| 9 | 9 | let q = p + 1; |
|
| 10 | 10 | if *q <> 22 { return 1; } |
|
| 11 | 11 | if *(1 + q) <> 33 { return 2; } |
|
| 12 | 12 | if *(q - 1) <> 11 { return 3; } |
|
| 13 | 13 | set *q = 44; |
test/tests/pointer.cast.unsafe.rad
+2 -2
| 6 | 6 | /// Reinterpret valid storage in an unsafe function. |
|
| 7 | 7 | @default unsafe fn main() -> i32 { |
|
| 8 | 8 | let bytes = &mut DATA[..] as *mut [u8]; |
|
| 9 | 9 | if bytes[0] <> 42 { return 1; } |
|
| 10 | 10 | set bytes[0] = 43; |
|
| 11 | - | let word = bytes.ptr as *u64; |
|
| 12 | - | if *word <> 43 { return 2; } |
|
| 13 | 11 | let words = bytes as *[u64]; |
|
| 12 | + | let word = words.ptr as *u64; |
|
| 13 | + | if *word <> 43 { return 2; } |
|
| 14 | 14 | if words[0] <> 43 { return 3; } |
|
| 15 | 15 | return 0; |
|
| 16 | 16 | } |
test/tests/pointer.slice.store.rad
+2 -2
| 7 | 7 | c: u32, |
|
| 8 | 8 | d: u32, |
|
| 9 | 9 | e: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Table: Copy { |
|
| 12 | + | record Table { |
|
| 13 | 13 | entries: *mut [Entry], |
|
| 14 | 14 | len: u32, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | record PtrBox: Copy { |
|
| 17 | + | record PtrBox { |
|
| 18 | 18 | ptr: *mut *mut [Entry], |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | unsafe static STORAGE: [Entry; 2] = undefined; |
|
| 22 | 22 | unsafe static TABLE: Table = undefined; |
test/tests/slice.append.rad
+9 -9
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice .append() method with inline allocator. |
|
| 3 | 3 | ||
| 4 | 4 | /// Simple bump allocator for testing. |
|
| 5 | - | record Arena: Copy { |
|
| 5 | + | record Arena { |
|
| 6 | 6 | data: *mut [u8], |
|
| 7 | 7 | offset: u32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | fn newArena(data: *mut [u8]) -> Arena { |
| 23 | 23 | return base as *mut opaque; |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Allocator record matching the compiler's expected layout. |
|
| 27 | 27 | record Allocator: Copy { |
|
| 28 | - | func: unsafe fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 29 | - | ctx: *mut opaque, |
|
| 28 | + | func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, |
|
| 29 | + | ctx: *unsafe mut opaque, |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | - | unsafe fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque { |
|
| 33 | - | let arena = ctx as *mut Arena; |
|
| 34 | - | return arenaAlloc(arena, size, al); |
|
| 32 | + | unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, al: u32) -> *mut opaque { |
|
| 33 | + | let arena = ctx as *unsafe mut Arena; |
|
| 34 | + | return arenaAlloc(&mut *arena, size, al); |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | - | fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 37 | + | unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator { |
|
| 38 | 38 | return Allocator { |
|
| 39 | 39 | func: arenaAllocFn, |
|
| 40 | - | ctx: arena as *mut opaque, |
|
| 40 | + | ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque, |
|
| 41 | 41 | }; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | unsafe static BUF: [u8; 4096] = undefined; |
|
| 45 | 45 |
| 104 | 104 | if nums[3] <> 40 { |
|
| 105 | 105 | return 12; |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | 108 | // Append from empty (cap == 0 triggers growth with cap = 1). |
|
| 109 | - | let mut empty = @sliceOf(ptr as *mut i32, 0, 0); |
|
| 109 | + | let mut empty: *mut [i32] = &mut []; |
|
| 110 | 110 | empty.append(99, a); |
|
| 111 | 111 | ||
| 112 | 112 | if empty.len <> 1 { |
|
| 113 | 113 | return 13; |
|
| 114 | 114 | } |
test/tests/slice.append.ril
+293 -293
| 341 | 341 | ebreak; |
|
| 342 | 342 | unreachable; |
|
| 343 | 343 | @then58 |
|
| 344 | 344 | ret 12; |
|
| 345 | 345 | @merge59 |
|
| 346 | - | reserve %167 16 8; |
|
| 347 | - | store w64 %10 %167 0; |
|
| 348 | - | store w32 0 %167 8; |
|
| 349 | - | store w32 0 %167 12; |
|
| 350 | - | load w32 %170 %167 8; |
|
| 351 | - | load w32 %171 %167 12; |
|
| 352 | - | br.ult w32 %170 %171 @append.store62 @append.grow63; |
|
| 346 | + | reserve %157 16 8; |
|
| 347 | + | store w64 0 %157 0; |
|
| 348 | + | store w32 0 %157 8; |
|
| 349 | + | store w32 0 %157 12; |
|
| 350 | + | load w32 %160 %157 8; |
|
| 351 | + | load w32 %161 %157 12; |
|
| 352 | + | br.ult w32 %160 %161 @append.store62 @append.grow63; |
|
| 353 | 353 | @guard#pass60 |
|
| 354 | 354 | load w64 %153 %11 0; |
|
| 355 | 355 | mul w64 %154 3 4; |
|
| 356 | 356 | add w64 %155 %153 %154; |
|
| 357 | 357 | sload w32 %156 %155 0; |
|
| 358 | 358 | br.ne w32 %156 40 @then58 @merge59; |
|
| 359 | 359 | @guard#trap61 |
|
| 360 | 360 | ebreak; |
|
| 361 | 361 | unreachable; |
|
| 362 | 362 | @append.store62 |
|
| 363 | - | load w64 %185 %167 0; |
|
| 364 | - | mul w64 %186 %170 4; |
|
| 365 | - | add w64 %187 %185 %186; |
|
| 366 | - | store w32 99 %187 0; |
|
| 367 | - | add w32 %188 %170 1; |
|
| 368 | - | store w32 %188 %167 8; |
|
| 369 | - | load w32 %191 %167 8; |
|
| 370 | - | br.ne w32 %191 1 @then67 @merge68; |
|
| 363 | + | load w64 %175 %157 0; |
|
| 364 | + | mul w64 %176 %160 4; |
|
| 365 | + | add w64 %177 %175 %176; |
|
| 366 | + | store w32 99 %177 0; |
|
| 367 | + | add w32 %178 %160 1; |
|
| 368 | + | store w32 %178 %157 8; |
|
| 369 | + | load w32 %181 %157 8; |
|
| 370 | + | br.ne w32 %181 1 @then67 @merge68; |
|
| 371 | 371 | @append.grow63 |
|
| 372 | - | shl w32 %172 %171 1; |
|
| 373 | - | or w32 %173 %172 1; |
|
| 374 | - | load w64 %174 %7 0; |
|
| 375 | - | load w64 %175 %7 8; |
|
| 376 | - | mul w32 %176 %173 4; |
|
| 377 | - | call w64 %177 %174(%175, %176, 4); |
|
| 378 | - | load w64 %178 %167 0; |
|
| 379 | - | mul w32 %179 %170 4; |
|
| 372 | + | shl w32 %162 %161 1; |
|
| 373 | + | or w32 %163 %162 1; |
|
| 374 | + | load w64 %164 %7 0; |
|
| 375 | + | load w64 %165 %7 8; |
|
| 376 | + | mul w32 %166 %163 4; |
|
| 377 | + | call w64 %167 %164(%165, %166, 4); |
|
| 378 | + | load w64 %168 %157 0; |
|
| 379 | + | mul w32 %169 %160 4; |
|
| 380 | 380 | jmp @append64(0); |
|
| 381 | - | @append64(w32 %180) |
|
| 382 | - | br.ult w32 %180 %179 @append65 @append66; |
|
| 381 | + | @append64(w32 %170) |
|
| 382 | + | br.ult w32 %170 %169 @append65 @append66; |
|
| 383 | 383 | @append65 |
|
| 384 | - | add w64 %181 %178 %180; |
|
| 385 | - | load w8 %182 %181 0; |
|
| 386 | - | add w64 %183 %177 %180; |
|
| 387 | - | store w8 %182 %183 0; |
|
| 388 | - | add w32 %184 %180 1; |
|
| 389 | - | jmp @append64(%184); |
|
| 384 | + | add w64 %171 %168 %170; |
|
| 385 | + | load w8 %172 %171 0; |
|
| 386 | + | add w64 %173 %167 %170; |
|
| 387 | + | store w8 %172 %173 0; |
|
| 388 | + | add w32 %174 %170 1; |
|
| 389 | + | jmp @append64(%174); |
|
| 390 | 390 | @append66 |
|
| 391 | - | store w64 %177 %167 0; |
|
| 392 | - | store w32 %173 %167 12; |
|
| 391 | + | store w64 %167 %157 0; |
|
| 392 | + | store w32 %163 %157 12; |
|
| 393 | 393 | jmp @append.store62; |
|
| 394 | 394 | @then67 |
|
| 395 | 395 | ret 13; |
|
| 396 | 396 | @merge68 |
|
| 397 | - | load w32 %192 %167 12; |
|
| 398 | - | br.ne w32 %192 1 @then69 @merge70; |
|
| 397 | + | load w32 %182 %157 12; |
|
| 398 | + | br.ne w32 %182 1 @then69 @merge70; |
|
| 399 | 399 | @then69 |
|
| 400 | 400 | ret 14; |
|
| 401 | 401 | @merge70 |
|
| 402 | - | load w32 %193 %167 8; |
|
| 403 | - | br.ult w32 0 %193 @guard#pass73 @guard#trap74; |
|
| 402 | + | load w32 %183 %157 8; |
|
| 403 | + | br.ult w32 0 %183 @guard#pass73 @guard#trap74; |
|
| 404 | 404 | @then71 |
|
| 405 | 405 | ret 15; |
|
| 406 | 406 | @merge72 |
|
| 407 | - | load w32 %198 %11 8; |
|
| 408 | - | load w64 %199 %11 0; |
|
| 407 | + | load w32 %188 %11 8; |
|
| 408 | + | load w64 %189 %11 0; |
|
| 409 | 409 | jmp @loop75(0, 0); |
|
| 410 | 410 | @guard#pass73 |
|
| 411 | - | load w64 %194 %167 0; |
|
| 412 | - | sload w32 %195 %194 0; |
|
| 413 | - | br.ne w32 %195 99 @then71 @merge72; |
|
| 411 | + | load w64 %184 %157 0; |
|
| 412 | + | sload w32 %185 %184 0; |
|
| 413 | + | br.ne w32 %185 99 @then71 @merge72; |
|
| 414 | 414 | @guard#trap74 |
|
| 415 | 415 | ebreak; |
|
| 416 | 416 | unreachable; |
|
| 417 | - | @loop75(w32 %200, w32 %204) |
|
| 418 | - | br.slt w32 %200 %198 @body76 @merge77; |
|
| 417 | + | @loop75(w32 %190, w32 %194) |
|
| 418 | + | br.slt w32 %190 %188 @body76 @merge77; |
|
| 419 | 419 | @body76 |
|
| 420 | - | mul w64 %201 %200 4; |
|
| 421 | - | add w64 %202 %199 %201; |
|
| 422 | - | sload w32 %203 %202 0; |
|
| 423 | - | add w32 %205 %204 %203; |
|
| 424 | - | add w32 %206 %200 1; |
|
| 425 | - | jmp @loop75(%206, %205); |
|
| 420 | + | mul w64 %191 %190 4; |
|
| 421 | + | add w64 %192 %189 %191; |
|
| 422 | + | sload w32 %193 %192 0; |
|
| 423 | + | add w32 %195 %194 %193; |
|
| 424 | + | add w32 %196 %190 1; |
|
| 425 | + | jmp @loop75(%196, %195); |
|
| 426 | 426 | @merge77 |
|
| 427 | - | br.ne w32 %204 150 @then78 @merge79; |
|
| 427 | + | br.ne w32 %194 150 @then78 @merge79; |
|
| 428 | 428 | @then78 |
|
| 429 | 429 | ret 16; |
|
| 430 | 430 | @merge79 |
|
| 431 | - | copy %207 $main$nominal$arena; |
|
| 432 | - | call w64 %208 $arenaAlloc(%207, 2, 1); |
|
| 433 | - | reserve %209 16 8; |
|
| 434 | - | store w64 %208 %209 0; |
|
| 435 | - | store w32 0 %209 8; |
|
| 436 | - | store w32 2 %209 12; |
|
| 437 | - | load w32 %213 %209 8; |
|
| 438 | - | load w32 %214 %209 12; |
|
| 439 | - | br.ult w32 %213 %214 @append.store80 @append.grow81; |
|
| 431 | + | copy %197 $main$nominal$arena; |
|
| 432 | + | call w64 %198 $arenaAlloc(%197, 2, 1); |
|
| 433 | + | reserve %199 16 8; |
|
| 434 | + | store w64 %198 %199 0; |
|
| 435 | + | store w32 0 %199 8; |
|
| 436 | + | store w32 2 %199 12; |
|
| 437 | + | load w32 %203 %199 8; |
|
| 438 | + | load w32 %204 %199 12; |
|
| 439 | + | br.ult w32 %203 %204 @append.store80 @append.grow81; |
|
| 440 | 440 | @append.store80 |
|
| 441 | - | load w64 %228 %209 0; |
|
| 442 | - | add w64 %229 %228 %213; |
|
| 443 | - | store w8 171 %229 0; |
|
| 444 | - | add w32 %230 %213 1; |
|
| 445 | - | store w32 %230 %209 8; |
|
| 446 | - | load w32 %235 %209 8; |
|
| 447 | - | load w32 %236 %209 12; |
|
| 448 | - | br.ult w32 %235 %236 @append.store85 @append.grow86; |
|
| 441 | + | load w64 %218 %199 0; |
|
| 442 | + | add w64 %219 %218 %203; |
|
| 443 | + | store w8 171 %219 0; |
|
| 444 | + | add w32 %220 %203 1; |
|
| 445 | + | store w32 %220 %199 8; |
|
| 446 | + | load w32 %225 %199 8; |
|
| 447 | + | load w32 %226 %199 12; |
|
| 448 | + | br.ult w32 %225 %226 @append.store85 @append.grow86; |
|
| 449 | 449 | @append.grow81 |
|
| 450 | - | shl w32 %215 %214 1; |
|
| 451 | - | or w32 %216 %215 1; |
|
| 452 | - | load w64 %217 %7 0; |
|
| 453 | - | load w64 %218 %7 8; |
|
| 454 | - | mul w32 %219 %216 1; |
|
| 455 | - | call w64 %220 %217(%218, %219, 1); |
|
| 456 | - | load w64 %221 %209 0; |
|
| 457 | - | mul w32 %222 %213 1; |
|
| 450 | + | shl w32 %205 %204 1; |
|
| 451 | + | or w32 %206 %205 1; |
|
| 452 | + | load w64 %207 %7 0; |
|
| 453 | + | load w64 %208 %7 8; |
|
| 454 | + | mul w32 %209 %206 1; |
|
| 455 | + | call w64 %210 %207(%208, %209, 1); |
|
| 456 | + | load w64 %211 %199 0; |
|
| 457 | + | mul w32 %212 %203 1; |
|
| 458 | 458 | jmp @append82(0); |
|
| 459 | - | @append82(w32 %223) |
|
| 460 | - | br.ult w32 %223 %222 @append83 @append84; |
|
| 459 | + | @append82(w32 %213) |
|
| 460 | + | br.ult w32 %213 %212 @append83 @append84; |
|
| 461 | 461 | @append83 |
|
| 462 | - | add w64 %224 %221 %223; |
|
| 463 | - | load w8 %225 %224 0; |
|
| 464 | - | add w64 %226 %220 %223; |
|
| 465 | - | store w8 %225 %226 0; |
|
| 466 | - | add w32 %227 %223 1; |
|
| 467 | - | jmp @append82(%227); |
|
| 462 | + | add w64 %214 %211 %213; |
|
| 463 | + | load w8 %215 %214 0; |
|
| 464 | + | add w64 %216 %210 %213; |
|
| 465 | + | store w8 %215 %216 0; |
|
| 466 | + | add w32 %217 %213 1; |
|
| 467 | + | jmp @append82(%217); |
|
| 468 | 468 | @append84 |
|
| 469 | - | store w64 %220 %209 0; |
|
| 470 | - | store w32 %216 %209 12; |
|
| 469 | + | store w64 %210 %199 0; |
|
| 470 | + | store w32 %206 %199 12; |
|
| 471 | 471 | jmp @append.store80; |
|
| 472 | 472 | @append.store85 |
|
| 473 | - | load w64 %250 %209 0; |
|
| 474 | - | add w64 %251 %250 %235; |
|
| 475 | - | store w8 205 %251 0; |
|
| 476 | - | add w32 %252 %235 1; |
|
| 477 | - | store w32 %252 %209 8; |
|
| 478 | - | load w32 %255 %209 8; |
|
| 479 | - | br.ne w32 %255 2 @then90 @merge91; |
|
| 473 | + | load w64 %240 %199 0; |
|
| 474 | + | add w64 %241 %240 %225; |
|
| 475 | + | store w8 205 %241 0; |
|
| 476 | + | add w32 %242 %225 1; |
|
| 477 | + | store w32 %242 %199 8; |
|
| 478 | + | load w32 %245 %199 8; |
|
| 479 | + | br.ne w32 %245 2 @then90 @merge91; |
|
| 480 | 480 | @append.grow86 |
|
| 481 | - | shl w32 %237 %236 1; |
|
| 482 | - | or w32 %238 %237 1; |
|
| 483 | - | load w64 %239 %7 0; |
|
| 484 | - | load w64 %240 %7 8; |
|
| 485 | - | mul w32 %241 %238 1; |
|
| 486 | - | call w64 %242 %239(%240, %241, 1); |
|
| 487 | - | load w64 %243 %209 0; |
|
| 488 | - | mul w32 %244 %235 1; |
|
| 481 | + | shl w32 %227 %226 1; |
|
| 482 | + | or w32 %228 %227 1; |
|
| 483 | + | load w64 %229 %7 0; |
|
| 484 | + | load w64 %230 %7 8; |
|
| 485 | + | mul w32 %231 %228 1; |
|
| 486 | + | call w64 %232 %229(%230, %231, 1); |
|
| 487 | + | load w64 %233 %199 0; |
|
| 488 | + | mul w32 %234 %225 1; |
|
| 489 | 489 | jmp @append87(0); |
|
| 490 | - | @append87(w32 %245) |
|
| 491 | - | br.ult w32 %245 %244 @append88 @append89; |
|
| 490 | + | @append87(w32 %235) |
|
| 491 | + | br.ult w32 %235 %234 @append88 @append89; |
|
| 492 | 492 | @append88 |
|
| 493 | - | add w64 %246 %243 %245; |
|
| 494 | - | load w8 %247 %246 0; |
|
| 495 | - | add w64 %248 %242 %245; |
|
| 496 | - | store w8 %247 %248 0; |
|
| 497 | - | add w32 %249 %245 1; |
|
| 498 | - | jmp @append87(%249); |
|
| 493 | + | add w64 %236 %233 %235; |
|
| 494 | + | load w8 %237 %236 0; |
|
| 495 | + | add w64 %238 %232 %235; |
|
| 496 | + | store w8 %237 %238 0; |
|
| 497 | + | add w32 %239 %235 1; |
|
| 498 | + | jmp @append87(%239); |
|
| 499 | 499 | @append89 |
|
| 500 | - | store w64 %242 %209 0; |
|
| 501 | - | store w32 %238 %209 12; |
|
| 500 | + | store w64 %232 %199 0; |
|
| 501 | + | store w32 %228 %199 12; |
|
| 502 | 502 | jmp @append.store85; |
|
| 503 | 503 | @then90 |
|
| 504 | 504 | ret 17; |
|
| 505 | 505 | @merge91 |
|
| 506 | - | load w32 %256 %209 8; |
|
| 507 | - | br.ult w32 0 %256 @guard#pass94 @guard#trap95; |
|
| 506 | + | load w32 %246 %199 8; |
|
| 507 | + | br.ult w32 0 %246 @guard#pass94 @guard#trap95; |
|
| 508 | 508 | @then92 |
|
| 509 | 509 | ret 18; |
|
| 510 | 510 | @merge93 |
|
| 511 | - | load w32 %259 %209 8; |
|
| 512 | - | br.ult w32 1 %259 @guard#pass98 @guard#trap99; |
|
| 511 | + | load w32 %249 %199 8; |
|
| 512 | + | br.ult w32 1 %249 @guard#pass98 @guard#trap99; |
|
| 513 | 513 | @guard#pass94 |
|
| 514 | - | load w64 %257 %209 0; |
|
| 515 | - | load w8 %258 %257 0; |
|
| 516 | - | br.ne w8 %258 171 @then92 @merge93; |
|
| 514 | + | load w64 %247 %199 0; |
|
| 515 | + | load w8 %248 %247 0; |
|
| 516 | + | br.ne w8 %248 171 @then92 @merge93; |
|
| 517 | 517 | @guard#trap95 |
|
| 518 | 518 | ebreak; |
|
| 519 | 519 | unreachable; |
|
| 520 | 520 | @then96 |
|
| 521 | 521 | ret 19; |
|
| 522 | 522 | @merge97 |
|
| 523 | - | load w32 %265 %209 8; |
|
| 524 | - | load w32 %266 %209 12; |
|
| 525 | - | br.ult w32 %265 %266 @append.store100 @append.grow101; |
|
| 523 | + | load w32 %255 %199 8; |
|
| 524 | + | load w32 %256 %199 12; |
|
| 525 | + | br.ult w32 %255 %256 @append.store100 @append.grow101; |
|
| 526 | 526 | @guard#pass98 |
|
| 527 | - | load w64 %260 %209 0; |
|
| 528 | - | add w64 %261 %260 1; |
|
| 529 | - | load w8 %262 %261 0; |
|
| 530 | - | br.ne w8 %262 205 @then96 @merge97; |
|
| 527 | + | load w64 %250 %199 0; |
|
| 528 | + | add w64 %251 %250 1; |
|
| 529 | + | load w8 %252 %251 0; |
|
| 530 | + | br.ne w8 %252 205 @then96 @merge97; |
|
| 531 | 531 | @guard#trap99 |
|
| 532 | 532 | ebreak; |
|
| 533 | 533 | unreachable; |
|
| 534 | 534 | @append.store100 |
|
| 535 | - | load w64 %280 %209 0; |
|
| 536 | - | add w64 %281 %280 %265; |
|
| 537 | - | store w8 239 %281 0; |
|
| 538 | - | add w32 %282 %265 1; |
|
| 539 | - | store w32 %282 %209 8; |
|
| 540 | - | load w32 %285 %209 8; |
|
| 541 | - | br.ne w32 %285 3 @then105 @merge106; |
|
| 535 | + | load w64 %270 %199 0; |
|
| 536 | + | add w64 %271 %270 %255; |
|
| 537 | + | store w8 239 %271 0; |
|
| 538 | + | add w32 %272 %255 1; |
|
| 539 | + | store w32 %272 %199 8; |
|
| 540 | + | load w32 %275 %199 8; |
|
| 541 | + | br.ne w32 %275 3 @then105 @merge106; |
|
| 542 | 542 | @append.grow101 |
|
| 543 | - | shl w32 %267 %266 1; |
|
| 544 | - | or w32 %268 %267 1; |
|
| 545 | - | load w64 %269 %7 0; |
|
| 546 | - | load w64 %270 %7 8; |
|
| 547 | - | mul w32 %271 %268 1; |
|
| 548 | - | call w64 %272 %269(%270, %271, 1); |
|
| 549 | - | load w64 %273 %209 0; |
|
| 550 | - | mul w32 %274 %265 1; |
|
| 543 | + | shl w32 %257 %256 1; |
|
| 544 | + | or w32 %258 %257 1; |
|
| 545 | + | load w64 %259 %7 0; |
|
| 546 | + | load w64 %260 %7 8; |
|
| 547 | + | mul w32 %261 %258 1; |
|
| 548 | + | call w64 %262 %259(%260, %261, 1); |
|
| 549 | + | load w64 %263 %199 0; |
|
| 550 | + | mul w32 %264 %255 1; |
|
| 551 | 551 | jmp @append102(0); |
|
| 552 | - | @append102(w32 %275) |
|
| 553 | - | br.ult w32 %275 %274 @append103 @append104; |
|
| 552 | + | @append102(w32 %265) |
|
| 553 | + | br.ult w32 %265 %264 @append103 @append104; |
|
| 554 | 554 | @append103 |
|
| 555 | - | add w64 %276 %273 %275; |
|
| 556 | - | load w8 %277 %276 0; |
|
| 557 | - | add w64 %278 %272 %275; |
|
| 558 | - | store w8 %277 %278 0; |
|
| 559 | - | add w32 %279 %275 1; |
|
| 560 | - | jmp @append102(%279); |
|
| 555 | + | add w64 %266 %263 %265; |
|
| 556 | + | load w8 %267 %266 0; |
|
| 557 | + | add w64 %268 %262 %265; |
|
| 558 | + | store w8 %267 %268 0; |
|
| 559 | + | add w32 %269 %265 1; |
|
| 560 | + | jmp @append102(%269); |
|
| 561 | 561 | @append104 |
|
| 562 | - | store w64 %272 %209 0; |
|
| 563 | - | store w32 %268 %209 12; |
|
| 562 | + | store w64 %262 %199 0; |
|
| 563 | + | store w32 %258 %199 12; |
|
| 564 | 564 | jmp @append.store100; |
|
| 565 | 565 | @then105 |
|
| 566 | 566 | ret 20; |
|
| 567 | 567 | @merge106 |
|
| 568 | - | load w32 %286 %209 12; |
|
| 569 | - | br.ne w32 %286 5 @then107 @merge108; |
|
| 568 | + | load w32 %276 %199 12; |
|
| 569 | + | br.ne w32 %276 5 @then107 @merge108; |
|
| 570 | 570 | @then107 |
|
| 571 | 571 | ret 21; |
|
| 572 | 572 | @merge108 |
|
| 573 | - | load w32 %287 %209 8; |
|
| 574 | - | br.ult w32 2 %287 @guard#pass111 @guard#trap112; |
|
| 573 | + | load w32 %277 %199 8; |
|
| 574 | + | br.ult w32 2 %277 @guard#pass111 @guard#trap112; |
|
| 575 | 575 | @then109 |
|
| 576 | 576 | ret 22; |
|
| 577 | 577 | @merge110 |
|
| 578 | - | load w32 %291 %209 8; |
|
| 579 | - | br.ult w32 0 %291 @guard#pass115 @guard#trap116; |
|
| 578 | + | load w32 %281 %199 8; |
|
| 579 | + | br.ult w32 0 %281 @guard#pass115 @guard#trap116; |
|
| 580 | 580 | @guard#pass111 |
|
| 581 | - | load w64 %288 %209 0; |
|
| 582 | - | add w64 %289 %288 2; |
|
| 583 | - | load w8 %290 %289 0; |
|
| 584 | - | br.ne w8 %290 239 @then109 @merge110; |
|
| 581 | + | load w64 %278 %199 0; |
|
| 582 | + | add w64 %279 %278 2; |
|
| 583 | + | load w8 %280 %279 0; |
|
| 584 | + | br.ne w8 %280 239 @then109 @merge110; |
|
| 585 | 585 | @guard#trap112 |
|
| 586 | 586 | ebreak; |
|
| 587 | 587 | unreachable; |
|
| 588 | 588 | @then113 |
|
| 589 | 589 | ret 23; |
|
| 590 | 590 | @merge114 |
|
| 591 | - | copy %294 $main$nominal$arena; |
|
| 592 | - | mul w32 %295 4 2; |
|
| 593 | - | call w64 %296 $arenaAlloc(%294, %295, 4); |
|
| 594 | - | reserve %297 16 8; |
|
| 595 | - | store w64 %296 %297 0; |
|
| 596 | - | store w32 0 %297 8; |
|
| 597 | - | store w32 2 %297 12; |
|
| 598 | - | load w32 %300 %297 8; |
|
| 599 | - | load w32 %301 %297 12; |
|
| 600 | - | br.ult w32 %300 %301 @append.store117 @append.grow118; |
|
| 591 | + | copy %284 $main$nominal$arena; |
|
| 592 | + | mul w32 %285 4 2; |
|
| 593 | + | call w64 %286 $arenaAlloc(%284, %285, 4); |
|
| 594 | + | reserve %287 16 8; |
|
| 595 | + | store w64 %286 %287 0; |
|
| 596 | + | store w32 0 %287 8; |
|
| 597 | + | store w32 2 %287 12; |
|
| 598 | + | load w32 %290 %287 8; |
|
| 599 | + | load w32 %291 %287 12; |
|
| 600 | + | br.ult w32 %290 %291 @append.store117 @append.grow118; |
|
| 601 | 601 | @guard#pass115 |
|
| 602 | - | load w64 %292 %209 0; |
|
| 603 | - | load w8 %293 %292 0; |
|
| 604 | - | br.ne w8 %293 171 @then113 @merge114; |
|
| 602 | + | load w64 %282 %199 0; |
|
| 603 | + | load w8 %283 %282 0; |
|
| 604 | + | br.ne w8 %283 171 @then113 @merge114; |
|
| 605 | 605 | @guard#trap116 |
|
| 606 | 606 | ebreak; |
|
| 607 | 607 | unreachable; |
|
| 608 | 608 | @append.store117 |
|
| 609 | - | load w64 %315 %297 0; |
|
| 610 | - | mul w64 %316 %300 4; |
|
| 611 | - | add w64 %317 %315 %316; |
|
| 612 | - | store w32 42 %317 0; |
|
| 613 | - | add w32 %318 %300 1; |
|
| 614 | - | store w32 %318 %297 8; |
|
| 615 | - | blit %297 %297 16; |
|
| 616 | - | load w32 %321 %297 8; |
|
| 617 | - | br.eq w32 %321 1 @assert.ok123 @assert.fail122; |
|
| 609 | + | load w64 %305 %287 0; |
|
| 610 | + | mul w64 %306 %290 4; |
|
| 611 | + | add w64 %307 %305 %306; |
|
| 612 | + | store w32 42 %307 0; |
|
| 613 | + | add w32 %308 %290 1; |
|
| 614 | + | store w32 %308 %287 8; |
|
| 615 | + | blit %287 %287 16; |
|
| 616 | + | load w32 %311 %287 8; |
|
| 617 | + | br.eq w32 %311 1 @assert.ok123 @assert.fail122; |
|
| 618 | 618 | @append.grow118 |
|
| 619 | - | shl w32 %302 %301 1; |
|
| 620 | - | or w32 %303 %302 1; |
|
| 621 | - | load w64 %304 %7 0; |
|
| 622 | - | load w64 %305 %7 8; |
|
| 623 | - | mul w32 %306 %303 4; |
|
| 624 | - | call w64 %307 %304(%305, %306, 4); |
|
| 625 | - | load w64 %308 %297 0; |
|
| 626 | - | mul w32 %309 %300 4; |
|
| 619 | + | shl w32 %292 %291 1; |
|
| 620 | + | or w32 %293 %292 1; |
|
| 621 | + | load w64 %294 %7 0; |
|
| 622 | + | load w64 %295 %7 8; |
|
| 623 | + | mul w32 %296 %293 4; |
|
| 624 | + | call w64 %297 %294(%295, %296, 4); |
|
| 625 | + | load w64 %298 %287 0; |
|
| 626 | + | mul w32 %299 %290 4; |
|
| 627 | 627 | jmp @append119(0); |
|
| 628 | - | @append119(w32 %310) |
|
| 629 | - | br.ult w32 %310 %309 @append120 @append121; |
|
| 628 | + | @append119(w32 %300) |
|
| 629 | + | br.ult w32 %300 %299 @append120 @append121; |
|
| 630 | 630 | @append120 |
|
| 631 | - | add w64 %311 %308 %310; |
|
| 632 | - | load w8 %312 %311 0; |
|
| 633 | - | add w64 %313 %307 %310; |
|
| 634 | - | store w8 %312 %313 0; |
|
| 635 | - | add w32 %314 %310 1; |
|
| 636 | - | jmp @append119(%314); |
|
| 631 | + | add w64 %301 %298 %300; |
|
| 632 | + | load w8 %302 %301 0; |
|
| 633 | + | add w64 %303 %297 %300; |
|
| 634 | + | store w8 %302 %303 0; |
|
| 635 | + | add w32 %304 %300 1; |
|
| 636 | + | jmp @append119(%304); |
|
| 637 | 637 | @append121 |
|
| 638 | - | store w64 %307 %297 0; |
|
| 639 | - | store w32 %303 %297 12; |
|
| 638 | + | store w64 %297 %287 0; |
|
| 639 | + | store w32 %293 %287 12; |
|
| 640 | 640 | jmp @append.store117; |
|
| 641 | 641 | @assert.fail122 |
|
| 642 | 642 | unreachable; |
|
| 643 | 643 | @assert.ok123 |
|
| 644 | - | load w32 %322 %297 8; |
|
| 645 | - | br.ult w32 0 %322 @guard#pass126 @guard#trap127; |
|
| 644 | + | load w32 %312 %287 8; |
|
| 645 | + | br.ult w32 0 %312 @guard#pass126 @guard#trap127; |
|
| 646 | 646 | @assert.fail124 |
|
| 647 | 647 | unreachable; |
|
| 648 | 648 | @assert.ok125 |
|
| 649 | - | load w32 %327 %297 8; |
|
| 650 | - | load w32 %328 %297 12; |
|
| 651 | - | br.ult w32 %327 %328 @append.store128 @append.grow129; |
|
| 649 | + | load w32 %317 %287 8; |
|
| 650 | + | load w32 %318 %287 12; |
|
| 651 | + | br.ult w32 %317 %318 @append.store128 @append.grow129; |
|
| 652 | 652 | @guard#pass126 |
|
| 653 | - | load w64 %323 %297 0; |
|
| 654 | - | sload w32 %324 %323 0; |
|
| 655 | - | br.eq w32 %324 42 @assert.ok125 @assert.fail124; |
|
| 653 | + | load w64 %313 %287 0; |
|
| 654 | + | sload w32 %314 %313 0; |
|
| 655 | + | br.eq w32 %314 42 @assert.ok125 @assert.fail124; |
|
| 656 | 656 | @guard#trap127 |
|
| 657 | 657 | ebreak; |
|
| 658 | 658 | unreachable; |
|
| 659 | 659 | @append.store128 |
|
| 660 | - | load w64 %342 %297 0; |
|
| 661 | - | mul w64 %343 %327 4; |
|
| 662 | - | add w64 %344 %342 %343; |
|
| 663 | - | store w32 43 %344 0; |
|
| 664 | - | add w32 %345 %327 1; |
|
| 665 | - | store w32 %345 %297 8; |
|
| 666 | - | blit %297 %297 16; |
|
| 667 | - | load w32 %348 %297 8; |
|
| 668 | - | br.eq w32 %348 2 @assert.ok134 @assert.fail133; |
|
| 660 | + | load w64 %332 %287 0; |
|
| 661 | + | mul w64 %333 %317 4; |
|
| 662 | + | add w64 %334 %332 %333; |
|
| 663 | + | store w32 43 %334 0; |
|
| 664 | + | add w32 %335 %317 1; |
|
| 665 | + | store w32 %335 %287 8; |
|
| 666 | + | blit %287 %287 16; |
|
| 667 | + | load w32 %338 %287 8; |
|
| 668 | + | br.eq w32 %338 2 @assert.ok134 @assert.fail133; |
|
| 669 | 669 | @append.grow129 |
|
| 670 | - | shl w32 %329 %328 1; |
|
| 671 | - | or w32 %330 %329 1; |
|
| 672 | - | load w64 %331 %7 0; |
|
| 673 | - | load w64 %332 %7 8; |
|
| 674 | - | mul w32 %333 %330 4; |
|
| 675 | - | call w64 %334 %331(%332, %333, 4); |
|
| 676 | - | load w64 %335 %297 0; |
|
| 677 | - | mul w32 %336 %327 4; |
|
| 670 | + | shl w32 %319 %318 1; |
|
| 671 | + | or w32 %320 %319 1; |
|
| 672 | + | load w64 %321 %7 0; |
|
| 673 | + | load w64 %322 %7 8; |
|
| 674 | + | mul w32 %323 %320 4; |
|
| 675 | + | call w64 %324 %321(%322, %323, 4); |
|
| 676 | + | load w64 %325 %287 0; |
|
| 677 | + | mul w32 %326 %317 4; |
|
| 678 | 678 | jmp @append130(0); |
|
| 679 | - | @append130(w32 %337) |
|
| 680 | - | br.ult w32 %337 %336 @append131 @append132; |
|
| 679 | + | @append130(w32 %327) |
|
| 680 | + | br.ult w32 %327 %326 @append131 @append132; |
|
| 681 | 681 | @append131 |
|
| 682 | - | add w64 %338 %335 %337; |
|
| 683 | - | load w8 %339 %338 0; |
|
| 684 | - | add w64 %340 %334 %337; |
|
| 685 | - | store w8 %339 %340 0; |
|
| 686 | - | add w32 %341 %337 1; |
|
| 687 | - | jmp @append130(%341); |
|
| 682 | + | add w64 %328 %325 %327; |
|
| 683 | + | load w8 %329 %328 0; |
|
| 684 | + | add w64 %330 %324 %327; |
|
| 685 | + | store w8 %329 %330 0; |
|
| 686 | + | add w32 %331 %327 1; |
|
| 687 | + | jmp @append130(%331); |
|
| 688 | 688 | @append132 |
|
| 689 | - | store w64 %334 %297 0; |
|
| 690 | - | store w32 %330 %297 12; |
|
| 689 | + | store w64 %324 %287 0; |
|
| 690 | + | store w32 %320 %287 12; |
|
| 691 | 691 | jmp @append.store128; |
|
| 692 | 692 | @assert.fail133 |
|
| 693 | 693 | unreachable; |
|
| 694 | 694 | @assert.ok134 |
|
| 695 | - | load w32 %349 %297 8; |
|
| 696 | - | br.ult w32 0 %349 @guard#pass135 @guard#trap136; |
|
| 695 | + | load w32 %339 %287 8; |
|
| 696 | + | br.ult w32 0 %339 @guard#pass135 @guard#trap136; |
|
| 697 | 697 | @guard#pass135 |
|
| 698 | - | load w64 %350 %297 0; |
|
| 699 | - | load w32 %353 %297 8; |
|
| 700 | - | load w32 %354 %297 12; |
|
| 701 | - | br.ult w32 %353 %354 @append.store137 @append.grow138; |
|
| 698 | + | load w64 %340 %287 0; |
|
| 699 | + | load w32 %343 %287 8; |
|
| 700 | + | load w32 %344 %287 12; |
|
| 701 | + | br.ult w32 %343 %344 @append.store137 @append.grow138; |
|
| 702 | 702 | @guard#trap136 |
|
| 703 | 703 | ebreak; |
|
| 704 | 704 | unreachable; |
|
| 705 | 705 | @append.store137 |
|
| 706 | - | load w64 %368 %297 0; |
|
| 707 | - | mul w64 %369 %353 4; |
|
| 708 | - | add w64 %370 %368 %369; |
|
| 709 | - | store w32 44 %370 0; |
|
| 710 | - | add w32 %371 %353 1; |
|
| 711 | - | store w32 %371 %297 8; |
|
| 712 | - | blit %297 %297 16; |
|
| 713 | - | load w32 %374 %297 8; |
|
| 714 | - | br.eq w32 %374 3 @assert.ok143 @assert.fail142; |
|
| 706 | + | load w64 %358 %287 0; |
|
| 707 | + | mul w64 %359 %343 4; |
|
| 708 | + | add w64 %360 %358 %359; |
|
| 709 | + | store w32 44 %360 0; |
|
| 710 | + | add w32 %361 %343 1; |
|
| 711 | + | store w32 %361 %287 8; |
|
| 712 | + | blit %287 %287 16; |
|
| 713 | + | load w32 %364 %287 8; |
|
| 714 | + | br.eq w32 %364 3 @assert.ok143 @assert.fail142; |
|
| 715 | 715 | @append.grow138 |
|
| 716 | - | shl w32 %355 %354 1; |
|
| 717 | - | or w32 %356 %355 1; |
|
| 718 | - | load w64 %357 %7 0; |
|
| 719 | - | load w64 %358 %7 8; |
|
| 720 | - | mul w32 %359 %356 4; |
|
| 721 | - | call w64 %360 %357(%358, %359, 4); |
|
| 722 | - | load w64 %361 %297 0; |
|
| 723 | - | mul w32 %362 %353 4; |
|
| 716 | + | shl w32 %345 %344 1; |
|
| 717 | + | or w32 %346 %345 1; |
|
| 718 | + | load w64 %347 %7 0; |
|
| 719 | + | load w64 %348 %7 8; |
|
| 720 | + | mul w32 %349 %346 4; |
|
| 721 | + | call w64 %350 %347(%348, %349, 4); |
|
| 722 | + | load w64 %351 %287 0; |
|
| 723 | + | mul w32 %352 %343 4; |
|
| 724 | 724 | jmp @append139(0); |
|
| 725 | - | @append139(w32 %363) |
|
| 726 | - | br.ult w32 %363 %362 @append140 @append141; |
|
| 725 | + | @append139(w32 %353) |
|
| 726 | + | br.ult w32 %353 %352 @append140 @append141; |
|
| 727 | 727 | @append140 |
|
| 728 | - | add w64 %364 %361 %363; |
|
| 729 | - | load w8 %365 %364 0; |
|
| 730 | - | add w64 %366 %360 %363; |
|
| 731 | - | store w8 %365 %366 0; |
|
| 732 | - | add w32 %367 %363 1; |
|
| 733 | - | jmp @append139(%367); |
|
| 728 | + | add w64 %354 %351 %353; |
|
| 729 | + | load w8 %355 %354 0; |
|
| 730 | + | add w64 %356 %350 %353; |
|
| 731 | + | store w8 %355 %356 0; |
|
| 732 | + | add w32 %357 %353 1; |
|
| 733 | + | jmp @append139(%357); |
|
| 734 | 734 | @append141 |
|
| 735 | - | store w64 %360 %297 0; |
|
| 736 | - | store w32 %356 %297 12; |
|
| 735 | + | store w64 %350 %287 0; |
|
| 736 | + | store w32 %346 %287 12; |
|
| 737 | 737 | jmp @append.store137; |
|
| 738 | 738 | @assert.fail142 |
|
| 739 | 739 | unreachable; |
|
| 740 | 740 | @assert.ok143 |
|
| 741 | - | load w32 %375 %297 12; |
|
| 742 | - | br.eq w32 %375 5 @assert.ok145 @assert.fail144; |
|
| 741 | + | load w32 %365 %287 12; |
|
| 742 | + | br.eq w32 %365 5 @assert.ok145 @assert.fail144; |
|
| 743 | 743 | @assert.fail144 |
|
| 744 | 744 | unreachable; |
|
| 745 | 745 | @assert.ok145 |
|
| 746 | - | load w32 %376 %297 8; |
|
| 747 | - | br.ult w32 0 %376 @guard#pass146 @guard#trap147; |
|
| 746 | + | load w32 %366 %287 8; |
|
| 747 | + | br.ult w32 0 %366 @guard#pass146 @guard#trap147; |
|
| 748 | 748 | @guard#pass146 |
|
| 749 | - | load w64 %377 %297 0; |
|
| 750 | - | br.ne w64 %350 %377 @assert.ok149 @assert.fail148; |
|
| 749 | + | load w64 %367 %287 0; |
|
| 750 | + | br.ne w64 %340 %367 @assert.ok149 @assert.fail148; |
|
| 751 | 751 | @guard#trap147 |
|
| 752 | 752 | ebreak; |
|
| 753 | 753 | unreachable; |
|
| 754 | 754 | @assert.fail148 |
|
| 755 | 755 | unreachable; |
|
| 756 | 756 | @assert.ok149 |
|
| 757 | - | load w32 %380 %297 8; |
|
| 758 | - | br.ult w32 0 %380 @guard#pass152 @guard#trap153; |
|
| 757 | + | load w32 %370 %287 8; |
|
| 758 | + | br.ult w32 0 %370 @guard#pass152 @guard#trap153; |
|
| 759 | 759 | @assert.fail150 |
|
| 760 | 760 | unreachable; |
|
| 761 | 761 | @assert.ok151 |
|
| 762 | - | load w32 %383 %297 8; |
|
| 763 | - | br.ult w32 1 %383 @guard#pass156 @guard#trap157; |
|
| 762 | + | load w32 %373 %287 8; |
|
| 763 | + | br.ult w32 1 %373 @guard#pass156 @guard#trap157; |
|
| 764 | 764 | @guard#pass152 |
|
| 765 | - | load w64 %381 %297 0; |
|
| 766 | - | sload w32 %382 %381 0; |
|
| 767 | - | br.eq w32 %382 42 @assert.ok151 @assert.fail150; |
|
| 765 | + | load w64 %371 %287 0; |
|
| 766 | + | sload w32 %372 %371 0; |
|
| 767 | + | br.eq w32 %372 42 @assert.ok151 @assert.fail150; |
|
| 768 | 768 | @guard#trap153 |
|
| 769 | 769 | ebreak; |
|
| 770 | 770 | unreachable; |
|
| 771 | 771 | @assert.fail154 |
|
| 772 | 772 | unreachable; |
|
| 773 | 773 | @assert.ok155 |
|
| 774 | - | load w32 %388 %297 8; |
|
| 775 | - | br.ult w32 2 %388 @guard#pass160 @guard#trap161; |
|
| 774 | + | load w32 %378 %287 8; |
|
| 775 | + | br.ult w32 2 %378 @guard#pass160 @guard#trap161; |
|
| 776 | 776 | @guard#pass156 |
|
| 777 | - | load w64 %384 %297 0; |
|
| 778 | - | mul w64 %385 1 4; |
|
| 779 | - | add w64 %386 %384 %385; |
|
| 780 | - | sload w32 %387 %386 0; |
|
| 781 | - | br.eq w32 %387 43 @assert.ok155 @assert.fail154; |
|
| 777 | + | load w64 %374 %287 0; |
|
| 778 | + | mul w64 %375 1 4; |
|
| 779 | + | add w64 %376 %374 %375; |
|
| 780 | + | sload w32 %377 %376 0; |
|
| 781 | + | br.eq w32 %377 43 @assert.ok155 @assert.fail154; |
|
| 782 | 782 | @guard#trap157 |
|
| 783 | 783 | ebreak; |
|
| 784 | 784 | unreachable; |
|
| 785 | 785 | @assert.fail158 |
|
| 786 | 786 | unreachable; |
|
| 787 | 787 | @assert.ok159 |
|
| 788 | 788 | ret 0; |
|
| 789 | 789 | @guard#pass160 |
|
| 790 | - | load w64 %389 %297 0; |
|
| 791 | - | mul w64 %390 2 4; |
|
| 792 | - | add w64 %391 %389 %390; |
|
| 793 | - | sload w32 %392 %391 0; |
|
| 794 | - | br.eq w32 %392 44 @assert.ok159 @assert.fail158; |
|
| 790 | + | load w64 %379 %287 0; |
|
| 791 | + | mul w64 %380 2 4; |
|
| 792 | + | add w64 %381 %379 %380; |
|
| 793 | + | sload w32 %382 %381 0; |
|
| 794 | + | br.eq w32 %382 44 @assert.ok159 @assert.fail158; |
|
| 795 | 795 | @guard#trap161 |
|
| 796 | 796 | ebreak; |
|
| 797 | 797 | unreachable; |
|
| 798 | 798 | } |
test/tests/static.slice.offset.rad
+1 -1
| 4 | 4 | record Entry: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Table: Copy { |
|
| 9 | + | record Table { |
|
| 10 | 10 | scratch: *mut [Entry], |
|
| 11 | 11 | entries: *mut [Entry], |
|
| 12 | 12 | len: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 |
test/tests/trait.dispatch.rad
+4 -4
| 1 | 1 | record Acc: Copy { |
|
| 2 | 2 | n: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Ops { |
|
| 6 | - | fn (*Ops) get() -> i32; |
|
| 7 | - | fn (*mut Ops) put(n: i32); |
|
| 6 | + | fn (&Ops) get() -> i32; |
|
| 7 | + | fn (&mut Ops) put(n: i32); |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | instance Ops for Acc { |
|
| 11 | - | fn (a: *Acc) get() -> i32 { |
|
| 11 | + | fn (a: &Acc) get() -> i32 { |
|
| 12 | 12 | return a.n; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn (a: *mut Acc) put(n: i32) { |
|
| 15 | + | fn (a: &mut Acc) put(n: i32) { |
|
| 16 | 16 | set a.n = n; |
|
| 17 | 17 | } |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | fn dispatch(o: *mut opaque Ops) -> i32 { |