module: (WIP) Associate updates with authority
413073d6c84f35132b0beba62a1a6121ee9d112e1b21c9990dcc42ffb541c512
Store module update payloads in permission-associated cells and keep immutable module identities separate from their mutable state. The module graph retains the external authority used by its update table. This checkpoint does not compile yet: shared update access still needs a matching authority binding derived from the graph-owned permission reference. Next, preserve that permission identity across graph access, pass the focused module and compiler checks, then reduce any remaining lifetime plumbing before running the full suite. Assisted-by: Codex:gpt-5.6-sol
1 parent
d1a3f60b
compiler/radiance.rad
+141 -127
| 193 | 193 | /// Whether to emit debug info (.debug file). |
|
| 194 | 194 | debug: bool, |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Compilation context. |
|
| 198 | - | record CompileContext { |
|
| 198 | + | record CompileContext: 'permission { |
|
| 199 | 199 | /// Array of packages to compile. |
|
| 200 | 200 | packages: [package::Package; MAX_PACKAGES], |
|
| 201 | 201 | /// Driver inputs for each package slot. |
|
| 202 | 202 | inputs: [PackageInput; MAX_PACKAGES], |
|
| 203 | 203 | /// Number of packages. |
|
| 204 | 204 | packageCount: u32, |
|
| 205 | 205 | /// Index of entry package. |
|
| 206 | 206 | entryPkgIdx: ?u32, |
|
| 207 | 207 | /// Global module graph shared by all packages. |
|
| 208 | - | graph: module::ModuleGraph, |
|
| 208 | + | graph: module::ModuleGraph 'permission, |
|
| 209 | 209 | /// Resolver configuration. |
|
| 210 | 210 | config: resolver::Config, |
|
| 211 | 211 | /// What to dump during compilation. |
|
| 212 | 212 | dump: Dump, |
|
| 213 | 213 | /// Output path for binary. |
| 291 | 291 | asmPathCount: 0, |
|
| 292 | 292 | }; |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | 295 | /// Register, load, and parse `path` within `pkg`. |
|
| 296 | - | unsafe fn processModule( |
|
| 296 | + | unsafe fn processModule 'permission ( |
|
| 297 | 297 | pkg: *unsafe mut package::Package, |
|
| 298 | - | graph: &mut module::ModuleGraph, |
|
| 298 | + | graph: &mut module::ModuleGraph 'permission, |
|
| 299 | 299 | path: *[u8], |
|
| 300 | 300 | nodeArena: &mut ast::NodeArena, |
|
| 301 | 301 | sourceArena: &mut alloc::Arena |
|
| 302 | 302 | ) throws (Error) { |
|
| 303 | 303 | pkgLog(pkg, &["parsing", "(", path, ")", ".."]); |
| 486 | 486 | debug: debugEnabled, |
|
| 487 | 487 | }; |
|
| 488 | 488 | } |
|
| 489 | 489 | ||
| 490 | 490 | /// Parse CLI arguments and return compilation context. |
|
| 491 | - | unsafe fn processCommand( |
|
| 491 | + | unsafe fn processCommand 'permission ( |
|
| 492 | 492 | args: *[*[u8]], |
|
| 493 | - | arena: &mut ast::NodeArena |
|
| 494 | - | ) -> CompileContext throws (Error) { |
|
| 493 | + | arena: &mut ast::NodeArena, |
|
| 494 | + | permission: &'permission mut module::Permission |
|
| 495 | + | ) -> CompileContext 'permission throws (Error) { |
|
| 495 | 496 | let mut inputs = [packageInput(""); MAX_PACKAGES]; |
|
| 496 | 497 | let command = try parseCommand(args, &mut inputs[..]); |
|
| 497 | - | let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena); |
|
| 498 | - | let mut ctx = CompileContext { |
|
| 498 | + | let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena, permission); |
|
| 499 | + | let mut ctx = CompileContext 'permission { |
|
| 499 | 500 | packages: undefined, |
|
| 500 | 501 | inputs, |
|
| 501 | 502 | packageCount: command.packageCount, |
|
| 502 | 503 | entryPkgIdx: command.entryPkgIdx, |
|
| 503 | 504 | graph, |
| 520 | 521 | } |
|
| 521 | 522 | return ctx; |
|
| 522 | 523 | } |
|
| 523 | 524 | ||
| 524 | 525 | /// Get the entry package from the context. |
|
| 525 | - | fn getEntryPackage(ctx: &CompileContext) -> package::Package throws (Error) { |
|
| 526 | + | fn getEntryPackage 'permission (ctx: &CompileContext 'permission) -> package::Package throws (Error) { |
|
| 526 | 527 | let entryIdx = ctx.entryPkgIdx else { |
|
| 527 | 528 | throw error(&["no entry package specified"]); |
|
| 528 | 529 | }; |
|
| 529 | 530 | return ctx.packages[entryIdx]; |
|
| 530 | 531 | } |
|
| 531 | 532 | ||
| 532 | 533 | /// Return the startup assembly path for the entry package, if one was supplied. |
|
| 533 | - | fn getEntryStartupPath(ctx: &CompileContext) -> ?*[u8] { |
|
| 534 | + | fn getEntryStartupPath 'permission (ctx: &CompileContext 'permission) -> ?*[u8] { |
|
| 534 | 535 | let entryIdx = ctx.entryPkgIdx else { |
|
| 535 | 536 | panic "getEntryStartupPath: no entry package"; |
|
| 536 | 537 | }; |
|
| 537 | 538 | return ctx.inputs[entryIdx].startupPath; |
|
| 538 | 539 | } |
|
| 539 | 540 | ||
| 540 | 541 | /// Get root module info from a package. |
|
| 541 | - | fn getRootModule(pkg: &package::Package, graph: &module::ModuleGraph) -> RootModule throws (Error) { |
|
| 542 | + | fn getRootModule 'permission (pkg: &package::Package, graph: &module::ModuleGraph 'permission) -> RootModule throws (Error) { |
|
| 542 | 543 | let rootId = pkg.rootModuleId else { |
|
| 543 | 544 | throw error(&["no root module found"]); |
|
| 544 | 545 | }; |
|
| 545 | 546 | let rootEntry = module::get(graph, rootId) else { |
|
| 546 | 547 | throw error(&["root module entry not found"]); |
|
| 547 | 548 | }; |
|
| 548 | - | let rootAst = module::astFor(rootEntry) else { |
|
| 549 | + | let rootAst = module::astFor(graph, rootEntry) else { |
|
| 549 | 550 | throw error(&["root module has no AST"]); |
|
| 550 | 551 | }; |
|
| 551 | 552 | return RootModule { entry: rootEntry, ast: rootAst }; |
|
| 552 | 553 | } |
|
| 553 | 554 | ||
| 554 | 555 | /// Dump the module graph. |
|
| 555 | - | unsafe fn dumpGraph(ctx: &CompileContext) { |
|
| 556 | + | unsafe fn dumpGraph 'permission (ctx: &CompileContext 'permission) { |
|
| 556 | 557 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 557 | 558 | module::printer::printGraph(&ctx.graph, &mut arena); |
|
| 558 | 559 | } |
|
| 559 | 560 | ||
| 560 | 561 | /// Dump the parsed AST. |
|
| 561 | - | unsafe fn dumpAst(ctx: &CompileContext) throws (Error) { |
|
| 562 | + | unsafe fn dumpAst 'permission (ctx: &CompileContext 'permission) throws (Error) { |
|
| 562 | 563 | let pkg = try getEntryPackage(ctx); |
|
| 563 | 564 | let root = try getRootModule(&pkg, &ctx.graph); |
|
| 564 | 565 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 565 | 566 | ||
| 566 | 567 | ast::printer::printTree(root.ast, &mut arena); |
|
| 567 | 568 | } |
|
| 568 | 569 | ||
| 569 | 570 | /// Lower all packages into a single IL program. |
|
| 570 | 571 | /// Dependencies are lowered first, then the entry package. |
|
| 571 | - | unsafe fn lowerAllPackages 'arena ( |
|
| 572 | - | ctx: &CompileContext, |
|
| 572 | + | unsafe fn lowerAllPackages 'arena 'permission ( |
|
| 573 | + | ctx: &CompileContext 'permission, |
|
| 573 | 574 | res: *unsafe mut resolver::Resolver 'arena |
|
| 574 | 575 | ) -> il::Program throws (Error) { |
|
| 575 | 576 | let entryIdx = ctx.entryPkgIdx else { |
|
| 576 | 577 | panic "lowerAllPackages: no entry package"; |
|
| 577 | 578 | }; |
|
| 578 | 579 | let entryPkg = &ctx.packages[entryIdx]; |
|
| 579 | 580 | ||
| 580 | 581 | // Create the lowerer accumulator using entry package's name. |
|
| 581 | 582 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
|
| 582 | 583 | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
|
| 583 | - | let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in { |
|
| 584 | - | let mut low = lower::lowerer( |
|
| 585 | - | resolved, graph, entryPkg.name, arena, options |
|
| 586 | - | ); |
|
| 584 | + | let resolved: 'phase = &*res where 'arena: 'phase in { |
|
| 585 | + | let mut low = lower::lowerer(resolved, entryPkg.name, arena, options); |
|
| 587 | 586 | try lowerAllPackagesInto(ctx, &mut low, &mut *arena); |
|
| 588 | 587 | ||
| 589 | 588 | // Finalize and return the unified program. |
|
| 590 | 589 | return lower::finalize(low); |
|
| 591 | 590 | } |
|
| 592 | 591 | } |
|
| 593 | 592 | ||
| 594 | 593 | /// Lower all packages into an existing lowerer. |
|
| 595 | - | unsafe fn lowerAllPackagesInto 'arena 'phase ( |
|
| 596 | - | ctx: &CompileContext, |
|
| 594 | + | unsafe fn lowerAllPackagesInto 'arena 'phase 'permission ( |
|
| 595 | + | ctx: &CompileContext 'permission, |
|
| 597 | 596 | low: &mut lower::Lowerer 'arena 'phase, |
|
| 598 | 597 | functionArena: &mut alloc::Arena |
|
| 599 | 598 | ) throws (Error) where 'arena: 'phase { |
|
| 600 | 599 | let entryIdx = ctx.entryPkgIdx else { |
|
| 601 | 600 | panic "lowerAllPackagesInto: no entry package"; |
| 609 | 608 | // Lower entry package. |
|
| 610 | 609 | try lowerPackage(ctx, low, &ctx.packages[entryIdx], true, functionArena); |
|
| 611 | 610 | } |
|
| 612 | 611 | ||
| 613 | 612 | /// Lower all modules in a package into the lowerer accumulator. |
|
| 614 | - | unsafe fn lowerPackage 'arena 'phase ( |
|
| 615 | - | ctx: &CompileContext, |
|
| 613 | + | unsafe fn lowerPackage 'arena 'phase 'permission ( |
|
| 614 | + | ctx: &CompileContext 'permission, |
|
| 616 | 615 | low: &mut lower::Lowerer 'arena 'phase, |
|
| 617 | 616 | pkg: &package::Package, |
|
| 618 | 617 | isEntry: bool, |
|
| 619 | 618 | functionArena: &mut alloc::Arena |
|
| 620 | 619 | ) throws (Error) where 'arena: 'phase { |
| 623 | 622 | }; |
|
| 624 | 623 | // Set lowerer's package context for qualified name generation. |
|
| 625 | 624 | // TODO: We shouldn't have to call this manually. |
|
| 626 | 625 | lower::setPackage(low, pkg.name); |
|
| 627 | 626 | ||
| 628 | - | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg, functionArena); |
|
| 627 | + | try lowerModuleTreeInto(low, &ctx.graph, rootId, isEntry, pkg, functionArena); |
|
| 629 | 628 | } |
|
| 630 | 629 | ||
| 631 | 630 | /// Recursively lower a module and all its children into the accumulator. |
|
| 632 | - | unsafe fn lowerModuleTreeInto 'arena 'phase ( |
|
| 633 | - | ctx: &CompileContext, |
|
| 631 | + | unsafe fn lowerModuleTreeInto 'arena 'phase 'permission ( |
|
| 634 | 632 | low: &mut lower::Lowerer 'arena 'phase, |
|
| 635 | - | graph: &module::ModuleGraph, |
|
| 633 | + | graph: &module::ModuleGraph 'permission, |
|
| 636 | 634 | modId: u16, |
|
| 637 | 635 | isRoot: bool, |
|
| 638 | 636 | pkg: &package::Package, |
|
| 639 | 637 | functionArena: &mut alloc::Arena |
|
| 640 | 638 | ) throws (Error) where 'arena: 'phase { |
|
| 641 | 639 | let entry = module::get(graph, modId) else { |
|
| 642 | 640 | throw error(&["module entry not found"]); |
|
| 643 | 641 | }; |
|
| 644 | - | let modAst = module::astFor(entry) else { |
|
| 642 | + | let modAst = module::astFor(graph, entry) else { |
|
| 645 | 643 | throw error(&["module has no AST"]); |
|
| 646 | 644 | }; |
|
| 647 | 645 | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 648 | 646 | ||
| 649 | 647 | try lower::lowerModule(low, modId, modAst, isRoot, functionArena) catch err { |
| 653 | 651 | io::printError("\n"); |
|
| 654 | 652 | ||
| 655 | 653 | throw Error::Other; |
|
| 656 | 654 | }; |
|
| 657 | 655 | // Recurse into children. |
|
| 658 | - | for i in 0..module::childCount(entry) { |
|
| 659 | - | let childId = module::childAt(entry, i); |
|
| 660 | - | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg, functionArena); |
|
| 656 | + | for i in 0..module::childCount(graph, entry) { |
|
| 657 | + | let childId = module::childAt(graph, entry, i); |
|
| 658 | + | try lowerModuleTreeInto(low, graph, childId, false, pkg, functionArena); |
|
| 661 | 659 | } |
|
| 662 | 660 | } |
|
| 663 | 661 | ||
| 664 | 662 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
|
| 665 | 663 | unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node { |
| 690 | 688 | ||
| 691 | 689 | return name; |
|
| 692 | 690 | } |
|
| 693 | 691 | ||
| 694 | 692 | /// Scan a single module's AST for `@test` functions and append them to `tests`. |
|
| 695 | - | fn collectModuleTests( |
|
| 696 | - | entry: *module::ModuleEntry, |
|
| 697 | - | tests: &mut [?TestDesc], |
|
| 698 | - | testCount: &mut u32 |
|
| 699 | - | ) { |
|
| 700 | - | let modAst = module::astFor(entry) else { |
|
| 693 | + | fn collectModuleTests 'permission (graph: &module::ModuleGraph 'permission, entry: *module::ModuleEntry, tests: &mut [?TestDesc], testCount: &mut u32) { |
|
| 694 | + | let modAst = module::astFor(graph, entry) else { |
|
| 701 | 695 | return; |
|
| 702 | 696 | }; |
|
| 703 | 697 | let case ast::NodeValue::Block(block) = modAst.value else { |
|
| 704 | 698 | return; |
|
| 705 | 699 | }; |
| 718 | 712 | } |
|
| 719 | 713 | } |
|
| 720 | 714 | } |
|
| 721 | 715 | ||
| 722 | 716 | /// Collect initialized test descriptors from one package in module order. |
|
| 723 | - | fn collectPackageTests(graph: &module::ModuleGraph, packageId: u16, tests: &mut [?TestDesc]) -> u32 { |
|
| 717 | + | fn collectPackageTests 'permission (graph: &module::ModuleGraph 'permission, packageId: u16, tests: &mut [?TestDesc]) -> u32 { |
|
| 724 | 718 | let mut count: u32 = 0; |
|
| 725 | - | for modIdx in 0..graph.entriesLen { |
|
| 719 | + | for modIdx in 0..module::entryCount(graph) { |
|
| 726 | 720 | if let entry = module::get(graph, modIdx as u16) { |
|
| 727 | 721 | if entry.packageId == packageId { |
|
| 728 | - | collectModuleTests(entry, tests, &mut count); |
|
| 722 | + | collectModuleTests(graph, entry, tests, &mut count); |
|
| 729 | 723 | } |
|
| 730 | 724 | } |
|
| 731 | 725 | } |
|
| 732 | 726 | return count; |
|
| 733 | 727 | } |
| 773 | 767 | /// ]); |
|
| 774 | 768 | /// } |
|
| 775 | 769 | /// ``` |
|
| 776 | 770 | /// |
|
| 777 | 771 | /// Uses `#`-prefixed names to avoid conflicts with user code. |
|
| 778 | - | unsafe fn generateTestRunner( |
|
| 779 | - | ctx: *unsafe mut CompileContext, |
|
| 780 | - | arena: &mut ast::NodeArena |
|
| 781 | - | ) throws (Error) { |
|
| 772 | + | unsafe fn generateTestRunner 'permission (ctx: *unsafe mut CompileContext 'permission, arena: &mut ast::NodeArena) throws (Error) { |
|
| 782 | 773 | let entryPkg = try getEntryPackage(ctx); |
|
| 783 | 774 | let root = try getRootModule(&entryPkg, &ctx.graph); |
|
| 784 | 775 | ||
| 785 | 776 | // Collect test functions from the entry package's modules. |
|
| 786 | 777 | let mut tests: [?TestDesc; MAX_TESTS] = [nil; MAX_TESTS]; |
| 921 | 912 | } |
|
| 922 | 913 | ||
| 923 | 914 | /// Serialize debug entries and write the `.debug` file. |
|
| 924 | 915 | /// Resolves module IDs to file paths via the module graph. |
|
| 925 | 916 | /// Format per entry is `{pc: u32, offset: u32, filePath: [u8], NULL}`. |
|
| 926 | - | fn writeDebugInfo( |
|
| 917 | + | fn writeDebugInfo 'permission ( |
|
| 927 | 918 | entries: &[types::DebugEntry], |
|
| 928 | - | graph: &module::ModuleGraph, |
|
| 919 | + | graph: &module::ModuleGraph 'permission, |
|
| 929 | 920 | basePath: *[u8], |
|
| 930 | 921 | buf: &mut [u8] |
|
| 931 | 922 | ) throws (Error) { |
|
| 932 | 923 | if entries.len == 0 { |
|
| 933 | 924 | return; |
| 953 | 944 | } |
|
| 954 | 945 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
|
| 955 | 946 | } |
|
| 956 | 947 | ||
| 957 | 948 | /// Run the resolver on the parsed modules. |
|
| 958 | - | unsafe fn runResolver 'arena (ctx: &CompileContext, mainArena: &'arena mut alloc::Arena, nodeCount: u32) -> resolver::Resolver 'arena throws (Error) { |
|
| 949 | + | unsafe fn runResolver 'arena 'permission ( |
|
| 950 | + | ctx: &CompileContext 'permission, |
|
| 951 | + | mainArena: &'arena mut alloc::Arena, |
|
| 952 | + | nodeCount: u32 |
|
| 953 | + | ) -> resolver::Resolver 'arena throws (Error) { |
|
| 959 | 954 | let entryPkg = try getEntryPackage(ctx); |
|
| 960 | 955 | ||
| 961 | 956 | pkgLog(&entryPkg, &["resolving", ".."]); |
|
| 962 | 957 | ||
| 963 | 958 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
| 986 | 981 | ||
| 987 | 982 | // Resolve all packages. |
|
| 988 | 983 | // TODO: Fix this error printing dance. |
|
| 989 | 984 | let diags = try resolver::resolve(&mut res, &ctx.graph, &resolverPkgs[..resolverPackageCount]) catch { |
|
| 990 | 985 | let diags = resolver::diagnostics(&mut res); |
|
| 991 | - | resolver::printer::printDiagnostics(&diags, &res); |
|
| 986 | + | resolver::printer::printDiagnostics(&diags, &res, &ctx.graph); |
|
| 992 | 987 | throw Error::Other; |
|
| 993 | 988 | }; |
|
| 994 | 989 | if not resolver::success(&diags) { |
|
| 995 | - | resolver::printer::printDiagnostics(&diags, &res); |
|
| 990 | + | resolver::printer::printDiagnostics(&diags, &res, &ctx.graph); |
|
| 996 | 991 | let mut countBuf: [u8; 10] = undefined; |
|
| 997 | 992 | let start = fmt::formatU32(diags.errors.len, &mut countBuf[..]); |
|
| 998 | 993 | io::printError("radiance: failed: "); |
|
| 999 | 994 | io::printError(&countBuf[start..]); |
|
| 1000 | 995 | io::printError(" errors\n"); |
| 1049 | 1044 | ||
| 1050 | 1045 | rv64::addAssembly(generator, program); |
|
| 1051 | 1046 | } |
|
| 1052 | 1047 | ||
| 1053 | 1048 | /// Assemble all inputs collected in the package inputs. |
|
| 1054 | - | unsafe fn assembleAsmInputs( |
|
| 1055 | - | ctx: &CompileContext, |
|
| 1049 | + | unsafe fn assembleAsmInputs 'permission ( |
|
| 1050 | + | ctx: &CompileContext 'permission, |
|
| 1056 | 1051 | generator: &mut rv64::Generator, |
|
| 1057 | 1052 | asmDataLen: &mut u32, |
|
| 1058 | 1053 | arena: &mut alloc::Arena |
|
| 1059 | 1054 | ) -> *[u8] throws (Error) { |
|
| 1060 | 1055 | for i in 0..ctx.packageCount { |
| 1070 | 1065 | } |
|
| 1071 | 1066 | return &ASM_RO_DATA_BUF[..*asmDataLen]; |
|
| 1072 | 1067 | } |
|
| 1073 | 1068 | ||
| 1074 | 1069 | /// Generate dependency packages before the entry package. |
|
| 1075 | - | unsafe fn generateAllPackagesInto 'arena 'phase ( |
|
| 1076 | - | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1077 | - | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1070 | + | unsafe fn generateAllPackagesInto 'arena 'phase 'permission ( |
|
| 1071 | + | ctx: &CompileContext 'permission, |
|
| 1072 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 1073 | + | generator: &mut rv64::Generator, |
|
| 1074 | + | fnArena: &mut alloc::Arena |
|
| 1078 | 1075 | ) throws (Error) where 'arena: 'phase { |
|
| 1079 | 1076 | let entryIdx = ctx.entryPkgIdx else panic "generateAllPackagesInto: no entry package"; |
|
| 1080 | 1077 | for i in 0..ctx.packageCount { |
|
| 1081 | 1078 | if i <> entryIdx { |
|
| 1082 | 1079 | try generatePackageInto(ctx, low, &ctx.packages[i], false, generator, fnArena); |
| 1084 | 1081 | } |
|
| 1085 | 1082 | try generatePackageInto(ctx, low, &ctx.packages[entryIdx], true, generator, fnArena); |
|
| 1086 | 1083 | } |
|
| 1087 | 1084 | ||
| 1088 | 1085 | /// Generate all functions in one package. |
|
| 1089 | - | unsafe fn generatePackageInto 'arena 'phase ( |
|
| 1090 | - | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1091 | - | pkg: &package::Package, isEntry: bool, |
|
| 1092 | - | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1086 | + | unsafe fn generatePackageInto 'arena 'phase 'permission ( |
|
| 1087 | + | ctx: &CompileContext 'permission, |
|
| 1088 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 1089 | + | pkg: &package::Package, |
|
| 1090 | + | isEntry: bool, |
|
| 1091 | + | generator: &mut rv64::Generator, |
|
| 1092 | + | fnArena: &mut alloc::Arena |
|
| 1093 | 1093 | ) throws (Error) where 'arena: 'phase { |
|
| 1094 | 1094 | let rootId = pkg.rootModuleId else throw error(&["no root module found"]); |
|
| 1095 | 1095 | lower::setPackage(low, pkg.name); |
|
| 1096 | 1096 | try generateModuleTree(ctx, low, rootId, isEntry, pkg, generator, fnArena); |
|
| 1097 | 1097 | } |
|
| 1098 | 1098 | ||
| 1099 | 1099 | /// Lower and consume each function before visiting the next declaration. |
|
| 1100 | - | unsafe fn generateModuleTree 'arena 'phase ( |
|
| 1101 | - | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1102 | - | modId: u16, isRoot: bool, pkg: &package::Package, |
|
| 1103 | - | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1100 | + | unsafe fn generateModuleTree 'arena 'phase 'permission ( |
|
| 1101 | + | ctx: &CompileContext 'permission, |
|
| 1102 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 1103 | + | modId: u16, |
|
| 1104 | + | isRoot: bool, |
|
| 1105 | + | pkg: &package::Package, |
|
| 1106 | + | generator: &mut rv64::Generator, |
|
| 1107 | + | fnArena: &mut alloc::Arena |
|
| 1104 | 1108 | ) throws (Error) where 'arena: 'phase { |
|
| 1105 | - | let entry = module::get(&ctx.graph, modId) else throw error(&["module entry not found"]); |
|
| 1106 | - | let modAst = module::astFor(entry) else throw error(&["module has no AST"]); |
|
| 1109 | + | let entry = module::get(&ctx.graph, modId) |
|
| 1110 | + | else throw error(&["module entry not found"]); |
|
| 1111 | + | let modAst = module::astFor(&ctx.graph, entry) |
|
| 1112 | + | else throw error(&["module has no AST"]); |
|
| 1107 | 1113 | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 1108 | 1114 | set low.currentMod = modId; |
|
| 1109 | 1115 | let mut cursor = try lower::moduleCursor(modAst, isRoot) catch err { |
|
| 1110 | 1116 | io::printError("radiance: internal error during lowering: "); |
|
| 1111 | 1117 | lower::printError(err); |
| 1120 | 1126 | throw Error::Other; |
|
| 1121 | 1127 | }; |
|
| 1122 | 1128 | let next = result else break; |
|
| 1123 | 1129 | codegen::emit(generator, fnArena, &*next.function, next.role); |
|
| 1124 | 1130 | } |
|
| 1125 | - | for i in 0..module::childCount(entry) { |
|
| 1126 | - | let childId = module::childAt(entry, i); |
|
| 1131 | + | for i in 0..module::childCount(&ctx.graph, entry) { |
|
| 1132 | + | let childId = module::childAt(&ctx.graph, entry, i); |
|
| 1127 | 1133 | try generateModuleTree(ctx, low, childId, false, pkg, generator, fnArena); |
|
| 1128 | 1134 | } |
|
| 1129 | 1135 | } |
|
| 1130 | 1136 | ||
| 1131 | 1137 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
|
| 1132 | - | unsafe fn lowerAndGenerateAllPackages 'arena ( |
|
| 1133 | - | ctx: &CompileContext, |
|
| 1138 | + | unsafe fn lowerAndGenerateAllPackages 'arena 'permission ( |
|
| 1139 | + | ctx: &CompileContext 'permission, |
|
| 1134 | 1140 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1135 | 1141 | fnArena: &mut alloc::Arena, |
|
| 1136 | 1142 | codegenOptions: CodegenOptions |
|
| 1137 | 1143 | ) -> rv64::Program throws (Error) { |
|
| 1138 | 1144 | let entryIdx = ctx.entryPkgIdx else { |
| 1170 | 1176 | placement: rv64::image::Placement::Hosted, |
|
| 1171 | 1177 | }, |
|
| 1172 | 1178 | emitterStorage |
|
| 1173 | 1179 | ); |
|
| 1174 | 1180 | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
|
| 1175 | - | let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in { |
|
| 1176 | - | let mut low = lower::lowerer( |
|
| 1177 | - | resolved, graph, entryPkg.name, arena, options |
|
| 1178 | - | ); |
|
| 1181 | + | let resolved: 'phase = &*res where 'arena: 'phase in { |
|
| 1182 | + | let mut low = lower::lowerer(resolved, entryPkg.name, arena, options); |
|
| 1179 | 1183 | let mut asmDataLen: u32 = 0; |
|
| 1180 | 1184 | if let path = startupPath { |
|
| 1181 | 1185 | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, arena); |
|
| 1182 | 1186 | } |
|
| 1183 | - | try generateAllPackagesInto(ctx, &mut low, &mut generator, fnArena); |
|
| 1187 | + | try generateAllPackagesInto( |
|
| 1188 | + | ctx, &mut low, &mut generator, fnArena |
|
| 1189 | + | ); |
|
| 1184 | 1190 | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, arena); |
|
| 1185 | - | ||
| 1186 | 1191 | match generator.entryPatch { |
|
| 1187 | 1192 | case rv64::EntryPatch::Reserved(targetName) => { |
|
| 1188 | 1193 | if targetName == nil { |
|
| 1189 | 1194 | throw error(&["fatal:", "no default function found"]); |
|
| 1190 | 1195 | } |
| 1218 | 1223 | /// Default function entry. |
|
| 1219 | 1224 | entry: ?*[u8], |
|
| 1220 | 1225 | } |
|
| 1221 | 1226 | ||
| 1222 | 1227 | /// Collect exported definitions and the default entry from a package's source modules. |
|
| 1223 | - | unsafe fn packageExports( |
|
| 1224 | - | ctx: &CompileContext, |
|
| 1228 | + | unsafe fn packageExports 'permission ( |
|
| 1229 | + | ctx: &CompileContext 'permission, |
|
| 1225 | 1230 | pkg: &package::Package, |
|
| 1226 | 1231 | ilProgram: &il::Program, |
|
| 1227 | 1232 | exports: &mut [binary::Export], |
|
| 1228 | 1233 | arena: &mut alloc::Arena |
|
| 1229 | 1234 | ) -> PackageExports throws (Error) { |
|
| 1230 | 1235 | let mut count: u32 = 0; |
|
| 1231 | 1236 | let mut entryName: ?*[u8] = nil; |
|
| 1232 | - | for i in 0..ctx.graph.entriesLen { |
|
| 1237 | + | for i in 0..module::entryCount(&ctx.graph) { |
|
| 1233 | 1238 | let modEntry = module::get(&ctx.graph, i as u16) else continue; |
|
| 1234 | 1239 | if modEntry.packageId <> pkg.id { |
|
| 1235 | 1240 | continue; |
|
| 1236 | 1241 | } |
|
| 1237 | - | let root = module::astFor(modEntry) else continue; |
|
| 1242 | + | let root = module::astFor(&ctx.graph, modEntry) else continue; |
|
| 1238 | 1243 | let case ast::NodeValue::Block(block) = root.value else continue; |
|
| 1239 | 1244 | for node in block.statements { |
|
| 1240 | 1245 | let mut ident: ?*ast::Node = nil; |
|
| 1241 | 1246 | let mut attrs: ?ast::Attributes = nil; |
|
| 1242 | 1247 | let mut kind = binary::ExportKind::Function; |
| 1312 | 1317 | throw error(&["cannot write binary RIL package", name]); |
|
| 1313 | 1318 | } |
|
| 1314 | 1319 | } |
|
| 1315 | 1320 | ||
| 1316 | 1321 | /// Emit one binary RIL file per package into an existing directory. |
|
| 1317 | - | unsafe fn emitPackages 'arena ( |
|
| 1318 | - | ctx: &CompileContext, |
|
| 1322 | + | unsafe fn emitPackages 'arena 'permission ( |
|
| 1323 | + | ctx: &CompileContext 'permission, |
|
| 1319 | 1324 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1320 | 1325 | directory: *[u8] |
|
| 1321 | 1326 | ) throws (Error) { |
|
| 1322 | 1327 | for i in 0..ctx.packageCount { |
|
| 1323 | 1328 | if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil { |
| 1361 | 1366 | let suffix = mem::stripPrefix(owner, name) else return false; |
|
| 1362 | 1367 | return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':'; |
|
| 1363 | 1368 | } |
|
| 1364 | 1369 | ||
| 1365 | 1370 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1366 | - | unsafe fn compile 'arena ( |
|
| 1367 | - | ctx: &CompileContext, |
|
| 1371 | + | unsafe fn compile 'arena 'permission ( |
|
| 1372 | + | ctx: &CompileContext 'permission, |
|
| 1368 | 1373 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1369 | 1374 | fnArena: &mut alloc::Arena |
|
| 1370 | 1375 | ) throws (Error) { |
|
| 1371 | 1376 | let entryPkg = try getEntryPackage(ctx); |
|
| 1372 | 1377 | if let directory = ctx.rilDirectory { |
| 1381 | 1386 | il::printer::printProgram(&mut out, &program); |
|
| 1382 | 1387 | io::print("\n"); |
|
| 1383 | 1388 | return; |
|
| 1384 | 1389 | } |
|
| 1385 | 1390 | if ctx.dump == Dump::Asm { |
|
| 1386 | - | let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions { |
|
| 1387 | - | logPath: nil, |
|
| 1388 | - | debug: false, |
|
| 1389 | - | entryMode: CodegenEntryMode::None, |
|
| 1390 | - | }); |
|
| 1391 | + | let result = try lowerAndGenerateAllPackages( |
|
| 1392 | + | ctx, res, fnArena, CodegenOptions { |
|
| 1393 | + | logPath: nil, |
|
| 1394 | + | debug: false, |
|
| 1395 | + | entryMode: CodegenEntryMode::None, |
|
| 1396 | + | } |
|
| 1397 | + | ); |
|
| 1391 | 1398 | printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs); |
|
| 1392 | 1399 | io::print("\n"); |
|
| 1393 | 1400 | ||
| 1394 | 1401 | return; |
|
| 1395 | 1402 | } |
| 1397 | 1404 | let outPath = ctx.outputPath else { |
|
| 1398 | 1405 | try lowerAllPackages(ctx, res); |
|
| 1399 | 1406 | return; |
|
| 1400 | 1407 | }; |
|
| 1401 | 1408 | let startupPath = getEntryStartupPath(ctx); |
|
| 1402 | - | let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions { |
|
| 1403 | - | logPath: outPath, |
|
| 1404 | - | debug: ctx.debug, |
|
| 1405 | - | entryMode: CodegenEntryMode::None |
|
| 1406 | - | if startupPath <> nil |
|
| 1407 | - | else CodegenEntryMode::DefaultEntry, |
|
| 1408 | - | }); |
|
| 1409 | + | let result = try lowerAndGenerateAllPackages( |
|
| 1410 | + | ctx, res, fnArena, CodegenOptions { |
|
| 1411 | + | logPath: outPath, |
|
| 1412 | + | debug: ctx.debug, |
|
| 1413 | + | entryMode: CodegenEntryMode::None |
|
| 1414 | + | if startupPath <> nil |
|
| 1415 | + | else CodegenEntryMode::DefaultEntry, |
|
| 1416 | + | } |
|
| 1417 | + | ); |
|
| 1409 | 1418 | ||
| 1410 | 1419 | let codeBytes = @sliceOf(result.code.ptr as *u8, result.code.len * rv64::INSTR_SIZE as u32); |
|
| 1411 | 1420 | if not writeImage( |
|
| 1412 | 1421 | codeBytes, |
|
| 1413 | 1422 | &RO_DATA_BUF[..result.roDataSize], |
| 1424 | 1433 | } |
|
| 1425 | 1434 | pkgLog(&entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1426 | 1435 | } |
|
| 1427 | 1436 | ||
| 1428 | 1437 | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 1429 | - | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
|
| 1430 | - | let mut ctx = try processCommand(env.args, &mut arena) catch { |
|
| 1431 | - | return 1; |
|
| 1432 | - | }; |
|
| 1433 | - | match ctx.dump { |
|
| 1434 | - | case Dump::Ast => { |
|
| 1435 | - | try dumpAst(&ctx) catch { |
|
| 1438 | + | let mut owner = module::Permission {}; |
|
| 1439 | + | let permission: 'permission = &mut owner in { |
|
| 1440 | + | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
|
| 1441 | + | let mut ctx = try processCommand(env.args, &mut arena, permission) catch { |
|
| 1442 | + | return 1; |
|
| 1443 | + | }; |
|
| 1444 | + | match ctx.dump { |
|
| 1445 | + | case Dump::Ast => { |
|
| 1446 | + | try dumpAst(&ctx) catch { |
|
| 1447 | + | return 1; |
|
| 1448 | + | }; |
|
| 1449 | + | return 0; |
|
| 1450 | + | } |
|
| 1451 | + | case Dump::Graph => { |
|
| 1452 | + | dumpGraph(&ctx); |
|
| 1453 | + | return 0; |
|
| 1454 | + | } |
|
| 1455 | + | else => {} |
|
| 1456 | + | } |
|
| 1457 | + | // Generate test runner if in test mode. |
|
| 1458 | + | if ctx.config.buildTest { |
|
| 1459 | + | try generateTestRunner(&mut ctx, &mut arena) catch { |
|
| 1436 | 1460 | return 1; |
|
| 1437 | 1461 | }; |
|
| 1438 | - | return 0; |
|
| 1439 | 1462 | } |
|
| 1440 | - | case Dump::Graph => { |
|
| 1441 | - | dumpGraph(&ctx); |
|
| 1463 | + | // Run resolution phase. |
|
| 1464 | + | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 1465 | + | let arenaRef: 'arena = &mut mainArena, context = &ctx in { |
|
| 1466 | + | let mut res = try runResolver( |
|
| 1467 | + | context, arenaRef, arena.nextId |
|
| 1468 | + | ) catch { |
|
| 1469 | + | return 1; |
|
| 1470 | + | }; |
|
| 1471 | + | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
|
| 1472 | + | ||
| 1473 | + | // Lower, dump, and/or generate output. |
|
| 1474 | + | try compile(context, &mut res, &mut fnArena) catch { |
|
| 1475 | + | return 1; |
|
| 1476 | + | }; |
|
| 1442 | 1477 | return 0; |
|
| 1443 | 1478 | } |
|
| 1444 | - | else => {} |
|
| 1445 | - | } |
|
| 1446 | - | // Generate test runner if in test mode. |
|
| 1447 | - | if ctx.config.buildTest { |
|
| 1448 | - | try generateTestRunner(&mut ctx, &mut arena) catch { |
|
| 1449 | - | return 1; |
|
| 1450 | - | }; |
|
| 1451 | - | } |
|
| 1452 | - | // Run resolution phase. |
|
| 1453 | - | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 1454 | - | let arenaRef: 'arena = &mut mainArena, context = &ctx in { |
|
| 1455 | - | let mut res = try runResolver(context, arenaRef, arena.nextId) catch { |
|
| 1456 | - | return 1; |
|
| 1457 | - | }; |
|
| 1458 | - | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
|
| 1459 | - | ||
| 1460 | - | // Lower, dump, and/or generate output. |
|
| 1461 | - | try compile(context, &mut res, &mut fnArena) catch { |
|
| 1462 | - | return 1; |
|
| 1463 | - | }; |
|
| 1464 | - | return 0; |
|
| 1465 | 1479 | } |
|
| 1466 | 1480 | } |
lib/std/lang/lower.rad
+6 -14
| 310 | 310 | /// Arena for persistent lowering state, including data and symbol names. |
|
| 311 | 311 | arena: *unsafe mut alloc::Arena, |
|
| 312 | 312 | /// Resolver for type information. Used to query types, symbols, and |
|
| 313 | 313 | /// compile-time constant values during lowering. |
|
| 314 | 314 | resolver: &'phase resolver::Resolver 'arena, |
|
| 315 | - | /// Module graph for cross-module symbol resolution. |
|
| 316 | - | moduleGraph: ?&'phase module::ModuleGraph, |
|
| 317 | 315 | /// Package name for qualified symbol names. |
|
| 318 | 316 | pkgName: *[u8], |
|
| 319 | 317 | /// Current module being lowered. |
|
| 320 | 318 | currentMod: ?u16, |
|
| 321 | 319 | /// Global data items (string literals, constants, static arrays). |
| 859 | 857 | pkgName: *[u8], |
|
| 860 | 858 | arena: &mut alloc::Arena |
|
| 861 | 859 | ) -> il::Program throws (LowerError) { |
|
| 862 | 860 | let functionArena = (&mut *arena) as *unsafe mut alloc::Arena; |
|
| 863 | 861 | let resolved: 'phase = &*res where 'arena: 'phase in { |
|
| 864 | - | let mut low = lowererState(resolved, nil, pkgName, functionArena, |
|
| 862 | + | let mut low = lowererState(resolved, pkgName, functionArena, |
|
| 865 | 863 | LowerOptions { debug: false, buildTest: false }); |
|
| 866 | 864 | try lowerDecls(&mut low, root, true, functionArena); |
|
| 867 | 865 | ||
| 868 | 866 | return finalize(low); |
|
| 869 | 867 | } |
| 872 | 870 | ///////////////////////////////// |
|
| 873 | 871 | // Multi-Module Lowering API // |
|
| 874 | 872 | ///////////////////////////////// |
|
| 875 | 873 | ||
| 876 | 874 | /// Create a lowerer for multi-module compilation. |
|
| 877 | - | /// The resolver, graph, and persistent arena must outlive the returned lowerer. |
|
| 875 | + | /// The resolver and persistent arena must outlive the returned lowerer. |
|
| 878 | 876 | export unsafe fn lowerer 'arena 'phase ( |
|
| 879 | 877 | res: &'phase resolver::Resolver 'arena, |
|
| 880 | - | graph: &'phase module::ModuleGraph, |
|
| 881 | 878 | pkgName: *[u8], |
|
| 882 | 879 | arena: *unsafe mut alloc::Arena, |
|
| 883 | 880 | options: LowerOptions |
|
| 884 | 881 | ) -> Lowerer 'arena 'phase where 'arena: 'phase { |
|
| 885 | - | return lowererState(res, graph, pkgName, arena, options); |
|
| 882 | + | return lowererState(res, pkgName, arena, options); |
|
| 886 | 883 | } |
|
| 887 | 884 | ||
| 888 | 885 | /// Initialize lowering state with empty owned collections and borrowed metadata. |
|
| 889 | 886 | fn lowererState 'arena 'phase ( |
|
| 890 | 887 | res: &'phase resolver::Resolver 'arena, |
|
| 891 | - | graph: ?&'phase module::ModuleGraph, |
|
| 892 | 888 | pkgName: *[u8], |
|
| 893 | 889 | arena: *unsafe mut alloc::Arena, |
|
| 894 | 890 | options: LowerOptions |
|
| 895 | 891 | ) -> Lowerer 'arena 'phase where 'arena: 'phase { |
|
| 896 | 892 | return Lowerer 'arena 'phase { |
|
| 897 | 893 | arena, |
|
| 898 | 894 | resolver: res, |
|
| 899 | - | moduleGraph: graph, |
|
| 900 | 895 | pkgName, |
|
| 901 | 896 | currentMod: nil, |
|
| 902 | 897 | data: &mut [], |
|
| 903 | 898 | packageDataStart: 0, |
|
| 904 | 899 | functions: &mut [], |
| 1002 | 997 | ||
| 1003 | 998 | ///////////////////////////////// |
|
| 1004 | 999 | // Qualified Name Construction // |
|
| 1005 | 1000 | ///////////////////////////////// |
|
| 1006 | 1001 | ||
| 1007 | - | /// Get module path segments for the current or specified module. |
|
| 1008 | - | /// Returns empty slice if no module graph or module not found. |
|
| 1002 | + | /// Return the qualified path for a module from retained resolver identities. |
|
| 1003 | + | /// Returns an empty slice if no module is active or its identity is missing. |
|
| 1009 | 1004 | fn getModulePath 'arena 'phase (self: &Lowerer 'arena 'phase, modId: ?u16) -> *[*[u8]] where 'arena: 'phase { |
|
| 1010 | - | let graph = self.moduleGraph else { |
|
| 1011 | - | return &[]; |
|
| 1012 | - | }; |
|
| 1013 | 1005 | let mut id = modId; |
|
| 1014 | 1006 | if id == nil { |
|
| 1015 | 1007 | set id = self.currentMod; |
|
| 1016 | 1008 | } |
|
| 1017 | 1009 | let actualId = id else { |
|
| 1018 | 1010 | return &[]; |
|
| 1019 | 1011 | }; |
|
| 1020 | - | let entry = module::get(graph, actualId) else { |
|
| 1012 | + | let entry = resolver::moduleFor(self.resolver, actualId) else { |
|
| 1021 | 1013 | return &[]; |
|
| 1022 | 1014 | }; |
|
| 1023 | 1015 | return module::moduleQualifiedPath(entry); |
|
| 1024 | 1016 | } |
|
| 1025 | 1017 |
lib/std/lang/module.rad
+106 -63
| 52 | 52 | PathTooDeep, |
|
| 53 | 53 | /// Attempt to register a module before its parent. |
|
| 54 | 54 | MissingParent, |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | + | /// Zero-state owner for authority over one module graph's mutable state. |
|
| 58 | + | export record Permission {} |
|
| 59 | + | ||
| 57 | 60 | /// Module metadata recorded inside the dependency graph. |
|
| 58 | 61 | export record ModuleEntry: Copy { |
|
| 59 | 62 | /// Numeric identifier for the slot (index in the graph array). |
|
| 60 | 63 | id: u16, |
|
| 61 | 64 | /// Package identifier this module belongs to. |
| 70 | 73 | name: *[u8], |
|
| 71 | 74 | /// Logical path from the root module to this module. |
|
| 72 | 75 | path: [*[u8]; MAX_MODULE_PATH_DEPTH], |
|
| 73 | 76 | /// Number of segments inside `path`. |
|
| 74 | 77 | pathDepth: u32, |
|
| 75 | - | /// Mutable module state shared by all entry views. |
|
| 76 | - | updates: *cell ModuleUpdates, |
|
| 77 | 78 | } |
|
| 78 | 79 | ||
| 79 | - | /// Copyable state stored in a module entry's cell. |
|
| 80 | + | /// Copyable state stored in a module graph update cell. |
|
| 80 | 81 | export record ModuleUpdates: Copy { |
|
| 81 | 82 | /// Current lifecycle state. |
|
| 82 | 83 | state: ModuleState, |
|
| 83 | 84 | /// Child module identifiers declared directly inside this module. |
|
| 84 | 85 | children: [u16; MAX_MODULES], |
| 89 | 90 | /// Source text for this module (for error reporting). |
|
| 90 | 91 | source: ?*[u8], |
|
| 91 | 92 | } |
|
| 92 | 93 | ||
| 93 | 94 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 94 | - | export record ModuleGraph { |
|
| 95 | + | export opaque record ModuleGraph: 'permission { |
|
| 95 | 96 | /// Entry identities indexed by module identifier. |
|
| 96 | 97 | entries: *mut [?*ModuleEntry], |
|
| 98 | + | /// Permission-associated updates indexed by module identifier. |
|
| 99 | + | updates: [?*cell 'permission ModuleUpdates; MAX_MODULES], |
|
| 100 | + | /// External authority for the update cells in this graph. |
|
| 101 | + | permission: &'permission mut Permission, |
|
| 97 | 102 | /// Number of initialized entries. |
|
| 98 | 103 | entriesLen: u32, |
|
| 99 | 104 | /// Arena for AST nodes and stable entry allocations. |
|
| 100 | 105 | arena: ?*unsafe mut ast::NodeArena, |
|
| 101 | 106 | } |
|
| 102 | 107 | ||
| 103 | 108 | /// Initialize an empty module graph backed by the provided storage. |
|
| 104 | 109 | /// The AST arena must outlive the graph and all retained entry views. |
|
| 105 | 110 | /// Entry allocation must occur outside speculative parser allocations. |
|
| 106 | - | export unsafe fn moduleGraph( |
|
| 111 | + | export unsafe fn moduleGraph 'permission ( |
|
| 107 | 112 | storage: *mut [?*ModuleEntry], |
|
| 108 | - | arena: &mut ast::NodeArena |
|
| 109 | - | ) -> ModuleGraph { |
|
| 113 | + | arena: &mut ast::NodeArena, |
|
| 114 | + | permission: &'permission mut Permission |
|
| 115 | + | ) -> ModuleGraph 'permission { |
|
| 110 | 116 | for i in 0..storage.len { |
|
| 111 | 117 | set storage[i] = nil; |
|
| 112 | 118 | } |
|
| 113 | - | return ModuleGraph { |
|
| 119 | + | return ModuleGraph 'permission { |
|
| 114 | 120 | entries: storage, |
|
| 121 | + | updates: [nil; MAX_MODULES], |
|
| 122 | + | permission, |
|
| 115 | 123 | entriesLen: 0, |
|
| 116 | 124 | arena: arena as *unsafe mut ast::NodeArena, |
|
| 117 | 125 | }; |
|
| 118 | 126 | } |
|
| 119 | 127 | ||
| 120 | 128 | /// Register a root module residing at `path` for a package. |
|
| 121 | - | export unsafe fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 129 | + | export unsafe fn registerRoot 'permission (graph: &mut ModuleGraph 'permission, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 122 | 130 | let name = try basenameSlice(filePath); |
|
| 123 | 131 | return try registerRootWithName(graph, pool, packageId, name, filePath); |
|
| 124 | 132 | } |
|
| 125 | 133 | ||
| 126 | 134 | /// Register a root module with an explicit name and file path for a package. |
|
| 127 | - | export unsafe fn registerRootWithName( |
|
| 128 | - | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 135 | + | export unsafe fn registerRootWithName 'permission ( |
|
| 136 | + | graph: &mut ModuleGraph 'permission, pool: &mut strings::Pool, |
|
| 129 | 137 | packageId: u16, |
|
| 130 | 138 | name: *[u8], |
|
| 131 | 139 | filePath: *[u8] |
|
| 132 | 140 | ) -> u16 throws (ModuleError) { |
|
| 133 | 141 | let m = try allocModule(graph, pool, packageId, name, filePath); |
| 138 | 146 | return id; |
|
| 139 | 147 | } |
|
| 140 | 148 | ||
| 141 | 149 | /// Register a child module. |
|
| 142 | 150 | /// Returns the module identifier. |
|
| 143 | - | export unsafe fn registerChild( |
|
| 144 | - | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 151 | + | export unsafe fn registerChild 'permission ( |
|
| 152 | + | graph: &mut ModuleGraph 'permission, pool: &mut strings::Pool, |
|
| 145 | 153 | parentId: u16, |
|
| 146 | 154 | name: *[u8], |
|
| 147 | 155 | filePath: *[u8] |
|
| 148 | 156 | ) -> u16 throws (ModuleError) { |
|
| 149 | 157 | assert name.len > 0, "registerChild: name must not be empty"; |
| 165 | 173 | for p in moduleQualifiedPath(parent) { |
|
| 166 | 174 | try appendPathSegment(m, p); |
|
| 167 | 175 | } |
|
| 168 | 176 | try appendPathSegment(m, name); |
|
| 169 | 177 | ||
| 170 | - | let id = try addChild(parent, m.id); |
|
| 178 | + | let id = try addChild(graph, parent, m.id); |
|
| 171 | 179 | publish(graph, m); |
|
| 172 | 180 | return id; |
|
| 173 | 181 | } |
|
| 174 | 182 | ||
| 183 | + | /// Return the number of registered module identities. |
|
| 184 | + | export fn entryCount 'permission (graph: &ModuleGraph 'permission) -> u32 { |
|
| 185 | + | return graph.entriesLen; |
|
| 186 | + | } |
|
| 187 | + | ||
| 175 | 188 | /// Fetch a read-only view of the module identified by `id`. |
|
| 176 | - | export fn get(graph: &ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 189 | + | export fn get 'permission (graph: &ModuleGraph 'permission, id: u16) -> ?*ModuleEntry { |
|
| 177 | 190 | if not isValidId(graph, id) { |
|
| 178 | 191 | return nil; |
|
| 179 | 192 | } |
|
| 180 | 193 | return graph.entries[id as u32]; |
|
| 181 | 194 | } |
|
| 182 | 195 | ||
| 183 | 196 | /// Return the identifier of the child stored at `index`. |
|
| 184 | - | export fn childAt(m: *ModuleEntry, index: u32) -> u16 { |
|
| 185 | - | let updates = *m.updates; |
|
| 186 | - | assert index < updates.childrenLen, "childAt: index must be valid"; |
|
| 187 | - | return updates.children[index]; |
|
| 197 | + | export fn childAt 'permission (graph: &ModuleGraph 'permission, m: *ModuleEntry, index: u32) -> u16 { |
|
| 198 | + | let cell = updatesFor(graph, m.id); |
|
| 199 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 200 | + | assert index < updates.childrenLen, "childAt: index must be valid"; |
|
| 201 | + | return updates.children[index]; |
|
| 202 | + | } |
|
| 188 | 203 | } |
|
| 189 | 204 | ||
| 190 | 205 | /// Public accessor for a module's directory prefix. |
|
| 191 | 206 | export fn moduleDir(m: *ModuleEntry) -> *[u8] { |
|
| 192 | 207 | return &m.filePath[..m.dirLen]; |
| 197 | 212 | assert m.pathDepth > 0, "moduleQualifiedPath: path must not be empty"; |
|
| 198 | 213 | return &m.path[..m.pathDepth]; |
|
| 199 | 214 | } |
|
| 200 | 215 | ||
| 201 | 216 | /// Retrieve the lifecycle state for `id`. |
|
| 202 | - | export fn state(graph: &ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 217 | + | export fn state 'permission (graph: &ModuleGraph 'permission, id: u16) -> ModuleState throws (ModuleError) { |
|
| 203 | 218 | let m = get(graph, id) else { |
|
| 204 | 219 | throw ModuleError::NotFound(id); |
|
| 205 | 220 | }; |
|
| 206 | - | return (*m.updates).state; |
|
| 221 | + | let cell = updatesFor(graph, m.id); |
|
| 222 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 223 | + | return updates.state; |
|
| 224 | + | } |
|
| 207 | 225 | } |
|
| 208 | 226 | ||
| 209 | 227 | /// Record the parsed AST root for `id`. |
|
| 210 | - | export fn setAst(graph: &mut ModuleGraph, id: u16, root: *ast::Node) throws (ModuleError) { |
|
| 228 | + | export fn setAst 'permission (graph: &mut ModuleGraph 'permission, id: u16, root: *ast::Node) throws (ModuleError) { |
|
| 211 | 229 | let m = get(graph, id) else throw ModuleError::NotFound(id); |
|
| 212 | - | let mut updates = *m.updates; |
|
| 213 | - | set updates.ast = root; |
|
| 214 | - | set updates.state = ModuleState::Parsed; |
|
| 215 | - | set *m.updates = updates; |
|
| 230 | + | let cell = updatesFor(graph, m.id); |
|
| 231 | + | let authority: 'loan = &mut *graph.permission, updates = &mut *cell in { |
|
| 232 | + | set updates.ast = root; |
|
| 233 | + | set updates.state = ModuleState::Parsed; |
|
| 234 | + | } |
|
| 216 | 235 | } |
|
| 217 | 236 | ||
| 218 | 237 | /// Set the source text for a module. |
|
| 219 | - | export fn setSource(graph: &mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 238 | + | export fn setSource 'permission (graph: &mut ModuleGraph 'permission, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 220 | 239 | let m = get(graph, id) else throw ModuleError::NotFound(id); |
|
| 221 | - | let mut updates = *m.updates; |
|
| 222 | - | set updates.source = source; |
|
| 223 | - | set *m.updates = updates; |
|
| 240 | + | let cell = updatesFor(graph, m.id); |
|
| 241 | + | let authority: 'loan = &mut *graph.permission, updates = &mut *cell in { |
|
| 242 | + | set updates.source = source; |
|
| 243 | + | } |
|
| 224 | 244 | } |
|
| 225 | 245 | ||
| 226 | 246 | /// Look up a child module by name under the given parent. |
|
| 227 | - | export fn findChild(graph: &ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 247 | + | export fn findChild 'permission (graph: &ModuleGraph 'permission, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 228 | 248 | assert isValidId(graph, parentId), "findChild: parent identifier is valid"; |
|
| 229 | 249 | ||
| 230 | 250 | let parent = get(graph, parentId) else panic; |
|
| 231 | - | let updates = *parent.updates; |
|
| 232 | - | for i in 0..updates.childrenLen { |
|
| 233 | - | let childId = updates.children[i]; |
|
| 234 | - | let child = get(graph, childId) else panic; |
|
| 235 | - | if mem::eq(child.name, name) { |
|
| 236 | - | return child; |
|
| 251 | + | let cell = updatesFor(graph, parent.id); |
|
| 252 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 253 | + | for i in 0..updates.childrenLen { |
|
| 254 | + | let childId = updates.children[i]; |
|
| 255 | + | let child = get(graph, childId) else panic; |
|
| 256 | + | if mem::eq(child.name, name) { |
|
| 257 | + | return child; |
|
| 258 | + | } |
|
| 237 | 259 | } |
|
| 238 | 260 | } |
|
| 239 | 261 | return nil; |
|
| 240 | 262 | } |
|
| 241 | 263 |
| 275 | 297 | ||
| 276 | 298 | /// Register a module from a file path, creating the full hierarchy as needed. |
|
| 277 | 299 | /// The path is split into components and the module hierarchy is built accordingly. |
|
| 278 | 300 | /// If `rootId` is `nil`, registers a new root for the given package. |
|
| 279 | 301 | /// Returns the module ID of the last component. |
|
| 280 | - | export unsafe fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 302 | + | export unsafe fn registerFromPath 'permission (graph: &mut ModuleGraph 'permission, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 281 | 303 | let root = rootId else { |
|
| 282 | 304 | return try registerRoot(graph, pool, packageId, filePath); |
|
| 283 | 305 | }; |
|
| 284 | 306 | let rootEntry = get(graph, root) else { |
|
| 285 | 307 | panic "registerFromPath: root is missing from storage"; |
| 320 | 342 | } |
|
| 321 | 343 | return try registerChild(graph, pool, parentId, childName, filePath); |
|
| 322 | 344 | } |
|
| 323 | 345 | ||
| 324 | 346 | /// Allocate a fresh entry in the graph. |
|
| 325 | - | unsafe fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 347 | + | unsafe fn allocModule 'permission (graph: &mut ModuleGraph 'permission, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 326 | 348 | if graph.entriesLen >= graph.entries.len { |
|
| 327 | 349 | throw ModuleError::CapacityExceeded; |
|
| 328 | 350 | } |
|
| 329 | 351 | let idx = graph.entriesLen; |
|
| 330 | 352 | let arena = graph.arena else panic; |
|
| 331 | 353 | let state = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleUpdates), @alignOf(ModuleUpdates)) as *mut ModuleUpdates; |
|
| 332 | - | set *state = ModuleUpdates { |
|
| 333 | - | state: ModuleState::Registered, |
|
| 334 | - | children: [0; MAX_MODULES], |
|
| 335 | - | childrenLen: 0, |
|
| 336 | - | ast: nil, |
|
| 337 | - | source: nil, |
|
| 338 | - | }; |
|
| 339 | - | let updates = &cell *state; |
|
| 354 | + | let updates: *cell 'permission ModuleUpdates = &cell 'permission *state; |
|
| 355 | + | let authority: 'loan = &mut *graph.permission, value = &mut *updates in { |
|
| 356 | + | set *value = ModuleUpdates { |
|
| 357 | + | state: ModuleState::Registered, |
|
| 358 | + | children: [0; MAX_MODULES], |
|
| 359 | + | childrenLen: 0, |
|
| 360 | + | ast: nil, |
|
| 361 | + | source: nil, |
|
| 362 | + | }; |
|
| 363 | + | } |
|
| 364 | + | set graph.updates[idx] = updates; |
|
| 340 | 365 | ||
| 341 | 366 | // TODO: This is a common pattern that needs better syntax. |
|
| 342 | 367 | let m = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleEntry), @alignOf(ModuleEntry)) as *mut ModuleEntry; |
|
| 343 | 368 | set *m = ModuleEntry { |
|
| 344 | 369 | id: idx as u16, |
| 347 | 372 | filePath, |
|
| 348 | 373 | dirLen: dirLength(filePath), |
|
| 349 | 374 | name: strings::intern(pool, name), |
|
| 350 | 375 | path: [""; MAX_MODULE_PATH_DEPTH], |
|
| 351 | 376 | pathDepth: 0, |
|
| 352 | - | updates, |
|
| 353 | 377 | }; |
|
| 354 | 378 | ||
| 355 | 379 | return m; |
|
| 356 | 380 | } |
|
| 357 | 381 |
| 363 | 387 | set entry.path[entry.pathDepth] = segment; |
|
| 364 | 388 | set entry.pathDepth += 1; |
|
| 365 | 389 | } |
|
| 366 | 390 | ||
| 367 | 391 | /// Append a child identifier to the parent's child list. |
|
| 368 | - | fn addChild(parent: *ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 369 | - | let mut updates = *parent.updates; |
|
| 370 | - | if updates.childrenLen >= updates.children.len { |
|
| 371 | - | throw ModuleError::CapacityExceeded; |
|
| 392 | + | fn addChild 'permission (graph: &mut ModuleGraph 'permission, parent: *ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 393 | + | let cell = updatesFor(graph, parent.id); |
|
| 394 | + | let authority: 'loan = &mut *graph.permission, updates = &mut *cell in { |
|
| 395 | + | if updates.childrenLen >= updates.children.len { |
|
| 396 | + | throw ModuleError::CapacityExceeded; |
|
| 397 | + | } |
|
| 398 | + | set updates.children[updates.childrenLen] = childId; |
|
| 399 | + | set updates.childrenLen += 1; |
|
| 372 | 400 | } |
|
| 373 | - | set updates.children[updates.childrenLen] = childId; |
|
| 374 | - | set updates.childrenLen += 1; |
|
| 375 | - | set *parent.updates = updates; |
|
| 376 | 401 | ||
| 377 | 402 | return childId; |
|
| 378 | 403 | } |
|
| 379 | 404 | ||
| 380 | 405 | /// Publish a fully initialized module entry in identifier order. |
|
| 381 | - | fn publish(graph: &mut ModuleGraph, entry: *ModuleEntry) { |
|
| 406 | + | fn publish 'permission (graph: &mut ModuleGraph 'permission, entry: *ModuleEntry) { |
|
| 382 | 407 | assert entry.id as u32 == graph.entriesLen; |
|
| 383 | 408 | set graph.entries[graph.entriesLen] = entry; |
|
| 384 | 409 | set graph.entriesLen += 1; |
|
| 385 | 410 | } |
|
| 386 | 411 | ||
| 387 | 412 | /// Return the number of registered children. |
|
| 388 | - | export fn childCount(entry: *ModuleEntry) -> u32 { |
|
| 389 | - | return (*entry.updates).childrenLen; |
|
| 413 | + | export fn childCount 'permission (graph: &ModuleGraph 'permission, entry: *ModuleEntry) -> u32 { |
|
| 414 | + | let cell = updatesFor(graph, entry.id); |
|
| 415 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 416 | + | return updates.childrenLen; |
|
| 417 | + | } |
|
| 390 | 418 | } |
|
| 391 | 419 | ||
| 392 | 420 | /// Return the current parsed root for an entry. |
|
| 393 | - | export fn astFor(entry: *ModuleEntry) -> ?*ast::Node { |
|
| 394 | - | return (*entry.updates).ast; |
|
| 421 | + | export fn astFor 'permission (graph: &ModuleGraph 'permission, entry: *ModuleEntry) -> ?*ast::Node { |
|
| 422 | + | let cell = updatesFor(graph, entry.id); |
|
| 423 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 424 | + | return updates.ast; |
|
| 425 | + | } |
|
| 395 | 426 | } |
|
| 396 | 427 | ||
| 397 | 428 | /// Return the current source text for an entry. |
|
| 398 | - | export fn sourceFor(entry: *ModuleEntry) -> ?*[u8] { |
|
| 399 | - | return (*entry.updates).source; |
|
| 429 | + | export fn sourceFor 'permission (graph: &ModuleGraph 'permission, entry: *ModuleEntry) -> ?*[u8] { |
|
| 430 | + | let cell = updatesFor(graph, entry.id); |
|
| 431 | + | let authority: 'loan = &*graph.permission, updates = &*cell in { |
|
| 432 | + | return updates.source; |
|
| 433 | + | } |
|
| 434 | + | } |
|
| 435 | + | ||
| 436 | + | /// Return the permission-associated update cell for `id`. |
|
| 437 | + | fn updatesFor 'permission (graph: &ModuleGraph 'permission, id: u16) -> *cell 'permission ModuleUpdates { |
|
| 438 | + | assert isValidId(graph, id), "updatesFor: module identifier is valid"; |
|
| 439 | + | let cell = graph.updates[id as u32] else { |
|
| 440 | + | panic "updatesFor: missing update cell"; |
|
| 441 | + | }; |
|
| 442 | + | return cell; |
|
| 400 | 443 | } |
|
| 401 | 444 | ||
| 402 | 445 | /// Check if `id` points at an allocated entry. |
|
| 403 | - | fn isValidId(graph: &ModuleGraph, id: u16) -> bool { |
|
| 446 | + | fn isValidId 'permission (graph: &ModuleGraph 'permission, id: u16) -> bool { |
|
| 404 | 447 | return (id as u32) < graph.entriesLen; |
|
| 405 | 448 | } |
|
| 406 | 449 | ||
| 407 | 450 | /// Return the length of the directory prefix for `path`. |
|
| 408 | 451 | /// Return zero if the path has no separator. |
lib/std/lang/module/printer.rad
+8 -7
| 27 | 27 | case super::ModuleState::Ready => return sexpr::sym("ready"), |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Recursively convert a module entry and its descendants to an S-expression. |
|
| 32 | - | unsafe fn subtreeToExpr( |
|
| 32 | + | unsafe fn subtreeToExpr 'permission ( |
|
| 33 | 33 | a: &mut alloc::Arena, |
|
| 34 | - | graph: &super::ModuleGraph, |
|
| 34 | + | graph: &super::ModuleGraph 'permission, |
|
| 35 | 35 | entry: *super::ModuleEntry |
|
| 36 | 36 | ) -> sexpr::Expr { |
|
| 37 | 37 | let idText = formatId(a, entry.id as u32); |
|
| 38 | 38 | let path = super::moduleQualifiedPath(entry); |
|
| 39 | 39 | let mut pathBuf: *[sexpr::Expr] = &[]; |
| 42 | 42 | for seg, i in path { set buf[i] = sexpr::sym(seg); } |
|
| 43 | 43 | set pathBuf = buf; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | let mut childBuf: *[sexpr::Expr] = &[]; |
|
| 47 | - | let childCount = super::childCount(entry); |
|
| 47 | + | let childCount = super::childCount(graph, entry); |
|
| 48 | 48 | if childCount > 0 { |
|
| 49 | 49 | let buf = try! sexpr::allocExprs(a, childCount); |
|
| 50 | 50 | for i in 0..childCount { |
|
| 51 | - | if let child = super::get(graph, super::childAt(entry, i)) { |
|
| 51 | + | if let child = super::get(graph, super::childAt(graph, entry, i)) { |
|
| 52 | 52 | set buf[i] = subtreeToExpr(a, graph, child); |
|
| 53 | 53 | } |
|
| 54 | 54 | } |
|
| 55 | 55 | set childBuf = buf; |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | + | let state = try! super::state(graph, entry.id); |
|
| 58 | 59 | return sexpr::block(a, "module", &[ |
|
| 59 | 60 | sexpr::sym(entry.name), |
|
| 60 | 61 | sexpr::list(a, "id", &[sexpr::sym(idText)]), |
|
| 61 | - | sexpr::list(a, "state", &[stateToExpr((*entry.updates).state)]), |
|
| 62 | + | sexpr::list(a, "state", &[stateToExpr(state)]), |
|
| 62 | 63 | sexpr::Expr::Str(entry.filePath), |
|
| 63 | 64 | sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false } |
|
| 64 | 65 | ], childBuf); |
|
| 65 | 66 | } |
|
| 66 | 67 | ||
| 67 | 68 | /// Print the entire module graph in S-expression format. |
|
| 68 | - | export unsafe fn printGraph(graph: &super::ModuleGraph, arena: &mut alloc::Arena) { |
|
| 69 | + | export unsafe fn printGraph 'permission (graph: &super::ModuleGraph 'permission, arena: &mut alloc::Arena) { |
|
| 69 | 70 | // Print all root modules. |
|
| 70 | - | for i in 0..graph.entriesLen { |
|
| 71 | + | for i in 0..super::entryCount(graph) { |
|
| 71 | 72 | let entry = super::get(graph, i as u16) else panic; |
|
| 72 | 73 | if entry.parent == nil { |
|
| 73 | 74 | sexpr::print(subtreeToExpr(arena, graph, entry), 0); |
|
| 74 | 75 | io::print("\n"); |
|
| 75 | 76 | } |
lib/std/lang/module/tests.rad
+224 -191
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | @test unsafe fn testRegisterChildren() throws (testing::TestError) { |
|
| 53 | 53 | static storage: [?*super::ModuleEntry; 4] = [nil; 4]; |
|
| 54 | 54 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 55 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 56 | - | ||
| 57 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch { |
|
| 58 | - | throw testing::TestError::Failed; |
|
| 59 | - | }; |
|
| 60 | - | let root = super::get(&graph, rootId) else { |
|
| 61 | - | throw testing::TestError::Failed; |
|
| 62 | - | }; |
|
| 63 | - | try expectPathSegments(root, &["root"]); |
|
| 64 | - | ||
| 65 | - | // First child. |
|
| 66 | - | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/first.rad") catch { |
|
| 67 | - | throw testing::TestError::Failed; |
|
| 68 | - | }; |
|
| 69 | - | let first = super::get(&graph, firstId) else { |
|
| 70 | - | throw testing::TestError::Failed; |
|
| 71 | - | }; |
|
| 72 | - | try expectSliceEq(first.filePath, "src/root/first.rad"); |
|
| 73 | - | try expectSliceEq(first.name, "first"); |
|
| 74 | - | try expectPathSegments(first, &["root", "first"]); |
|
| 75 | - | ||
| 76 | - | // Second child. |
|
| 77 | - | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/second.rad") catch { |
|
| 78 | - | throw testing::TestError::Failed; |
|
| 79 | - | }; |
|
| 80 | - | let second = super::get(&graph, secondId) else { |
|
| 81 | - | throw testing::TestError::Failed; |
|
| 82 | - | }; |
|
| 83 | - | try expectSliceEq(second.filePath, "src/root/second.rad"); |
|
| 84 | - | try expectSliceEq(second.name, "second"); |
|
| 85 | - | try expectPathSegments(second, &["root", "second"]); |
|
| 86 | - | ||
| 87 | - | let parent = super::get(&graph, rootId) else { |
|
| 88 | - | throw testing::TestError::Failed; |
|
| 89 | - | }; |
|
| 90 | - | try testing::expect(graph.entriesLen == 3); |
|
| 91 | - | try testing::expect(super::childCount(parent) == 2); |
|
| 92 | - | try testing::expect(super::childAt(parent, 0) == firstId); |
|
| 93 | - | try testing::expect(super::childAt(parent, 1) == secondId); |
|
| 55 | + | let mut owner = super::Permission {}; |
|
| 56 | + | let permission: 'permission = &mut owner in { |
|
| 57 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 58 | + | ||
| 59 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch { |
|
| 60 | + | throw testing::TestError::Failed; |
|
| 61 | + | }; |
|
| 62 | + | let root = super::get(&graph, rootId) else { |
|
| 63 | + | throw testing::TestError::Failed; |
|
| 64 | + | }; |
|
| 65 | + | try expectPathSegments(root, &["root"]); |
|
| 66 | + | ||
| 67 | + | // First child. |
|
| 68 | + | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/first.rad") catch { |
|
| 69 | + | throw testing::TestError::Failed; |
|
| 70 | + | }; |
|
| 71 | + | let first = super::get(&graph, firstId) else { |
|
| 72 | + | throw testing::TestError::Failed; |
|
| 73 | + | }; |
|
| 74 | + | try expectSliceEq(first.filePath, "src/root/first.rad"); |
|
| 75 | + | try expectSliceEq(first.name, "first"); |
|
| 76 | + | try expectPathSegments(first, &["root", "first"]); |
|
| 77 | + | ||
| 78 | + | // Second child. |
|
| 79 | + | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/root/second.rad") catch { |
|
| 80 | + | throw testing::TestError::Failed; |
|
| 81 | + | }; |
|
| 82 | + | let second = super::get(&graph, secondId) else { |
|
| 83 | + | throw testing::TestError::Failed; |
|
| 84 | + | }; |
|
| 85 | + | try expectSliceEq(second.filePath, "src/root/second.rad"); |
|
| 86 | + | try expectSliceEq(second.name, "second"); |
|
| 87 | + | try expectPathSegments(second, &["root", "second"]); |
|
| 88 | + | ||
| 89 | + | let parent = super::get(&graph, rootId) else { |
|
| 90 | + | throw testing::TestError::Failed; |
|
| 91 | + | }; |
|
| 92 | + | try testing::expect(super::entryCount(&graph) == 3); |
|
| 93 | + | try testing::expect(super::childCount(&graph, parent) == 2); |
|
| 94 | + | try testing::expect(super::childAt(&graph, parent, 0) == firstId); |
|
| 95 | + | try testing::expect(super::childAt(&graph, parent, 1) == secondId); |
|
| 96 | + | } |
|
| 94 | 97 | } |
|
| 95 | 98 | ||
| 96 | 99 | @test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) { |
|
| 97 | 100 | static storage: [?*super::ModuleEntry; 4] = [nil; 4]; |
|
| 98 | 101 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 99 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 100 | - | ||
| 101 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch { |
|
| 102 | - | throw testing::TestError::Failed; |
|
| 103 | - | }; |
|
| 104 | - | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 105 | - | throw testing::TestError::Failed; |
|
| 106 | - | }; |
|
| 107 | - | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 108 | - | throw testing::TestError::Failed; |
|
| 109 | - | }; |
|
| 110 | - | try testing::expect(firstId == secondId); |
|
| 111 | - | ||
| 112 | - | let parent = super::get(&graph, rootId) else { |
|
| 113 | - | throw testing::TestError::Failed; |
|
| 114 | - | }; |
|
| 115 | - | try testing::expect(graph.entriesLen == 2); |
|
| 116 | - | try testing::expect(super::childCount(parent) == 1); |
|
| 102 | + | let mut owner = super::Permission {}; |
|
| 103 | + | let permission: 'permission = &mut owner in { |
|
| 104 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 105 | + | ||
| 106 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch { |
|
| 107 | + | throw testing::TestError::Failed; |
|
| 108 | + | }; |
|
| 109 | + | let firstId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 110 | + | throw testing::TestError::Failed; |
|
| 111 | + | }; |
|
| 112 | + | let secondId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/main/util.rad") catch { |
|
| 113 | + | throw testing::TestError::Failed; |
|
| 114 | + | }; |
|
| 115 | + | try testing::expect(firstId == secondId); |
|
| 116 | + | ||
| 117 | + | let parent = super::get(&graph, rootId) else { |
|
| 118 | + | throw testing::TestError::Failed; |
|
| 119 | + | }; |
|
| 120 | + | try testing::expect(super::entryCount(&graph) == 2); |
|
| 121 | + | try testing::expect(super::childCount(&graph, parent) == 1); |
|
| 122 | + | } |
|
| 117 | 123 | } |
|
| 118 | 124 | ||
| 119 | 125 | @test fn testTrimExtensionWithRadExtension() throws (testing::TestError) { |
|
| 120 | 126 | let input = "parser.rad"; |
|
| 121 | 127 | let result = super::trimExtension(input) |
| 165 | 171 | } |
|
| 166 | 172 | ||
| 167 | 173 | @test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) { |
|
| 168 | 174 | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 169 | 175 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 170 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 171 | - | ||
| 172 | - | // Register root module. |
|
| 173 | - | let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 174 | - | throw testing::TestError::Failed; |
|
| 175 | - | }; |
|
| 176 | - | let std = super::get(&graph, stdId) else { |
|
| 177 | - | throw testing::TestError::Failed; |
|
| 178 | - | }; |
|
| 179 | - | try expectSliceEq(std.filePath, "lib/std.rad"); |
|
| 180 | - | try expectSliceEq(super::moduleDir(std), "lib/"); |
|
| 181 | - | try expectPathSegments(std, &["std"]); |
|
| 182 | - | ||
| 183 | - | // Register child of root. |
|
| 184 | - | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang.rad") catch { |
|
| 185 | - | throw testing::TestError::Failed; |
|
| 186 | - | }; |
|
| 187 | - | let lang = super::get(&graph, langId) else { |
|
| 188 | - | throw testing::TestError::Failed; |
|
| 189 | - | }; |
|
| 190 | - | try expectSliceEq(lang.filePath, "lib/std/lang.rad"); |
|
| 191 | - | try expectSliceEq(lang.name, "lang"); |
|
| 192 | - | try expectSliceEq(super::moduleDir(lang), "lib/std/"); |
|
| 193 | - | try expectPathSegments(lang, &["std", "lang"]); |
|
| 194 | - | ||
| 195 | - | // Register grandchild. |
|
| 196 | - | let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang/parser.rad") catch { |
|
| 197 | - | throw testing::TestError::Failed; |
|
| 198 | - | }; |
|
| 199 | - | let parser = super::get(&graph, parserId) else { |
|
| 200 | - | throw testing::TestError::Failed; |
|
| 201 | - | }; |
|
| 202 | - | try expectSliceEq(parser.filePath, "lib/std/lang/parser.rad"); |
|
| 203 | - | try expectSliceEq(parser.name, "parser"); |
|
| 204 | - | try expectSliceEq(super::moduleDir(parser), "lib/std/lang/"); |
|
| 205 | - | try expectPathSegments(parser, &["std", "lang", "parser"]); |
|
| 206 | - | ||
| 207 | - | // Verify parent-child relationships. |
|
| 208 | - | try testing::expect(graph.entriesLen == 3); |
|
| 209 | - | try testing::expect(super::childCount(std) == 1); |
|
| 210 | - | try testing::expect(super::childAt(std, 0) == langId); |
|
| 211 | - | try testing::expect(super::childCount(lang) == 1); |
|
| 212 | - | try testing::expect(super::childAt(lang, 0) == parserId); |
|
| 213 | - | try testing::expect(super::childCount(parser) == 0); |
|
| 176 | + | let mut owner = super::Permission {}; |
|
| 177 | + | let permission: 'permission = &mut owner in { |
|
| 178 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 179 | + | ||
| 180 | + | // Register root module. |
|
| 181 | + | let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 182 | + | throw testing::TestError::Failed; |
|
| 183 | + | }; |
|
| 184 | + | let std = super::get(&graph, stdId) else { |
|
| 185 | + | throw testing::TestError::Failed; |
|
| 186 | + | }; |
|
| 187 | + | try expectSliceEq(std.filePath, "lib/std.rad"); |
|
| 188 | + | try expectSliceEq(super::moduleDir(std), "lib/"); |
|
| 189 | + | try expectPathSegments(std, &["std"]); |
|
| 190 | + | ||
| 191 | + | // Register child of root. |
|
| 192 | + | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang.rad") catch { |
|
| 193 | + | throw testing::TestError::Failed; |
|
| 194 | + | }; |
|
| 195 | + | let lang = super::get(&graph, langId) else { |
|
| 196 | + | throw testing::TestError::Failed; |
|
| 197 | + | }; |
|
| 198 | + | try expectSliceEq(lang.filePath, "lib/std/lang.rad"); |
|
| 199 | + | try expectSliceEq(lang.name, "lang"); |
|
| 200 | + | try expectSliceEq(super::moduleDir(lang), "lib/std/"); |
|
| 201 | + | try expectPathSegments(lang, &["std", "lang"]); |
|
| 202 | + | ||
| 203 | + | // Register grandchild. |
|
| 204 | + | let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, stdId, "lib/std/lang/parser.rad") catch { |
|
| 205 | + | throw testing::TestError::Failed; |
|
| 206 | + | }; |
|
| 207 | + | let parser = super::get(&graph, parserId) else { |
|
| 208 | + | throw testing::TestError::Failed; |
|
| 209 | + | }; |
|
| 210 | + | try expectSliceEq(parser.filePath, "lib/std/lang/parser.rad"); |
|
| 211 | + | try expectSliceEq(parser.name, "parser"); |
|
| 212 | + | try expectSliceEq(super::moduleDir(parser), "lib/std/lang/"); |
|
| 213 | + | try expectPathSegments(parser, &["std", "lang", "parser"]); |
|
| 214 | + | ||
| 215 | + | // Verify parent-child relationships. |
|
| 216 | + | try testing::expect(super::entryCount(&graph) == 3); |
|
| 217 | + | try testing::expect(super::childCount(&graph, std) == 1); |
|
| 218 | + | try testing::expect(super::childAt(&graph, std, 0) == langId); |
|
| 219 | + | try testing::expect(super::childCount(&graph, lang) == 1); |
|
| 220 | + | try testing::expect(super::childAt(&graph, lang, 0) == parserId); |
|
| 221 | + | try testing::expect(super::childCount(&graph, parser) == 0); |
|
| 222 | + | } |
|
| 214 | 223 | } |
|
| 215 | 224 | ||
| 216 | 225 | @test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) { |
|
| 217 | 226 | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 218 | 227 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 219 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 220 | - | ||
| 221 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 228 | + | let mut owner = super::Permission {}; |
|
| 229 | + | let permission: 'permission = &mut owner in { |
|
| 230 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 231 | + | ||
| 232 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 233 | + | throw testing::TestError::Failed; |
|
| 234 | + | }; |
|
| 235 | + | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "std/lang/parser.rad") catch { |
|
| 236 | + | // Expected to fail due to missing intermediate parent. |
|
| 237 | + | return; |
|
| 238 | + | }; |
|
| 222 | 239 | throw testing::TestError::Failed; |
|
| 223 | - | }; |
|
| 224 | - | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "std/lang/parser.rad") catch { |
|
| 225 | - | // Expected to fail due to missing intermediate parent. |
|
| 226 | - | return; |
|
| 227 | - | }; |
|
| 228 | - | throw testing::TestError::Failed; |
|
| 240 | + | } |
|
| 229 | 241 | } |
|
| 230 | 242 | ||
| 231 | 243 | @test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) { |
|
| 232 | 244 | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 233 | 245 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 234 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 235 | - | ||
| 236 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 246 | + | let mut owner = super::Permission {}; |
|
| 247 | + | let permission: 'permission = &mut owner in { |
|
| 248 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 249 | + | ||
| 250 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 251 | + | throw testing::TestError::Failed; |
|
| 252 | + | }; |
|
| 253 | + | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "parser.rad") catch { |
|
| 254 | + | // Expected to fail due to missing intermediate parent. |
|
| 255 | + | return; |
|
| 256 | + | }; |
|
| 237 | 257 | throw testing::TestError::Failed; |
|
| 238 | - | }; |
|
| 239 | - | let _ = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "parser.rad") catch { |
|
| 240 | - | // Expected to fail due to missing intermediate parent. |
|
| 241 | - | return; |
|
| 242 | - | }; |
|
| 243 | - | throw testing::TestError::Failed; |
|
| 258 | + | } |
|
| 244 | 259 | } |
|
| 245 | 260 | ||
| 246 | 261 | @test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) { |
|
| 247 | 262 | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 248 | 263 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 249 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 250 | - | ||
| 251 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 252 | - | throw testing::TestError::Failed; |
|
| 253 | - | }; |
|
| 254 | - | // Verify root module was registered with correct ID. |
|
| 255 | - | try testing::expect(rootId == 0); |
|
| 256 | - | ||
| 257 | - | let root = super::get(&graph, rootId) else { |
|
| 258 | - | throw testing::TestError::Failed; |
|
| 259 | - | }; |
|
| 260 | - | try expectSliceEq(root.name, "std"); |
|
| 261 | - | try expectSliceEq(root.filePath, "lib/std.rad"); |
|
| 262 | - | try expectPathSegments(root, &["std"]); |
|
| 264 | + | let mut owner = super::Permission {}; |
|
| 265 | + | let permission: 'permission = &mut owner in { |
|
| 266 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 267 | + | ||
| 268 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 269 | + | throw testing::TestError::Failed; |
|
| 270 | + | }; |
|
| 271 | + | // Verify root module was registered with correct ID. |
|
| 272 | + | try testing::expect(rootId == 0); |
|
| 273 | + | ||
| 274 | + | let root = super::get(&graph, rootId) else { |
|
| 275 | + | throw testing::TestError::Failed; |
|
| 276 | + | }; |
|
| 277 | + | try expectSliceEq(root.name, "std"); |
|
| 278 | + | try expectSliceEq(root.filePath, "lib/std.rad"); |
|
| 279 | + | try expectPathSegments(root, &["std"]); |
|
| 280 | + | } |
|
| 263 | 281 | } |
|
| 264 | 282 | ||
| 265 | 283 | @test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) { |
|
| 266 | 284 | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 267 | 285 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 268 | - | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 269 | - | ||
| 270 | - | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch { |
|
| 271 | - | throw testing::TestError::Failed; |
|
| 272 | - | }; |
|
| 273 | - | // Verify root module was registered with correct ID. |
|
| 274 | - | try testing::expect(rootId == 0); |
|
| 275 | - | ||
| 276 | - | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang.rad") catch { |
|
| 277 | - | throw testing::TestError::Failed; |
|
| 278 | - | }; |
|
| 279 | - | let lang = super::get(&graph, langId) else { |
|
| 280 | - | throw testing::TestError::Failed; |
|
| 281 | - | }; |
|
| 282 | - | try expectSliceEq(lang.name, "lang"); |
|
| 283 | - | try expectPathSegments(lang, &["root", "lang"]); |
|
| 284 | - | ||
| 285 | - | let parserId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang/parser.rad") catch { |
|
| 286 | - | throw testing::TestError::Failed; |
|
| 287 | - | }; |
|
| 288 | - | let parser = super::get(&graph, parserId) else { |
|
| 289 | - | throw testing::TestError::Failed; |
|
| 290 | - | }; |
|
| 291 | - | try expectSliceEq(parser.name, "parser"); |
|
| 292 | - | try expectPathSegments(parser, &["root", "lang", "parser"]); |
|
| 286 | + | let mut owner = super::Permission {}; |
|
| 287 | + | let permission: 'permission = &mut owner in { |
|
| 288 | + | let mut graph = super::moduleGraph(&mut storage[..], &mut arena, permission); |
|
| 289 | + | ||
| 290 | + | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch { |
|
| 291 | + | throw testing::TestError::Failed; |
|
| 292 | + | }; |
|
| 293 | + | // Verify root module was registered with correct ID. |
|
| 294 | + | try testing::expect(rootId == 0); |
|
| 295 | + | ||
| 296 | + | let langId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang.rad") catch { |
|
| 297 | + | throw testing::TestError::Failed; |
|
| 298 | + | }; |
|
| 299 | + | let lang = super::get(&graph, langId) else { |
|
| 300 | + | throw testing::TestError::Failed; |
|
| 301 | + | }; |
|
| 302 | + | try expectSliceEq(lang.name, "lang"); |
|
| 303 | + | try expectPathSegments(lang, &["root", "lang"]); |
|
| 304 | + | ||
| 305 | + | let parserId = try super::registerFromPath( |
|
| 306 | + | &mut graph, &mut STRING_POOL, 0, rootId, "src/pkg/root/lang/parser.rad" |
|
| 307 | + | ) catch { |
|
| 308 | + | throw testing::TestError::Failed; |
|
| 309 | + | }; |
|
| 310 | + | let parser = super::get(&graph, parserId) else { |
|
| 311 | + | throw testing::TestError::Failed; |
|
| 312 | + | }; |
|
| 313 | + | try expectSliceEq(parser.name, "parser"); |
|
| 314 | + | try expectPathSegments(parser, &["root", "lang", "parser"]); |
|
| 315 | + | } |
|
| 293 | 316 | } |
|
| 317 | + | ||
| 294 | 318 | /// Move the graph owner while its published entries retain their identities. |
|
| 295 | - | fn relocate(graph: super::ModuleGraph) -> super::ModuleGraph { |
|
| 319 | + | fn relocate 'permission ( |
|
| 320 | + | graph: super::ModuleGraph 'permission |
|
| 321 | + | ) -> super::ModuleGraph 'permission { |
|
| 296 | 322 | return graph; |
|
| 297 | 323 | } |
|
| 324 | + | ||
| 298 | 325 | @test unsafe fn testGraphRelocation() throws (testing::TestError) { |
|
| 299 | 326 | static DATA: [u8; 16384] = [0; 16384]; |
|
| 300 | 327 | static ENTRIES: [?*super::ModuleEntry; 2] = [nil; 2]; |
|
| 301 | 328 | static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 302 | 329 | let mut arena = ast::nodeArena(&mut DATA[..]); |
|
| 303 | - | let mut original = super::moduleGraph(&mut ENTRIES[..], &mut arena); |
|
| 304 | - | let rootId = try! super::registerRoot(&mut original, &mut POOL, 0, "root.rad"); |
|
| 305 | - | let root = super::get(&original, rootId) else panic; |
|
| 306 | - | let alias = root; |
|
| 307 | - | let mut graph = relocate(original); |
|
| 308 | - | let child = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 309 | - | try testing::expect(super::childCount(root) == 1); |
|
| 310 | - | try testing::expect(super::childAt(alias, 0) == child); |
|
| 311 | - | let published = super::get(&graph, rootId) else panic; |
|
| 312 | - | try testing::expect(published as u64 == root as u64); |
|
| 313 | - | let saved = alloc::used(&arena.arena); |
|
| 314 | - | let reused = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 315 | - | try testing::expect(reused == child); |
|
| 316 | - | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 317 | - | let mut failed = false; |
|
| 318 | - | try super::registerChild(&mut graph, &mut POOL, rootId, "full", "root/full.rad") catch { |
|
| 319 | - | set failed = true; |
|
| 320 | - | }; |
|
| 321 | - | try testing::expect(failed); |
|
| 322 | - | try testing::expect(graph.entriesLen == 2); |
|
| 323 | - | try testing::expect(super::childCount(alias) == 1); |
|
| 324 | - | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 325 | - | let source = "fn answer() -> u32 { return 42; }"; |
|
| 326 | - | let parsed: *ast::Node = try! parser::parse(scanner::SourceLoc::String, source, &mut arena, &mut POOL); |
|
| 327 | - | try! super::setAst(&mut graph, rootId, parsed); |
|
| 328 | - | try! super::setSource(&mut graph, rootId, source); |
|
| 329 | - | let retainedAst = super::astFor(alias) else panic; |
|
| 330 | - | try testing::expect(retainedAst as u64 == parsed as u64); |
|
| 331 | - | let retainedSource = super::sourceFor(root) else panic; |
|
| 332 | - | try testing::expect(mem::eq(retainedSource, source)); |
|
| 333 | - | try testing::expect(try! super::state(&graph, rootId) == super::ModuleState::Parsed); |
|
| 334 | - | ||
| 330 | + | let mut owner = super::Permission {}; |
|
| 331 | + | let permission: 'permission = &mut owner in { |
|
| 332 | + | let mut original = super::moduleGraph(&mut ENTRIES[..], &mut arena, permission); |
|
| 333 | + | let rootId = try! super::registerRoot(&mut original, &mut POOL, 0, "root.rad"); |
|
| 334 | + | let root = super::get(&original, rootId) else panic; |
|
| 335 | + | let alias = root; |
|
| 336 | + | let mut graph = relocate(original); |
|
| 337 | + | let child = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 338 | + | try testing::expect(super::childCount(&graph, root) == 1); |
|
| 339 | + | try testing::expect(super::childAt(&graph, alias, 0) == child); |
|
| 340 | + | let published = super::get(&graph, rootId) else panic; |
|
| 341 | + | try testing::expect(published as u64 == root as u64); |
|
| 342 | + | let saved = alloc::used(&arena.arena); |
|
| 343 | + | let reused = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 344 | + | try testing::expect(reused == child); |
|
| 345 | + | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 346 | + | let mut failed = false; |
|
| 347 | + | try super::registerChild(&mut graph, &mut POOL, rootId, "full", "root/full.rad") catch { |
|
| 348 | + | set failed = true; |
|
| 349 | + | }; |
|
| 350 | + | try testing::expect(failed); |
|
| 351 | + | try testing::expect(super::entryCount(&graph) == 2); |
|
| 352 | + | try testing::expect(super::childCount(&graph, alias) == 1); |
|
| 353 | + | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 354 | + | let source = "fn answer() -> u32 { return 42; }"; |
|
| 355 | + | let parsed: *ast::Node = try! parser::parse( |
|
| 356 | + | scanner::SourceLoc::String, source, &mut arena, &mut POOL |
|
| 357 | + | ); |
|
| 358 | + | try! super::setAst(&mut graph, rootId, parsed); |
|
| 359 | + | try! super::setSource(&mut graph, rootId, source); |
|
| 360 | + | let retainedAst = super::astFor(&graph, alias) else panic; |
|
| 361 | + | try testing::expect(retainedAst as u64 == parsed as u64); |
|
| 362 | + | let retainedSource = super::sourceFor(&graph, root) else panic; |
|
| 363 | + | try testing::expect(mem::eq(retainedSource, source)); |
|
| 364 | + | try testing::expect( |
|
| 365 | + | try! super::state(&graph, rootId) == super::ModuleState::Parsed |
|
| 366 | + | ); |
|
| 367 | + | } |
|
| 335 | 368 | } |
lib/std/lang/package.rad
+1 -1
| 31 | 31 | set pkg.rootModuleId = nil; |
|
| 32 | 32 | ||
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Register a module described by the file path. |
|
| 36 | - | export unsafe fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16 |
|
| 36 | + | export unsafe fn registerModule 'permission (pkg: &mut Package, graph: &mut module::ModuleGraph 'permission, pool: &mut strings::Pool, filePath: *[u8]) -> u16 |
|
| 37 | 37 | throws (module::ModuleError) |
|
| 38 | 38 | { |
|
| 39 | 39 | let modId = try module::registerFromPath(graph, pool, pkg.id, pkg.rootModuleId, filePath); |
|
| 40 | 40 | // First registered module becomes the root. |
|
| 41 | 41 | if pkg.rootModuleId == nil { |
lib/std/lang/resolver.rad
+18 -10
| 1167 | 1167 | types: *unsafe mut [?*TypeNode], |
|
| 1168 | 1168 | /// Diagnostics recorded so far. |
|
| 1169 | 1169 | errors: DiagnosticBuffer, |
|
| 1170 | 1170 | /// Stable module identities indexed by module ID. |
|
| 1171 | 1171 | moduleEntries: [?*module::ModuleEntry; module::MAX_MODULES], |
|
| 1172 | + | /// Parsed roots captured while module update authority is available. |
|
| 1173 | + | moduleRoots: [?*ast::Node; module::MAX_MODULES], |
|
| 1172 | 1174 | /// Cache of module scopes indexed by module ID. |
|
| 1173 | 1175 | moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES], |
|
| 1174 | 1176 | /// Trait instance registry. |
|
| 1175 | 1177 | instances: [InstanceEntry; MAX_INSTANCES], |
|
| 1176 | 1178 | /// Number of registered instances. |
| 1791 | 1793 | arena, |
|
| 1792 | 1794 | nodeData: NodeDataTable { entries: nodeData }, |
|
| 1793 | 1795 | types, |
|
| 1794 | 1796 | errors: DiagnosticBuffer { entries: errors, len: 0 }, |
|
| 1795 | 1797 | moduleEntries: [nil; module::MAX_MODULES], |
|
| 1798 | + | moduleRoots: [nil; module::MAX_MODULES], |
|
| 1796 | 1799 | moduleScopes, |
|
| 1797 | 1800 | instances: undefined, |
|
| 1798 | 1801 | instancesLen: 0, |
|
| 1799 | 1802 | methods: undefined, |
|
| 1800 | 1803 | methodsLen: 0, |
| 1898 | 1901 | ||
| 1899 | 1902 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
|
| 1900 | 1903 | unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1901 | 1904 | let modEntry = findChildModule(self, name, self.currentMod) |
|
| 1902 | 1905 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 1903 | - | let modRoot = module::astFor(modEntry) |
|
| 1906 | + | let modRoot = self.moduleRoots[modEntry.id as u32] |
|
| 1904 | 1907 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
|
| 1905 | 1908 | ||
| 1906 | 1909 | return enterModuleScope(self, modRoot, modEntry); |
|
| 1907 | 1910 | } |
|
| 1908 | 1911 |
| 3816 | 3819 | return self.moduleEntries[id as u32]; |
|
| 3817 | 3820 | } |
|
| 3818 | 3821 | ||
| 3819 | 3822 | /// Find a retained child identity by its parent and name. |
|
| 3820 | 3823 | fn findChildModule 'arena (self: &Resolver 'arena, name: *[u8], parentId: u16) -> ?*module::ModuleEntry { |
|
| 3821 | - | let parent = moduleFor(self, parentId) else return nil; |
|
| 3822 | - | for i in 0..module::childCount(parent) { |
|
| 3823 | - | let child = moduleFor(self, module::childAt(parent, i)) |
|
| 3824 | - | else panic "findChildModule: missing child identity"; |
|
| 3825 | - | if mem::eq(child.name, name) { |
|
| 3826 | - | return child; |
|
| 3824 | + | for child in self.moduleEntries { |
|
| 3825 | + | if let entry = child { |
|
| 3826 | + | if entry.parent == parentId and mem::eq(entry.name, name) { |
|
| 3827 | + | return entry; |
|
| 3828 | + | } |
|
| 3827 | 3829 | } |
|
| 3828 | 3830 | } |
|
| 3829 | 3831 | return nil; |
|
| 3830 | 3832 | } |
|
| 3831 | 3833 |
| 13142 | 13144 | } |
|
| 13143 | 13145 | } |
|
| 13144 | 13146 | ||
| 13145 | 13147 | /// Resolve all packages. |
|
| 13146 | 13148 | /// Module entries retain their identity throughout resolution and diagnostics. |
|
| 13147 | - | export unsafe fn resolve 'arena (self: &mut Resolver 'arena, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 13148 | - | assert graph.entriesLen <= self.moduleEntries.len, "resolve: module registry capacity exceeded"; |
|
| 13149 | + | export unsafe fn resolve 'arena 'permission (self: &mut Resolver 'arena, graph: &module::ModuleGraph 'permission, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 13150 | + | assert module::entryCount(graph) <= self.moduleEntries.len, "resolve: module registry capacity exceeded"; |
|
| 13149 | 13151 | for i in 0..self.moduleEntries.len { |
|
| 13150 | - | set self.moduleEntries[i] = module::get(graph, i as u16); |
|
| 13152 | + | let entry = module::get(graph, i as u16); |
|
| 13153 | + | set self.moduleEntries[i] = entry; |
|
| 13154 | + | if let present = entry { |
|
| 13155 | + | set self.moduleRoots[i] = module::astFor(graph, present); |
|
| 13156 | + | } else { |
|
| 13157 | + | set self.moduleRoots[i] = nil; |
|
| 13158 | + | } |
|
| 13151 | 13159 | } |
|
| 13152 | 13160 | ||
| 13153 | 13161 | // 1. Bind all package roots to enable cross-package references. |
|
| 13154 | 13162 | for i in 0..packages.len { |
|
| 13155 | 13163 | let pkg = packages[i]; |
lib/std/lang/resolver/opaqueTests.rad
+3 -3
| 52 | 52 | let model = try tests::registerModule(&mut tests::MODULE_GRAPH, root, "model", OPAQUE_RECORD_MODEL, &mut arena); |
|
| 53 | 53 | let result = try tests::resolveModuleTree(&mut res, root); |
|
| 54 | 54 | try tests::expectErrorKind(&result, resolver::ErrorKind::OpaqueRecordAccess) catch error { |
|
| 55 | 55 | io::print(program); |
|
| 56 | 56 | io::print("\n"); |
|
| 57 | - | resolver::printer::printDiagnostics(&result.diagnostics, &res); |
|
| 57 | + | tests::printDiagnostics(&result.diagnostics, &res); |
|
| 58 | 58 | throw error; |
|
| 59 | 59 | }; |
|
| 60 | 60 | } |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
| 73 | 73 | let program = "use root::model; fn f(p: model::Value) -> u32 { return p.number; }"; |
|
| 74 | 74 | let nested = try tests::registerModule(&mut tests::MODULE_GRAPH, model, "child", program if child else "", &mut arena); |
|
| 75 | 75 | let client = try tests::registerModule(&mut tests::MODULE_GRAPH, root, "client", "" if child else program, &mut arena); |
|
| 76 | 76 | let result = try tests::resolveModuleTree(&mut res, root); |
|
| 77 | 77 | try tests::expectErrorKind(&result, resolver::ErrorKind::OpaqueRecordAccess) catch error { |
|
| 78 | - | resolver::printer::printDiagnostics(&result.diagnostics, &res); |
|
| 78 | + | tests::printDiagnostics(&result.diagnostics, &res); |
|
| 79 | 79 | throw error; |
|
| 80 | 80 | }; |
|
| 81 | 81 | } |
|
| 82 | 82 | } |
|
| 83 | 83 | } |
| 89 | 89 | let mut res = tests::testResolver(memory); |
|
| 90 | 90 | let result = try tests::resolveProgramStr(&mut res, |
|
| 91 | 91 | "opaque record Value: Copy { number: u32 } opaque record Word: Copy(u32); opaque record View: 'r + Copy { number: &'r cell u32 } fn f() { let mut p = Value { number: 1 }; set p.number += 2; let q: Value = { number: p.number }; let r: ?Value = { number: 4 }; let case Value { number } = q else panic; if let case Value { number: n } = p; n > 0 {} while let case Value { number: n } = p { break; } match p { case Value { number: n } => {} else => {} } let mut w = Word(1); set *w = 2; { let field = &mut *w; set *field = 3; } let case Word(n) = w else panic; } fn g 'r (number: &'r cell u32) -> View 'r { let p = View 'r { number }; set *p.number = 42; let case View 'r { number: value } = p else panic; return { number: value }; }" |
|
| 92 | 92 | ); |
|
| 93 | 93 | try tests::expectNoErrors(&result) catch error { |
|
| 94 | - | resolver::printer::printDiagnostics(&result.diagnostics, &res); |
|
| 94 | + | tests::printDiagnostics(&result.diagnostics, &res); |
|
| 95 | 95 | throw error; |
|
| 96 | 96 | }; |
|
| 97 | 97 | } |
|
| 98 | 98 | } |
lib/std/lang/resolver/printer.rad
+4 -5
| 280 | 280 | printNominalArguments(applied); |
|
| 281 | 281 | } |
|
| 282 | 282 | } |
|
| 283 | 283 | ||
| 284 | 284 | /// Print a single diagnostic entry. |
|
| 285 | - | unsafe fn printError 'arena (err: &super::Error, res: &super::Resolver 'arena) { |
|
| 285 | + | unsafe fn printError 'arena 'permission (err: &super::Error, res: &super::Resolver 'arena, graph: &module::ModuleGraph 'permission) { |
|
| 286 | 286 | if let node = err.node { |
|
| 287 | 287 | // Find the module containing this error. |
|
| 288 | 288 | if let moduleEntry = super::moduleFor(res, err.moduleId) { |
|
| 289 | 289 | // Get the source text if available. |
|
| 290 | - | if let source = module::sourceFor(moduleEntry) { |
|
| 290 | + | if let source = module::sourceFor(graph, moduleEntry) { |
|
| 291 | 291 | // Convert offset to location. |
|
| 292 | 292 | if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) { |
|
| 293 | 293 | // Print: filename:line:col: error: message |
|
| 294 | 294 | if let case scanner::SourceLoc::File(path) = loc.source { |
|
| 295 | 295 | io::print(path); |
| 315 | 315 | } |
|
| 316 | 316 | } |
|
| 317 | 317 | } else { |
|
| 318 | 318 | io::print("error: "); |
|
| 319 | 319 | } |
|
| 320 | - | ||
| 321 | 320 | // Print the error message. |
|
| 322 | 321 | match err.kind { |
|
| 323 | 322 | case super::ErrorKind::TypeMismatch(mismatch) => { |
|
| 324 | 323 | io::print("type mismatch: expected "); |
|
| 325 | 324 | printType(mismatch.expected); |
| 662 | 661 | } |
|
| 663 | 662 | io::print("\n"); |
|
| 664 | 663 | } |
|
| 665 | 664 | ||
| 666 | 665 | /// Entry point for printing resolver diagnostics in vim quickfix format. |
|
| 667 | - | export unsafe fn printDiagnostics 'arena (diag: &super::Diagnostics, res: &super::Resolver 'arena) { |
|
| 666 | + | export unsafe fn printDiagnostics 'arena 'permission (diag: &super::Diagnostics, res: &super::Resolver 'arena, graph: &module::ModuleGraph 'permission) { |
|
| 668 | 667 | for i in 0..diag.errors.len { |
|
| 669 | - | printError(&diag.errors[i], res); |
|
| 668 | + | printError(&diag.errors[i], res, graph); |
|
| 670 | 669 | } |
|
| 671 | 670 | } |
|
| 672 | 671 | ||
| 673 | 672 | /// Print an applied nominal type's explicit region arguments. |
|
| 674 | 673 | unsafe fn printNominalArguments(applied: *unsafe super::NominalApplication) { |
lib/std/lang/resolver/tests.rad
+148 -53
| 388 | 388 | unsafe static ERROR_STORAGE: [super::Error; 16] = undefined; |
|
| 389 | 389 | ||
| 390 | 390 | /// Package scope used by resolver tests. |
|
| 391 | 391 | unsafe static PKG_SCOPE: super::Scope = undefined; |
|
| 392 | 392 | ||
| 393 | - | /// Module entries used by resolver tests. |
|
| 393 | + | /// One parsed module staged for permission-checked graph construction. |
|
| 394 | + | record TestModule: Copy { |
|
| 395 | + | /// Parent identity, or nil for a package root. |
|
| 396 | + | parentId: ?u16, |
|
| 397 | + | /// Source module name. |
|
| 398 | + | name: *[u8], |
|
| 399 | + | /// Parsed module root. |
|
| 400 | + | root: *ast::Node, |
|
| 401 | + | /// Source text used to parse the root. |
|
| 402 | + | source: *[u8], |
|
| 403 | + | } |
|
| 404 | + | ||
| 405 | + | /// Permission-independent module fixture populated by resolver tests. |
|
| 406 | + | export opaque record TestModuleGraph { |
|
| 407 | + | /// Staged modules in identifier order. |
|
| 408 | + | entries: [?TestModule; 8], |
|
| 409 | + | /// Number of initialized staged modules. |
|
| 410 | + | entriesLen: u32, |
|
| 411 | + | } |
|
| 412 | + | ||
| 413 | + | /// Module entries used when materializing resolver test graphs. |
|
| 394 | 414 | unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; 8] = [nil; 8]; |
|
| 395 | 415 | ||
| 396 | - | /// Module graph used by resolver tests. |
|
| 397 | - | export unsafe static MODULE_GRAPH: module::ModuleGraph = undefined; |
|
| 416 | + | /// Staged module graph used by resolver tests. |
|
| 417 | + | export unsafe static MODULE_GRAPH: TestModuleGraph = TestModuleGraph { |
|
| 418 | + | entries: [nil; 8], |
|
| 419 | + | entriesLen: 0, |
|
| 420 | + | }; |
|
| 398 | 421 | ||
| 399 | 422 | /// Module AST arena storage used by resolver tests. |
|
| 400 | 423 | static MODULE_ARENA_STORAGE: [u8; 16384] = [0; 16384]; |
|
| 401 | 424 | ||
| 402 | 425 | /// Module AST arena used by resolver tests. |
| 434 | 457 | /// Create an arena owner for one resolver test scope. |
|
| 435 | 458 | export unsafe fn testArena() -> alloc::Arena { |
|
| 436 | 459 | return alloc::new(&mut ARENA_STORAGE[..]); |
|
| 437 | 460 | } |
|
| 438 | 461 | ||
| 439 | - | /// Construct a resolver backed by test storage and a synthetic module graph. |
|
| 462 | + | /// Construct a resolver backed by test storage. |
|
| 440 | 463 | export unsafe fn testResolver 'arena (arena: &'arena mut alloc::Arena) -> super::Resolver 'arena { |
|
| 441 | 464 | // TODO: This should be initialized only once. |
|
| 442 | 465 | for i in 0..LITERALS.len { |
|
| 443 | 466 | strings::intern(&mut STRING_POOL, LITERALS[i]); |
|
| 444 | 467 | } |
|
| 445 | - | // TODO: Use local static for this. |
|
| 446 | - | // Reset the module graph for each test. |
|
| 447 | - | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
|
| 448 | - | set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA); |
|
| 468 | + | set MODULE_GRAPH.entriesLen = 0; |
|
| 449 | 469 | let config = super::Config { buildTest: true }; |
|
| 450 | 470 | let res = super::resolver(arena, testStorage(), config); |
|
| 451 | 471 | ||
| 452 | 472 | return res; |
|
| 453 | 473 | } |
| 492 | 512 | }; |
|
| 493 | 513 | return TestResult { diagnostics, root: stmt }; |
|
| 494 | 514 | } |
|
| 495 | 515 | ||
| 496 | 516 | /// Resolve session diagnostics against the standard allocator declaration contract. |
|
| 497 | - | export unsafe fn resolveSessionProgramStr 'arena (self: &mut super::Resolver 'arena, program: *[u8]) -> TestResult |
|
| 498 | - | throws (testing::TestError) |
|
| 499 | - | { |
|
| 517 | + | export unsafe fn resolveSessionProgramStr 'arena ( |
|
| 518 | + | self: &mut super::Resolver 'arena, |
|
| 519 | + | program: *[u8] |
|
| 520 | + | ) -> TestResult throws (testing::TestError) { |
|
| 500 | 521 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 501 | - | let root = try registerModule(&mut MODULE_GRAPH, nil, "std", "export mod lang; mod app;", &mut arena); |
|
| 502 | - | let lang = try registerModule(&mut MODULE_GRAPH, root, "lang", "export mod alloc;", &mut arena); |
|
| 522 | + | let root = try registerModule( |
|
| 523 | + | &mut MODULE_GRAPH, nil, "std", "export mod lang; mod app;", &mut arena |
|
| 524 | + | ); |
|
| 525 | + | let lang = try registerModule( |
|
| 526 | + | &mut MODULE_GRAPH, root, "lang", "export mod alloc;", &mut arena |
|
| 527 | + | ); |
|
| 503 | 528 | let allocator = try registerModule(&mut MODULE_GRAPH, lang, "alloc", |
|
| 504 | 529 | "export record Arena { data: *mut [u8], offset: u32 } export fn reset(arena: &mut Arena) { set arena.offset = 0; } export union AllocError: Copy { OutOfMemory } export trait Alloc { unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); } instance Alloc for Arena { unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { panic; } unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { panic; } }", |
|
| 505 | 530 | &mut arena); |
|
| 506 | 531 | let app = try registerModule(&mut MODULE_GRAPH, root, "app", program, &mut arena); |
|
| 507 | 532 | return try resolveModuleTree(self, root); |
| 524 | 549 | diagnostics: analysis.diagnostics, |
|
| 525 | 550 | root: analysis.root, |
|
| 526 | 551 | }; |
|
| 527 | 552 | } |
|
| 528 | 553 | ||
| 529 | - | /// Resolve a module with the full resolution process. |
|
| 554 | + | /// Materialize staged modules under one externally owned permission. |
|
| 555 | + | unsafe fn materializeModuleGraph 'permission ( |
|
| 556 | + | fixture: &TestModuleGraph, |
|
| 557 | + | permission: &'permission mut module::Permission |
|
| 558 | + | ) -> module::ModuleGraph 'permission throws (testing::TestError) { |
|
| 559 | + | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
|
| 560 | + | let mut graph = module::moduleGraph( |
|
| 561 | + | &mut MODULE_ENTRIES[..], &mut MODULE_ARENA, permission |
|
| 562 | + | ); |
|
| 563 | + | for i in 0..fixture.entriesLen { |
|
| 564 | + | let staged = fixture.entries[i] else throw testing::TestError::Failed; |
|
| 565 | + | let id = if let parentId = staged.parentId { |
|
| 566 | + | try module::registerChild( |
|
| 567 | + | &mut graph, &mut STRING_POOL, parentId, staged.name, "<test>" |
|
| 568 | + | ) catch { |
|
| 569 | + | throw testing::TestError::Failed; |
|
| 570 | + | } |
|
| 571 | + | } else { |
|
| 572 | + | try module::registerRootWithName( |
|
| 573 | + | &mut graph, &mut STRING_POOL, 0, staged.name, "<test>" |
|
| 574 | + | ) catch { |
|
| 575 | + | throw testing::TestError::Failed; |
|
| 576 | + | } |
|
| 577 | + | }; |
|
| 578 | + | if id as u32 != i { |
|
| 579 | + | throw testing::TestError::Failed; |
|
| 580 | + | } |
|
| 581 | + | try module::setAst(&mut graph, id, staged.root) catch { |
|
| 582 | + | throw testing::TestError::Failed; |
|
| 583 | + | }; |
|
| 584 | + | try module::setSource(&mut graph, id, staged.source) catch { |
|
| 585 | + | throw testing::TestError::Failed; |
|
| 586 | + | }; |
|
| 587 | + | } |
|
| 588 | + | return graph; |
|
| 589 | + | } |
|
| 590 | + | ||
| 591 | + | /// Print module diagnostics using a freshly materialized permission-checked graph. |
|
| 592 | + | export unsafe fn printDiagnostics 'arena ( |
|
| 593 | + | diagnostics: &super::Diagnostics, |
|
| 594 | + | res: &super::Resolver 'arena |
|
| 595 | + | ) { |
|
| 596 | + | let mut owner = module::Permission {}; |
|
| 597 | + | let permission: 'permission = &mut owner in { |
|
| 598 | + | let graph = try! materializeModuleGraph(&MODULE_GRAPH, permission); |
|
| 599 | + | super::printer::printDiagnostics(diagnostics, res, &graph); |
|
| 600 | + | } |
|
| 601 | + | } |
|
| 602 | + | ||
| 603 | + | /// Resolve a staged module tree with the full resolution process. |
|
| 530 | 604 | export unsafe fn resolveModuleTree 'arena ( |
|
| 531 | 605 | res: &mut super::Resolver 'arena, |
|
| 532 | 606 | rootId: u16 |
|
| 533 | 607 | ) -> TestResult throws (testing::TestError) { |
|
| 534 | - | let root = module::get(&MODULE_GRAPH, rootId) |
|
| 535 | - | else throw testing::TestError::Failed; |
|
| 536 | - | let rootAst = module::astFor(root) |
|
| 537 | - | else throw testing::TestError::Failed; |
|
| 538 | - | let packages = [super::Pkg { |
|
| 539 | - | rootEntry: root, |
|
| 540 | - | rootAst, |
|
| 541 | - | }]; |
|
| 542 | - | let diagnostics = try super::resolve(res, &MODULE_GRAPH, &packages[..]) catch { |
|
| 543 | - | throw testing::TestError::Failed; |
|
| 544 | - | }; |
|
| 545 | - | return TestResult { diagnostics, root: rootAst }; |
|
| 608 | + | let mut owner = module::Permission {}; |
|
| 609 | + | let permission: 'permission = &mut owner in { |
|
| 610 | + | let graph = try materializeModuleGraph(&MODULE_GRAPH, permission); |
|
| 611 | + | let root = module::get(&graph, rootId) |
|
| 612 | + | else throw testing::TestError::Failed; |
|
| 613 | + | let rootAst = module::astFor(&graph, root) |
|
| 614 | + | else throw testing::TestError::Failed; |
|
| 615 | + | let packages = [super::Pkg { |
|
| 616 | + | rootEntry: root, |
|
| 617 | + | rootAst, |
|
| 618 | + | }]; |
|
| 619 | + | let diagnostics = try super::resolve( |
|
| 620 | + | res, &graph, &packages[..] |
|
| 621 | + | ) catch { |
|
| 622 | + | throw testing::TestError::Failed; |
|
| 623 | + | }; |
|
| 624 | + | return TestResult { diagnostics, root: rootAst }; |
|
| 625 | + | } |
|
| 546 | 626 | } |
|
| 547 | 627 | ||
| 548 | - | /// Register a module in the graph and attach a parsed AST to it. |
|
| 549 | - | /// If parentId is nil, registers as a root module. |
|
| 628 | + | /// Register a parsed module in the permission-independent test fixture. |
|
| 629 | + | /// If `parentId` is nil, registers a root module. |
|
| 550 | 630 | export unsafe fn registerModule( |
|
| 551 | - | graph: &mut module::ModuleGraph, |
|
| 631 | + | graph: &mut TestModuleGraph, |
|
| 552 | 632 | parentId: ?u16, |
|
| 553 | 633 | name: *[u8], |
|
| 554 | 634 | code: *[u8], |
|
| 555 | 635 | arena: &mut ast::NodeArena |
|
| 556 | 636 | ) -> u16 throws (testing::TestError) { |
|
| 557 | - | let filePath = "<test>"; |
|
| 558 | - | let mut modId: u16 = undefined; |
|
| 559 | - | if let parent = parentId { |
|
| 560 | - | set modId = try module::registerChild(graph, &mut STRING_POOL, parent, name, filePath) catch { |
|
| 561 | - | throw testing::TestError::Failed; |
|
| 562 | - | }; |
|
| 563 | - | } else { |
|
| 564 | - | set modId = try module::registerRootWithName(graph, &mut STRING_POOL, 0, name, filePath) catch { |
|
| 565 | - | throw testing::TestError::Failed; |
|
| 566 | - | }; |
|
| 637 | + | if graph.entriesLen >= graph.entries.len { |
|
| 638 | + | throw testing::TestError::Failed; |
|
| 567 | 639 | } |
|
| 568 | - | let root = try parser::parse(scanner::SourceLoc::String, code, arena, &mut STRING_POOL) catch { |
|
| 640 | + | let root = try parser::parse( |
|
| 641 | + | scanner::SourceLoc::String, code, arena, &mut STRING_POOL |
|
| 642 | + | ) catch { |
|
| 569 | 643 | panic "registerModule: parsing failed"; |
|
| 570 | 644 | }; |
|
| 571 | - | try module::setAst(graph, modId, root) catch { |
|
| 572 | - | panic "registerModule: module not found"; |
|
| 645 | + | let id = graph.entriesLen as u16; |
|
| 646 | + | set graph.entries[graph.entriesLen] = TestModule { |
|
| 647 | + | parentId, |
|
| 648 | + | name, |
|
| 649 | + | root, |
|
| 650 | + | source: code, |
|
| 573 | 651 | }; |
|
| 574 | - | return modId; |
|
| 652 | + | set graph.entriesLen += 1; |
|
| 653 | + | return id; |
|
| 575 | 654 | } |
|
| 576 | 655 | ||
| 577 | 656 | /// Ensure an expression statement produces the expected type and return the expression node. |
|
| 578 | 657 | fn expectExprStmtType 'arena (self: &super::Resolver 'arena, node: *ast::Node, expected: super::Type) -> *ast::Node |
|
| 579 | 658 | throws (testing::TestError) |
| 8593 | 8672 | let storage: 'test = &mut arena in { |
|
| 8594 | 8673 | let mut res = testResolver(storage); |
|
| 8595 | 8674 | if let _ = super::moduleFor(&res, 0) { |
|
| 8596 | 8675 | throw testing::TestError::Failed; |
|
| 8597 | 8676 | } |
|
| 8598 | - | let root = try! module::registerRootWithName(&mut MODULE_GRAPH, &mut STRING_POOL, 0, "root", "/root.rad"); |
|
| 8599 | - | let child = try! module::registerChild(&mut MODULE_GRAPH, &mut STRING_POOL, root, "child", "/child.rad"); |
|
| 8600 | - | let diagnostics = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
|
| 8601 | - | assert super::success(&diagnostics); |
|
| 8602 | - | set MODULE_GRAPH.entriesLen = 0; |
|
| 8603 | - | set MODULE_ENTRIES[root as u32] = nil; |
|
| 8604 | - | set MODULE_ENTRIES[child as u32] = nil; |
|
| 8605 | - | try checkModuleQueries(&res, root, child); |
|
| 8606 | - | let empty = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
|
| 8607 | - | assert super::success(&empty); |
|
| 8677 | + | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
|
| 8678 | + | let mut root: u16 = 0; |
|
| 8679 | + | let mut child: u16 = 0; |
|
| 8680 | + | let mut owner = module::Permission {}; |
|
| 8681 | + | let permission: 'permission = &mut owner in { |
|
| 8682 | + | let mut graph = module::moduleGraph( |
|
| 8683 | + | &mut MODULE_ENTRIES[..], &mut MODULE_ARENA, permission |
|
| 8684 | + | ); |
|
| 8685 | + | set root = try! module::registerRootWithName( |
|
| 8686 | + | &mut graph, &mut STRING_POOL, 0, "root", "/root.rad" |
|
| 8687 | + | ); |
|
| 8688 | + | set child = try! module::registerChild( |
|
| 8689 | + | &mut graph, &mut STRING_POOL, root, "child", "/child.rad" |
|
| 8690 | + | ); |
|
| 8691 | + | let diagnostics = try! super::resolve(&mut res, &graph, &[]); |
|
| 8692 | + | assert super::success(&diagnostics); |
|
| 8693 | + | } |
|
| 8694 | + | let mut emptyOwner = module::Permission {}; |
|
| 8695 | + | let emptyPermission: 'empty = &mut emptyOwner in { |
|
| 8696 | + | let graph = module::moduleGraph( |
|
| 8697 | + | &mut MODULE_ENTRIES[..], &mut MODULE_ARENA, emptyPermission |
|
| 8698 | + | ); |
|
| 8699 | + | try checkModuleQueries(&res, root, child); |
|
| 8700 | + | let empty = try! super::resolve(&mut res, &graph, &[]); |
|
| 8701 | + | assert super::success(&empty); |
|
| 8702 | + | } |
|
| 8608 | 8703 | if let _ = super::moduleFor(&res, root) { |
|
| 8609 | 8704 | throw testing::TestError::Failed; |
|
| 8610 | 8705 | } |
|
| 8611 | 8706 | } |
|
| 8612 | 8707 | } |